]> granicus.if.org Git - postgis/commitdiff
Add an internal geometry tree for use in a native prepared geometry scheme for fast...
authorPaul Ramsey <pramsey@cleverelephant.ca>
Mon, 30 Nov 2009 20:52:16 +0000 (20:52 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Mon, 30 Nov 2009 20:52:16 +0000 (20:52 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@4948 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/Makefile.in
liblwgeom/lwalgorithm.c
liblwgeom/lwalgorithm.h
liblwgeom/lwgeodetic.h
liblwgeom/lwtree.c [new file with mode: 0644]
liblwgeom/lwtree.h [new file with mode: 0644]
liblwgeom/measures.c

index addeed08840e2572792beda19bc8aae7344652a1..dc6bb771e6a1ab0ea67f6722fa976e2ba86d71cd 100644 (file)
@@ -50,7 +50,8 @@ SA_OBJS = \
        g_serialized.o \
        g_util.o \
        lwgeodetic.o \
-       lwspheroid.o
+       lwspheroid.o \
+       lwtree.o
 
 SA_HEADERS = \
        liblwgeom.h \
index bd0de73553ed7668025c18d100edec65ff03416e..90179abdde9c5eb3af05cdb5db9365f9581948a4 100644 (file)
 ** Return > 0.0 if point Q is right of segment P
 ** Return = 0.0 if point Q in on segment P
 */
-double lw_segment_side(POINT2D p1, POINT2D p2, POINT2D q)
+double lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
 {
-       return ( (q.x - p1.x) * (p2.y - p1.y) - (p2.x - p1.x) * (q.y - p1.y) );
+       double side = ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) );
+       if( FP_IS_ZERO(side) )
+               return 0.0;
+       else
+               return side;
 }
 
 
-int lw_segment_envelope_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2)
+int lw_segment_envelope_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
 {
-       double minq=LW_MIN(q1.x,q2.x);
-       double maxq=LW_MAX(q1.x,q2.x);
-       double minp=LW_MIN(p1.x,p2.x);
-       double maxp=LW_MAX(p1.x,p2.x);
+       double minq=LW_MIN(q1->x,q2->x);
+       double maxq=LW_MAX(q1->x,q2->x);
+       double minp=LW_MIN(p1->x,p2->x);
+       double maxp=LW_MAX(p1->x,p2->x);
 
        if (FP_GT(minp,maxq) || FP_LT(maxp,minq))
                return LW_FALSE;
 
-       minq=LW_MIN(q1.y,q2.y);
-       maxq=LW_MAX(q1.y,q2.y);
-       minp=LW_MIN(p1.y,p2.y);
-       maxp=LW_MAX(p1.y,p2.y);
+       minq=LW_MIN(q1->y,q2->y);
+       maxq=LW_MAX(q1->y,q2->y);
+       minp=LW_MIN(p1->y,p2->y);
+       maxp=LW_MAX(p1->y,p2->y);
 
        if (FP_GT(minp,maxq) || FP_LT(maxp,minq))
                return LW_FALSE;
@@ -62,7 +66,7 @@ int lw_segment_envelope_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q
 **             SEG_CROSS_LEFT = 2,
 **             SEG_CROSS_RIGHT = 3,
 */
-int lw_segment_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2)
+int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
 {
 
        double pq1, pq2, qp1, qp2;
@@ -84,13 +88,13 @@ int lw_segment_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2)
        /* Are the start and end points of p on the same side of q? */
        qp1=lw_segment_side(q1,q2,p1);
        qp2=lw_segment_side(q1,q2,p2);
-       if ((qp1>0 && qp2>0) || (qp1<0 && qp2<0))
+       if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) )
        {
                return SEG_NO_INTERSECTION;
        }
 
        /* Nobody is on one side or another? Must be colinear. */
-       if (FP_IS_ZERO(pq1) && FP_IS_ZERO(pq2) && FP_IS_ZERO(qp1) && FP_IS_ZERO(qp2))
+       if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 )
        {
                return SEG_COLINEAR;
        }
@@ -104,13 +108,13 @@ int lw_segment_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2)
        LWDEBUGF(4, "qp1=%.15g qp2=%.15g", qp1, qp2);
 
        /* Second point of p or q touches, it's not a crossing. */
