/** * Where the circ_center_spherical() function fails, we need a fall-back. The failures * happen in short arcs, where the spherical distance between two points is practically * the same as the straight-line distance, so our fallback will be to use the straight-line * between the two to calculate the new projected center. For proportions far from 0.5 * this will be increasingly more incorrect. */ static int circ_center_cartesian(const GEOGRAPHIC_POINT* c1, const GEOGRAPHIC_POINT* c2, double distance, double offset, GEOGRAPHIC_POINT* center) { POINT3D p1, p2; POINT3D p1p2, pc; double proportion = offset/distance; LWDEBUG(4,"calculating cartesian center"); geog2cart(c1, &p1); geog2cart(c2, &p2); /* Difference between p2 and p1 */ p1p2.x = p2.x - p1.x; p1p2.y = p2.y - p1.y; p1p2.z = p2.z - p1.z; /* Scale difference to proportion */ p1p2.x *= proportion; p1p2.y *= proportion; p1p2.z *= proportion; /* Add difference to p1 to get approximate center point */ pc.x = p1.x + p1p2.x; pc.y = p1.y + p1p2.y; pc.z = p1.z + p1p2.z; normalize(&pc); /* Convert center point to geographics */ cart2geog(&pc, center); return LW_SUCCESS; }
/** * Create a new leaf node, storing pointers back to the end points for later. */ static CIRC_NODE* circ_node_leaf_new(const POINTARRAY* pa, int i) { POINT2D *p1, *p2; POINT3D q1, q2, c; GEOGRAPHIC_POINT g1, g2, gc; CIRC_NODE *node; double diameter; p1 = (POINT2D*)getPoint_internal(pa, i); p2 = (POINT2D*)getPoint_internal(pa, i+1); geographic_point_init(p1->x, p1->y, &g1); geographic_point_init(p2->x, p2->y, &g2); LWDEBUGF(3,"edge #%d (%g %g, %g %g)", i, p1->x, p1->y, p2->x, p2->y); diameter = sphere_distance(&g1, &g2); /* Zero length edge, doesn't get a node */ if ( FP_EQUALS(diameter, 0.0) ) return NULL; /* Allocate */ node = lwalloc(sizeof(CIRC_NODE)); node->p1 = p1; node->p2 = p2; /* Convert ends to X/Y/Z, sum, and normalize to get mid-point */ geog2cart(&g1, &q1); geog2cart(&g2, &q2); vector_sum(&q1, &q2, &c); normalize(&c); cart2geog(&c, &gc); node->center = gc; node->radius = diameter / 2.0; LWDEBUGF(3,"edge #%d CENTER(%g %g) RADIUS=%g", i, gc.lon, gc.lat, node->radius); /* Leaf has no children */ node->num_nodes = 0; node->nodes = NULL; node->edge_num = i; return node; }
static int CircTreePIP(const CIRC_NODE* tree1, const GSERIALIZED* g1, const POINT4D* in_point) { int tree1_type = gserialized_get_type(g1); GBOX gbox1; GEOGRAPHIC_POINT in_gpoint; POINT3D in_point3d; POSTGIS_DEBUGF(3, "tree1_type=%d", tree1_type); /* If the tree'ed argument is a polygon, do the P-i-P using the tree-based P-i-P */ if ( tree1_type == POLYGONTYPE || tree1_type == MULTIPOLYGONTYPE ) { POSTGIS_DEBUG(3, "tree is a polygon, using tree PiP"); /* Need a gbox to calculate an outside point */ if ( LW_FAILURE == gserialized_get_gbox_p(g1, &gbox1) ) { LWGEOM* lwgeom1 = lwgeom_from_gserialized(g1); POSTGIS_DEBUG(3, "unable to read gbox from gserialized, calculating from scratch"); lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1); lwgeom_free(lwgeom1); } /* Flip the candidate point into geographics */ geographic_point_init(in_point->x, in_point->y, &in_gpoint); geog2cart(&in_gpoint, &in_point3d); /* If the candidate isn't in the tree box, it's not in the tree area */ if ( ! gbox_contains_point3d(&gbox1, &in_point3d) ) { POSTGIS_DEBUG(3, "in_point3d is not inside the tree gbox, CircTreePIP returning FALSE"); return LW_FALSE; } /* The candidate point is in the box, so it *might* be inside the tree */ else { POINT2D pt2d_outside; /* latlon */ POINT2D pt2d_inside; pt2d_inside.x = in_point->x; pt2d_inside.y = in_point->y; /* Calculate a definitive outside point */ gbox_pt_outside(&gbox1, &pt2d_outside); POSTGIS_DEBUGF(3, "p2d_inside=POINT(%g %g) p2d_outside=POINT(%g %g)", pt2d_inside.x, pt2d_inside.y, pt2d_outside.x, pt2d_outside.y); /* Test the candidate point for strict containment */ POSTGIS_DEBUG(3, "calling circ_tree_contains_point for PiP test"); return circ_tree_contains_point(tree1, &pt2d_inside, &pt2d_outside, NULL); } } else { POSTGIS_DEBUG(3, "tree1 not polygonal, so CircTreePIP returning FALSE"); return LW_FALSE; } }
static double circ_tree_distance_tree_internal(const CIRC_NODE* n1, const CIRC_NODE* n2, double threshold, double* min_dist, double* max_dist, GEOGRAPHIC_POINT* closest1, GEOGRAPHIC_POINT* closest2) { double max; double d, d_min; int i; LWDEBUGF(4, "entered, min_dist %.8g max_dist %.8g", *min_dist, *max_dist); // circ_tree_print(n1, 0); // circ_tree_print(n2, 0); /* Short circuit if we've already hit the minimum */ if( FP_LT(*min_dist, threshold) ) return *min_dist; /* If your minimum is greater than anyone's maximum, you can't hold the winner */ if( circ_node_min_distance(n1, n2) > *max_dist ) { LWDEBUGF(4, "pruning pair %p, %p", n1, n2); return MAXFLOAT; } /* If your maximum is a new low, we'll use that as our new global tolerance */ max = circ_node_max_distance(n1, n2); LWDEBUGF(5, "max %.8g", max); if( max < *max_dist ) *max_dist = max; /* Both leaf nodes, do a real distance calculation */ if( circ_node_is_leaf(n1) ) { if( circ_node_is_leaf(n2) ) { double d; GEOGRAPHIC_POINT close1, close2; LWDEBUGF(4, "testing leaf pair [%d], [%d]", n1->edge_num, n2->edge_num); /* One of the nodes is a point */ if ( n1->p1 == n1->p2 || n2->p1 == n2->p2 ) { GEOGRAPHIC_EDGE e; GEOGRAPHIC_POINT gp1, gp2; /* Both nodes are points! */ if ( n1->p1 == n1->p2 && n2->p1 == n2->p2 ) { geographic_point_init(n1->p1->x, n1->p1->y, &gp1); geographic_point_init(n2->p1->x, n2->p1->y, &gp2); close1 = gp1; close2 = gp2; d = sphere_distance(&gp1, &gp2); } /* Node 1 is a point */ else if ( n1->p1 == n1->p2 ) { geographic_point_init(n1->p1->x, n1->p1->y, &gp1); geographic_point_init(n2->p1->x, n2->p1->y, &(e.start)); geographic_point_init(n2->p2->x, n2->p2->y, &(e.end)); close1 = gp1; d = edge_distance_to_point(&e, &gp1, &close2); } /* Node 2 is a point */ else { geographic_point_init(n2->p1->x, n2->p1->y, &gp1); geographic_point_init(n1->p1->x, n1->p1->y, &(e.start)); geographic_point_init(n1->p2->x, n1->p2->y, &(e.end)); close1 = gp1; d = edge_distance_to_point(&e, &gp1, &close2); } LWDEBUGF(4, " got distance %g", d); } /* Both nodes are edges */ else { GEOGRAPHIC_EDGE e1, e2; GEOGRAPHIC_POINT g; POINT3D A1, A2, B1, B2; geographic_point_init(n1->p1->x, n1->p1->y, &(e1.start)); geographic_point_init(n1->p2->x, n1->p2->y, &(e1.end)); geographic_point_init(n2->p1->x, n2->p1->y, &(e2.start)); geographic_point_init(n2->p2->x, n2->p2->y, &(e2.end)); geog2cart(&(e1.start), &A1); geog2cart(&(e1.end), &A2); geog2cart(&(e2.start), &B1); geog2cart(&(e2.end), &B2); if ( edge_intersects(&A1, &A2, &B1, &B2) ) { d = 0.0; edge_intersection(&e1, &e2, &g); close1 = close2 = g; } else { d = edge_distance_to_edge(&e1, &e2, &close1, &close2); } LWDEBUGF(4, "edge_distance_to_edge returned %g", d); } if ( d < *min_dist ) { *min_dist = d; *closest1 = close1; *closest2 = close2; } return d; } else { d_min = MAXFLOAT; for ( i = 0; i < n2->num_nodes; i++ ) { d = circ_tree_distance_tree_internal(n1, n2->nodes[i], threshold, min_dist, max_dist, closest1, closest2); d_min = FP_MIN(d_min, d); } return d_min; } } else { d_min = MAXFLOAT; for ( i = 0; i < n1->num_nodes; i++ ) { d = circ_tree_distance_tree_internal(n2, n1->nodes[i], threshold, min_dist, max_dist, closest1, closest2); d_min = FP_MIN(d_min, d); } return d_min; } }
/** * Walk the tree and count intersections between the stab line and the edges. * odd => containment, even => no containment. * KNOWN PROBLEM: Grazings (think of a sharp point, just touching the * stabline) will be counted for one, which will throw off the count. */ int circ_tree_contains_point(const CIRC_NODE* node, const POINT2D* pt, const POINT2D* pt_outside, int* on_boundary) { GEOGRAPHIC_POINT closest; GEOGRAPHIC_EDGE stab_edge, edge; POINT3D S1, S2, E1, E2; double d; int i, c; /* Construct a stabline edge from our "inside" to our known outside point */ geographic_point_init(pt->x, pt->y, &(stab_edge.start)); geographic_point_init(pt_outside->x, pt_outside->y, &(stab_edge.end)); geog2cart(&(stab_edge.start), &S1); geog2cart(&(stab_edge.end), &S2); LWDEBUG(3, "entered"); /* * If the stabline doesn't cross within the radius of a node, there's no * way it can cross. */ LWDEBUGF(3, "working on node %p, edge_num %d, radius %g, center POINT(%g %g)", node, node->edge_num, node->radius, rad2deg(node->center.lon), rad2deg(node->center.lat)); d = edge_distance_to_point(&stab_edge, &(node->center), &closest); LWDEBUGF(3, "edge_distance_to_point=%g, node_radius=%g", d, node->radius); if ( FP_LTEQ(d, node->radius) ) { LWDEBUGF(3,"entering this branch (%p)", node); /* Return the crossing number of this leaf */ if ( circ_node_is_leaf(node) ) { int inter; LWDEBUGF(3, "leaf node calculation (edge %d)", node->edge_num); geographic_point_init(node->p1->x, node->p1->y, &(edge.start)); geographic_point_init(node->p2->x, node->p2->y, &(edge.end)); geog2cart(&(edge.start), &E1); geog2cart(&(edge.end), &E2); inter = edge_intersects(&S1, &S2, &E1, &E2); if ( inter & PIR_INTERSECTS ) { LWDEBUG(3," got stab line edge_intersection with this edge!"); /* To avoid double counting crossings-at-a-vertex, */ /* always ignore crossings at "lower" ends of edges*/ if ( inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR ) { LWDEBUG(3," rejecting stab line grazing by left-side edge"); return 0; } else { LWDEBUG(3," accepting stab line intersection"); return 1; } } } /* Or, add up the crossing numbers of all children of this node. */ else { c = 0; for ( i = 0; i < node->num_nodes; i++ ) { LWDEBUG(3,"internal node calculation"); LWDEBUGF(3," calling circ_tree_contains_point on child %d!", i); c += circ_tree_contains_point(node->nodes[i], pt, pt_outside, on_boundary); } return c % 2; } } else { LWDEBUGF(3,"skipping this branch (%p)", node); } return 0; }