-       if ( FP_IS_ZERO(pq2) || FP_IS_ZERO(qp2) )
+       if ( pq2 == 0.0 || qp2 == 0.0 )
        {
                return SEG_NO_INTERSECTION;
        }
 
        /* First point of p touches, it's a "crossing". */
-       if ( FP_IS_ZERO(pq1) )
+       if ( pq1 == 0.0 )
        {
                if ( FP_GT(pq2,0.0) )
                        return SEG_CROSS_RIGHT;
@@ -119,7 +123,7 @@ int lw_segment_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2)
        }
 
        /* First point of q touches, it's a crossing. */
-       if ( FP_IS_ZERO(qp1) )
+       if ( qp1 == 0.0 )
        {
                if ( FP_LT(pq1,pq2) )
                        return SEG_CROSS_RIGHT;
@@ -135,7 +139,6 @@ int lw_segment_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2)
 
        /* This should never happen! */
        return SEG_ERROR;
-
 }
 
 /**
@@ -190,7 +193,7 @@ int lwline_crossing_direction(LWLINE *l1, LWLINE *l2)
                        /* Update second point of p to next value */
                        rv = getPoint2d_p(pa1, j, &p2);
                        
-                       this_cross = lw_segment_intersects(p1, p2, q1, q2);
+                       this_cross = lw_segment_intersects(&p1, &p2, &q1, &q2);
 
                        LWDEBUGF(4, "i=%d, j=%d (%.8g %.8g, %.8g %.8g)", this_cross, i, j, p1.x, p1.y, p2.x, p2.y);
 
index 3af4307dcc16bdcda31c074d398aeecafc68563a..581631054f68132b4e72c39cb79d447a4e893686 100644 (file)
@@ -23,9 +23,9 @@ enum CG_SEGMENT_INTERSECTION_TYPE {
     SEG_TOUCH_RIGHT = 5
 };
 
-double lw_segment_side(POINT2D p1, POINT2D p2, POINT2D q);
-int lw_segment_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2);
-int lw_segment_envelope_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2);
+double lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q);
+int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2);
+int lw_segment_envelope_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2);
 
 
 enum CG_LINE_CROSS_TYPE {
index 102fdc3b8bfb09059a2fd38782a57e2baf594bc6..3ce5f4277c95d61d0c874ad0981464591adaf773 100644 (file)
@@ -34,6 +34,15 @@ typedef struct
        GEOGRAPHIC_POINT end;
 } GEOGRAPHIC_EDGE;
 
+/**
+* Holder for sorting points in distance algorithm
+*/
+typedef struct
+{
+       double measure;
+       uint32 index;
+} DISTANCE_ORDER;
+
 /**
 * Conversion functions
 */
diff --git a/liblwgeom/lwtree.c b/liblwgeom/lwtree.c
new file mode 100644 (file)
index 0000000..266d777
--- /dev/null
@@ -0,0 +1,218 @@
+#include "lwalgorithm.h"
+#include "lwtree.h"
+
+
+/**
+* Internal nodes have their point references set to NULL.
+*/
+static int rect_node_is_leaf(const RECT_NODE *node)
+{
+       return (node->p1 != NULL);
+}
+
+/**
+* Recurse from top of node tree and free all children. 
+* does not free underlying point array.
+*/
+void rect_tree_free(RECT_NODE *node)
+{
+       if( node->left_node )
+       {
+               rect_tree_free(node->left_node);
+               node->left_node = 0;
+       }
+       if( node->right_node )
+       {
+               rect_tree_free(node->right_node);
+               node->right_node = 0;
+       }
+       lwfree(node);
+}
+
+/* 0 => no containment */
+int rect_tree_contains_point(const RECT_NODE *node, const POINT2D *pt, int *on_boundary)
+{
+       if( FP_CONTAINS_INCL(node->ymin, pt->y, node->ymax) )
+       {
+               if( rect_node_is_leaf(node) )
+               {
+                       double side = lw_segment_side(node->p1, node->p2, pt);
+                       if ( side == 0.0 )
+                               *on_boundary = LW_TRUE;
+                       return (side < 0.0 ? -1 : 1 );
+               }
+               else 
+               {
+                       return rect_tree_contains_point(node->left_node, pt, on_boundary) +
+                              rect_tree_contains_point(node->right_node, pt, on_boundary);
+               }
+       }
+       //printf("NOT in measure range\n");
+       return 0;
+}
+
+int rect_tree_intersects_tree(const RECT_NODE *n1, const RECT_NODE *n2)
+{
+       /* There can only be an edge intersection if the rectangles overlap */
+       if( ! ( FP_GTEQ(n1->xmin, n2->xmax) || FP_GTEQ(n2->xmin, n1->xmax) || FP_GTEQ(n1->ymin, n2->ymax) || FP_GTEQ(n2->ymin, n1->ymax) ) )
+       {
+               /* We can only test for a true intersection if the nodes are both leaf nodes */
+               if( rect_node_is_leaf(n1) && rect_node_is_leaf(n2) )
+               {
+                       /* Check for true intersection */
+                       if( lw_segment_intersects(n1->p1, n1->p2, n2->p1, n2->p2) )
+                               return LW_TRUE;
+                       else
+                               return LW_FALSE;
+               }
+               else 
+               {
+                       /* Recurse to children */
+                       if( rect_node_is_leaf(n1) ) 
+                       {
+                               if( rect_tree_intersects_tree(n2->left_node, n1) || rect_tree_intersects_tree(n2->right_node, n1) )
+                                       return LW_TRUE;
+                               else
+                                       return LW_FALSE;
+                       }
+                       else 
+                       {
+                               if( rect_tree_intersects_tree(n1->left_node, n2) || rect_tree_intersects_tree(n1->right_node, n2) )
+                                       return LW_TRUE;
+                               else
+                                       return LW_FALSE;
+                       }
+               }
+       }
+       else
+       {
+               return LW_FALSE;
+       }
+}
+
+
+/**
+* Create a new leaf node, calculating a measure value for each point on the
+* edge and storing pointers back to the end points for later.
+*/
+RECT_NODE* rect_node_leaf_new(const POINTARRAY *pa, int i)
+{
+       POINT2D *p1, *p2;
+       RECT_NODE *node;
+
+       p1 = (POINT2D*)getPoint_internal(pa, i);
+       p2 = (POINT2D*)getPoint_internal(pa, i+1);
+
+       /* Zero length edge, doesn't get a node */
+       if( FP_EQUALS(p1->x, p2->x) && FP_EQUALS(p1->y, p2->y) )
+               return NULL;
+               
+       node = lwalloc(sizeof(RECT_NODE));
+       node->p1 = p1;
+       node->p2 = p2;
+       node->xmin = FP_MIN(p1->x,p2->x);
+       node->xmax = FP_MAX(p1->x,p2->x);
+       node->ymin = FP_MIN(p1->y,p2->y);
+       node->ymax = FP_MAX(p1->y,p2->y);
+       node->left_node = NULL;
+       node->right_node = NULL;
+       return node;
+}
+
+/**
+* Create a new internal node, calculating the new measure range for the node,
+* and storing pointers to the child nodes.
+*/
+RECT_NODE* rect_node_internal_new(RECT_NODE *left_node, RECT_NODE *right_node)
+{
+       RECT_NODE *node = lwalloc(sizeof(RECT_NODE));
+       node->p1 = NULL;
+       node->p2 = NULL;
+       node->xmin = FP_MIN(left_node->xmin, right_node->xmin);
+       node->xmax = FP_MAX(left_node->xmax, right_node->xmax); 
+       node->ymin = FP_MIN(left_node->ymin, right_node->ymin);
+       node->ymax = FP_MAX(left_node->ymax, right_node->ymax); 
+       node->left_node = left_node;
+       node->right_node = right_node;
+       return node;
+}
+
+/**
+* Build a tree of nodes from a point array, one node per edge, and each
+* with an associated measure range along a one-dimensional space. We
+* can then search that space as a range tree.
+*/
+RECT_NODE* rect_tree_new(const POINTARRAY *pa)
+{
+       int num_edges, num_children, num_parents;
+       int i, j;
+       RECT_NODE **nodes;
+       RECT_NODE *node;
+       RECT_NODE *tree;
+       
+       if( pa->npoints < 2 )
+       {
+               return NULL;
+       }
+               
+       /* 
+       ** First create a flat list of nodes, one per edge.
+       ** For each vertex, transform into our one-dimensional measure.
+       ** Hopefully, when projected, the points turn into a fairly
+       ** uniformly distributed collection of measures.
+       */
+       num_edges = pa->npoints - 1;
+       nodes = lwalloc(sizeof(RECT_NODE*) * pa->npoints);
+       j = 0;
+       for( i = 0; i < num_edges; i++ )
+       {
+               node = rect_node_leaf_new(pa, i);
+               if( node ) /* Not zero length? */
+               {
+                       nodes[j] = node;
+                       j++;
+               }
+       }
+       
+       /*
+       ** If we sort the nodelist first, we'll get a more balanced tree
+       ** in the end, but at the cost of sorting. For now, we just
+       ** build the tree knowing that point arrays tend to have a 
+       ** reasonable amount of sorting already.
+       */
+       
+       num_children = j;
+       num_parents = num_children / 2;
+       while( num_parents > 0 )
+       {
+               j = 0;
+               while( j < num_parents )
+               {
+                       /* 
+                       ** Each new parent includes pointers to the children, so even though
+                       ** we are over-writing their place in the list, we still have references
+                       ** to them via the tree.
+                       */
+                       nodes[j] = rect_node_internal_new(nodes[2*j], nodes[(2*j)+1]);
+                       j++;
+               }
+               /* Odd number of children, just copy the last node up a level */
+               if( num_children % 2 )
+               {
+                       nodes[j] = nodes[num_children - 1];
+                       num_parents++;
+               }
+               num_children = num_parents;
+               num_parents = num_children / 2;
+       } 
+
+       /* Take a reference to the head of the tree*/
+       tree = nodes[0];
+       
+       /* Free the old list structure, leaving the tree in place */
+       lwfree(nodes);
+       
+       return tree;
+       
+}
+
diff --git a/liblwgeom/lwtree.h b/liblwgeom/lwtree.h
new file mode 100644 (file)
index 0000000..b9f811b
--- /dev/null
@@ -0,0 +1,24 @@
+#include "libgeom.h"
+
+
+/**
+* Note that p1 and p2 are pointers into an independent POINTARRAY, do not free them.
+*/
+typedef struct rect_node
+{
+       double xmin;
+       double xmax;
+       double ymin;
+       double ymax;
+       struct rect_node *left_node;
+       struct rect_node *right_node;
+       POINT2D *p1;
+       POINT2D *p2;
+} RECT_NODE;   
+
+int rect_tree_contains_point(const RECT_NODE *tree, const POINT2D *pt, int *on_boundary);
+int rect_tree_intersects_tree(const RECT_NODE *tree1, const RECT_NODE *tree2);
+void rect_tree_free(RECT_NODE *tree);
+RECT_NODE* rect_node_leaf_new(const POINTARRAY *pa, int i);
+RECT_NODE* rect_node_internal_new(RECT_NODE *left_node, RECT_NODE *right_node);
+RECT_NODE* rect_tree_new(const POINTARRAY *pa);
index 3d5778131f1cbf5f06823e56a5a45c10b72940c3..520d62824aaf708ebfede18bbca16e25b8ed37fa 100644 (file)
@@ -1023,7 +1023,7 @@ lw_dist2d_fast_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2,DISTPTS *dl, BOX2D
                        thevalue = theP.x-(k*theP.y);
                        list1[t].themeasure=thevalue;
                        list1[t].pnr=t;
-                       /*lwnotice("1pnr=%d, pnr=%d, themeasure=%f, themeasure=%f",t,list1[t].pnr,thevalue,list1[t].themeasure  );*/
+                       //lwnotice("l1 %d, measure=%f",t,thevalue );
                }
                for (t=0; t<n2; t++) /*for each segment in L2*/
                {
@@ -1032,6 +1032,7 @@ lw_dist2d_fast_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2,DISTPTS *dl, BOX2D
                        thevalue = theP.x-(k*theP.y);
                        list2[t].themeasure=thevalue;
                        list2[t].pnr=t;
+                       //lwnotice("l2 %d, measure=%f",t,thevalue );
                }
                c1m = c1.x-(k*c1.y);
                c2m = c2.x-(k*c2.y);