PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwgeodetic_tree.c
Go to the documentation of this file.
1/**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * PostGIS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * PostGIS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright (C) 2012-2015 Paul Ramsey <pramsey@cleverelephant.ca>
22 * Copyright (C) 2012-2015 Sandro Santilli <strk@kbt.io>
23 *
24 **********************************************************************/
25
26#include "liblwgeom_internal.h"
27#include "lwgeodetic_tree.h"
28#include "lwgeom_log.h"
29
30
31/* Internal prototype */
32static CIRC_NODE* circ_nodes_merge(CIRC_NODE** nodes, int num_nodes);
33
34
38static inline int
40{
41 return (node->num_nodes == 0);
42}
43
48void
50{
51 uint32_t i;
52 if ( ! node ) return;
53
54 if (node->nodes)
55 {
56 for (i = 0; i < node->num_nodes; i++)
57 circ_tree_free(node->nodes[i]);
58 lwfree(node->nodes);
59 }
60 lwfree(node);
61}
62
63
67static CIRC_NODE*
69{
70 POINT2D *p1, *p2;
71 POINT3D q1, q2, c;
72 GEOGRAPHIC_POINT g1, g2, gc;
73 CIRC_NODE *node;
74 double diameter;
75
76 p1 = (POINT2D*)getPoint_internal(pa, i);
77 p2 = (POINT2D*)getPoint_internal(pa, i+1);
78 geographic_point_init(p1->x, p1->y, &g1);
79 geographic_point_init(p2->x, p2->y, &g2);
80
81 LWDEBUGF(3,"edge #%d (%g %g, %g %g)", i, p1->x, p1->y, p2->x, p2->y);
82
83 diameter = sphere_distance(&g1, &g2);
84
85 /* Zero length edge, doesn't get a node */
86 if ( FP_EQUALS(diameter, 0.0) )
87 return NULL;
88
89 /* Allocate */
90 node = lwalloc(sizeof(CIRC_NODE));
91 node->p1 = p1;
92 node->p2 = p2;
93
94 /* Convert ends to X/Y/Z, sum, and normalize to get mid-point */
95 geog2cart(&g1, &q1);
96 geog2cart(&g2, &q2);
97 vector_sum(&q1, &q2, &c);
98 normalize(&c);
99 cart2geog(&c, &gc);
100 node->center = gc;
101 node->radius = diameter / 2.0;
102
103 LWDEBUGF(3,"edge #%d CENTER(%g %g) RADIUS=%g", i, gc.lon, gc.lat, node->radius);
104
105 /* Leaf has no children */
106 node->num_nodes = 0;
107 node->nodes = NULL;
108 node->edge_num = i;
109
110 /* Zero out metadata */
111 node->pt_outside.x = 0.0;
112 node->pt_outside.y = 0.0;
113 node->geom_type = 0;
114
115 return node;
116}
117
121static CIRC_NODE*
123{
124 CIRC_NODE* tree = lwalloc(sizeof(CIRC_NODE));
125 tree->p1 = tree->p2 = (POINT2D*)getPoint_internal(pa, 0);
126 geographic_point_init(tree->p1->x, tree->p1->y, &(tree->center));
127 tree->radius = 0.0;
128 tree->nodes = NULL;
129 tree->num_nodes = 0;
130 tree->edge_num = 0;
131 tree->geom_type = POINTTYPE;
132 tree->pt_outside.x = 0.0;
133 tree->pt_outside.y = 0.0;
134 return tree;
135}
136
141static int
142circ_node_compare(const void* v1, const void* v2)
143{
144 POINT2D p1, p2;
145 unsigned int u1, u2;
146 CIRC_NODE *c1 = *((CIRC_NODE**)v1);
147 CIRC_NODE *c2 = *((CIRC_NODE**)v2);
148 p1.x = rad2deg((c1->center).lon);
149 p1.y = rad2deg((c1->center).lat);
150 p2.x = rad2deg((c2->center).lon);
151 p2.y = rad2deg((c2->center).lat);
152 u1 = geohash_point_as_int(&p1);
153 u2 = geohash_point_as_int(&p2);
154 if ( u1 < u2 ) return -1;
155 if ( u1 > u2 ) return 1;
156 return 0;
157}
158
165static int
166circ_center_spherical(const GEOGRAPHIC_POINT* c1, const GEOGRAPHIC_POINT* c2, double distance, double offset, GEOGRAPHIC_POINT* center)
167{
168 /* Direction from c1 to c2 */
169 double dir = sphere_direction(c1, c2, distance);
170
171 LWDEBUG(4,"calculating spherical center");
172
173 LWDEBUGF(4,"dir is %g", dir);
174
175 /* Catch sphere_direction when it barfs */
176 if ( isnan(dir) )
177 return LW_FAILURE;
178
179 /* Center of new circle is projection from start point, using offset distance*/
180 return sphere_project(c1, offset, dir, center);
181}
182
190static int
191circ_center_cartesian(const GEOGRAPHIC_POINT* c1, const GEOGRAPHIC_POINT* c2, double distance, double offset, GEOGRAPHIC_POINT* center)
192{
193 POINT3D p1, p2;
194 POINT3D p1p2, pc;
195 double proportion = offset/distance;
196
197 LWDEBUG(4,"calculating cartesian center");
198
199 geog2cart(c1, &p1);
200 geog2cart(c2, &p2);
201
202 /* Difference between p2 and p1 */
203 p1p2.x = p2.x - p1.x;
204 p1p2.y = p2.y - p1.y;
205 p1p2.z = p2.z - p1.z;
206
207 /* Scale difference to proportion */
208 p1p2.x *= proportion;
209 p1p2.y *= proportion;
210 p1p2.z *= proportion;
211
212 /* Add difference to p1 to get approximate center point */
213 pc.x = p1.x + p1p2.x;
214 pc.y = p1.y + p1p2.y;
215 pc.z = p1.z + p1p2.z;
216 normalize(&pc);
217
218 /* Convert center point to geographics */
219 cart2geog(&pc, center);
220
221 return LW_SUCCESS;
222}
223
224
229static CIRC_NODE*
230circ_node_internal_new(CIRC_NODE** c, uint32_t num_nodes)
231{
232 CIRC_NODE *node = NULL;
233 GEOGRAPHIC_POINT new_center, c1;
234 double new_radius;
235 double offset1, dist, D, r1, ri;
236 uint32_t i, new_geom_type;
237
238 LWDEBUGF(3, "called with %d nodes --", num_nodes);
239
240 /* Can't do anything w/ empty input */
241 if ( num_nodes < 1 )
242 return node;
243
244 /* Initialize calculation with values of the first circle */
245 new_center = c[0]->center;
246 new_radius = c[0]->radius;
247 new_geom_type = c[0]->geom_type;
248
249 /* Merge each remaining circle into the new circle */
250 for ( i = 1; i < num_nodes; i++ )
251 {
252 c1 = new_center;
253 r1 = new_radius;
254
255 dist = sphere_distance(&c1, &(c[i]->center));
256 ri = c[i]->radius;
257
258 /* Promote geometry types up the tree, getting more and more collected */
259 /* Go until we find a value */
260 if ( ! new_geom_type )
261 {
262 new_geom_type = c[i]->geom_type;
263 }
264 /* Promote singleton to a multi-type */
265 else if ( ! lwtype_is_collection(new_geom_type) )
266 {
267 /* Anonymous collection if types differ */
268 if ( new_geom_type != c[i]->geom_type )
269 {
270 new_geom_type = COLLECTIONTYPE;
271 }
272 else
273 {
274 new_geom_type = lwtype_get_collectiontype(new_geom_type);
275 }
276 }
277 /* If we can't add next feature to this collection cleanly, promote again to anonymous collection */
278 else if ( new_geom_type != lwtype_get_collectiontype(c[i]->geom_type) )
279 {
280 new_geom_type = COLLECTIONTYPE;
281 }
282
283
284 LWDEBUGF(3, "distance between new (%g %g) and %i (%g %g) is %g", c1.lon, c1.lat, i, c[i]->center.lon, c[i]->center.lat, dist);
285
286 if ( FP_EQUALS(dist, 0) )
287 {
288 LWDEBUG(3, " distance between centers is zero");
289 new_radius = r1 + 2*dist;
290 new_center = c1;
291 }
292 else if ( dist < fabs(r1 - ri) )
293 {
294 /* new contains next */
295 if ( r1 > ri )
296 {
297 LWDEBUG(3, " c1 contains ci");
298 new_center = c1;
299 new_radius = r1;
300 }
301 /* next contains new */
302 else
303 {
304 LWDEBUG(3, " ci contains c1");
305 new_center = c[i]->center;
306 new_radius = ri;
307 }
308 }
309 else
310 {
311 LWDEBUG(3, " calculating new center");
312 /* New circle diameter */
313 D = dist + r1 + ri;
314 LWDEBUGF(3," D is %g", D);
315
316 /* New radius */
317 new_radius = D / 2.0;
318
319 /* Distance from cn1 center to the new center */
320 offset1 = ri + (D - (2.0*r1 + 2.0*ri)) / 2.0;
321 LWDEBUGF(3," offset1 is %g", offset1);
322
323 /* Sometimes the sphere_direction function fails... this causes the center calculation */
324 /* to fail too. In that case, we're going to fall back to a cartesian calculation, which */
325 /* is less exact, so we also have to pad the radius by (hack alert) an arbitrary amount */
326 /* which is hopefully always big enough to contain the input edges */
327 if ( circ_center_spherical(&c1, &(c[i]->center), dist, offset1, &new_center) == LW_FAILURE )
328 {
329 circ_center_cartesian(&c1, &(c[i]->center), dist, offset1, &new_center);
330 new_radius *= 1.1;
331 }
332 }
333 LWDEBUGF(3, " new center is (%g %g) new radius is %g", new_center.lon, new_center.lat, new_radius);
334 }
335
336 node = lwalloc(sizeof(CIRC_NODE));
337 node->p1 = NULL;
338 node->p2 = NULL;
339 node->center = new_center;
340 node->radius = new_radius;
341 node->num_nodes = num_nodes;
342 node->nodes = c;
343 node->edge_num = -1;
344 node->geom_type = new_geom_type;
345 node->pt_outside.x = 0.0;
346 node->pt_outside.y = 0.0;
347 return node;
348}
349
355{
356 int num_edges;
357 int i, j;
358 CIRC_NODE **nodes;
359 CIRC_NODE *node;
360 CIRC_NODE *tree;
361
362 /* Can't do anything with no points */
363 if ( pa->npoints < 1 )
364 return NULL;
365
366 /* Special handling for a single point */
367 if ( pa->npoints == 1 )
368 return circ_node_leaf_point_new(pa);
369
370 /* First create a flat list of nodes, one per edge. */
371 num_edges = pa->npoints - 1;
372 nodes = lwalloc(sizeof(CIRC_NODE*) * pa->npoints);
373 j = 0;
374 for ( i = 0; i < num_edges; i++ )
375 {
376 node = circ_node_leaf_new(pa, i);
377 if ( node ) /* Not zero length? */
378 nodes[j++] = node;
379 }
380
381 /* Special case: only zero-length edges. Make a point node. */
382 if ( j == 0 ) {
383 lwfree(nodes);
384 return circ_node_leaf_point_new(pa);
385 }
386
387 /* Merge the node list pairwise up into a tree */
388 tree = circ_nodes_merge(nodes, j);
389
390 /* Free the old list structure, leaving the tree in place */
391 lwfree(nodes);
392
393 return tree;
394}
395
401static void
402circ_nodes_sort(CIRC_NODE** nodes, int num_nodes)
403{
404 qsort(nodes, num_nodes, sizeof(CIRC_NODE*), circ_node_compare);
405}
406
407
408static CIRC_NODE*
409circ_nodes_merge(CIRC_NODE** nodes, int num_nodes)
410{
411 CIRC_NODE **inodes = NULL;
412 int num_children = num_nodes;
413 int inode_num = 0;
414 int num_parents = 0;
415 int j;
416
417 /* TODO, roll geom_type *up* as tree is built, changing to collection types as simple types are merged
418 * TODO, change the distance algorithm to drive down to simple types first, test pip on poly/other cases, then test edges
419 */
420
421 while( num_children > 1 )
422 {
423 for ( j = 0; j < num_children; j++ )
424 {
425 inode_num = (j % CIRC_NODE_SIZE);
426 if ( inode_num == 0 )
427 inodes = lwalloc(sizeof(CIRC_NODE*)*CIRC_NODE_SIZE);
428
429 inodes[inode_num] = nodes[j];
430
431 if ( inode_num == CIRC_NODE_SIZE-1 )
432 nodes[num_parents++] = circ_node_internal_new(inodes, CIRC_NODE_SIZE);
433 }
434
435 /* Clean up any remaining nodes... */
436 if ( inode_num == 0 )
437 {
438 /* Promote solo nodes without merging */
439 nodes[num_parents++] = inodes[0];
440 lwfree(inodes);
441 }
442 else if ( inode_num < CIRC_NODE_SIZE-1 )
443 {
444 /* Merge spare nodes */
445 nodes[num_parents++] = circ_node_internal_new(inodes, inode_num+1);
446 }
447
448 num_children = num_parents;
449 num_parents = 0;
450 }
451
452 /* Return a reference to the head of the tree */
453 return nodes[0];
454}
455
456
461{
462 if ( circ_node_is_leaf(node) )
463 {
464 pt->x = node->p1->x;
465 pt->y = node->p1->y;
466 return LW_SUCCESS;
467 }
468 else
469 {
470 return circ_tree_get_point(node->nodes[0], pt);
471 }
472}
473
475{
476 POINT3D center3d;
478 // if (node->radius >= M_PI) return LW_FAILURE;
479 geog2cart(&(node->center), &center3d);
480 vector_scale(&center3d, -1.0);
481 cart2geog(&center3d, &g);
482 pt->x = rad2deg(g.lon);
483 pt->y = rad2deg(g.lat);
484 return LW_SUCCESS;
485}
486
487
494int circ_tree_contains_point(const CIRC_NODE* node, const POINT2D* pt, const POINT2D* pt_outside, int level, int* on_boundary)
495{
496 GEOGRAPHIC_POINT closest;
497 GEOGRAPHIC_EDGE stab_edge, edge;
498 POINT3D S1, S2, E1, E2;
499 double d;
500 uint32_t i, c;
501
502 /* Construct a stabline edge from our "inside" to our known outside point */
503 geographic_point_init(pt->x, pt->y, &(stab_edge.start));
504 geographic_point_init(pt_outside->x, pt_outside->y, &(stab_edge.end));
505 geog2cart(&(stab_edge.start), &S1);
506 geog2cart(&(stab_edge.end), &S2);
507
508 LWDEBUGF(3, "%*s entered", level, "");
509
510 /*
511 * If the stabline doesn't cross within the radius of a node, there's no
512 * way it can cross.
513 */
514
515 LWDEBUGF(3, "%*s :working on node %p, edge_num %d, radius %g, center POINT(%.12g %.12g)", level, "", node, node->edge_num, node->radius, rad2deg(node->center.lon), rad2deg(node->center.lat));
516 d = edge_distance_to_point(&stab_edge, &(node->center), &closest);
517 LWDEBUGF(3, "%*s :edge_distance_to_point=%g, node_radius=%g", level, "", d, node->radius);
518 if ( FP_LTEQ(d, node->radius) )
519 {
520 LWDEBUGF(3,"%*s :entering this branch (%p)", level, "", node);
521
522 /* Return the crossing number of this leaf */
523 if ( circ_node_is_leaf(node) )
524 {
525 int inter;
526 LWDEBUGF(3, "%*s :leaf node calculation (edge %d)", level, "", node->edge_num);
527 geographic_point_init(node->p1->x, node->p1->y, &(edge.start));
528 geographic_point_init(node->p2->x, node->p2->y, &(edge.end));
529 geog2cart(&(edge.start), &E1);
530 geog2cart(&(edge.end), &E2);
531
532 inter = edge_intersects(&S1, &S2, &E1, &E2);
533 LWDEBUGF(3, "%*s :inter = %d", level, "", inter);
534
535 if ( inter & PIR_INTERSECTS )
536 {
537 LWDEBUGF(3,"%*s ::got stab line edge_intersection with this edge!", level, "");
538 /* To avoid double counting crossings-at-a-vertex, */
539 /* always ignore crossings at "lower" ends of edges*/
540 GEOGRAPHIC_POINT e1, e2;
541 cart2geog(&E1,&e1); cart2geog(&E2,&e2);
542
543 LWDEBUGF(3,"%*s LINESTRING(%.15g %.15g,%.15g %.15g)", level, "",
544 pt->x, pt->y,
545 pt_outside->x, pt_outside->y
546 );
547
548 LWDEBUGF(3,"%*s LINESTRING(%.15g %.15g,%.15g %.15g)", level, "",
549 rad2deg(e1.lon), rad2deg(e1.lat),
550 rad2deg(e2.lon), rad2deg(e2.lat)
551 );
552
553 if ( inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR )
554 {
555 LWDEBUGF(3,"%*s ::rejecting stab line grazing by left-side edge", level, "");
556 return 0;
557 }
558 else
559 {
560 LWDEBUGF(3,"%*s ::accepting stab line intersection", level, "");
561 return 1;
562 }
563 }
564 else
565 {
566 LWDEBUGF(3,"%*s edge does not intersect", level, "");
567 }
568 }
569 /* Or, add up the crossing numbers of all children of this node. */
570 else
571 {
572 c = 0;
573 for ( i = 0; i < node->num_nodes; i++ )
574 {
575 LWDEBUGF(3,"%*s calling circ_tree_contains_point on child %d!", level, "", i);
576 c += circ_tree_contains_point(node->nodes[i], pt, pt_outside, level + 1, on_boundary);
577 }
578 return c % 2;
579 }
580 }
581 else
582 {
583 LWDEBUGF(3,"%*s skipping this branch (%p)", level, "", node);
584 }
585 return 0;
586}
587
588static double
590{
591 double d = sphere_distance(&(n1->center), &(n2->center));
592 double r1 = n1->radius;
593 double r2 = n2->radius;
594
595 if ( d < r1 + r2 )
596 return 0.0;
597
598 return d - r1 - r2;
599}
600
601static double
603{
604 return sphere_distance(&(n1->center), &(n2->center)) + n1->radius + n2->radius;
605}
606
607double
608circ_tree_distance_tree(const CIRC_NODE* n1, const CIRC_NODE* n2, const SPHEROID* spheroid, double threshold)
609{
610 double min_dist = FLT_MAX;
611 double max_dist = FLT_MAX;
612 GEOGRAPHIC_POINT closest1, closest2;
613 /* Quietly decrease the threshold just a little to avoid cases where */
614 /* the actual spheroid distance is larger than the sphere distance */
615 /* causing the return value to be larger than the threshold value */
616 double threshold_radians = 0.95 * threshold / spheroid->radius;
617
618 circ_tree_distance_tree_internal(n1, n2, threshold_radians, &min_dist, &max_dist, &closest1, &closest2);
619
620 /* Spherical case */
621 if ( spheroid->a == spheroid->b )
622 {
623 return spheroid->radius * sphere_distance(&closest1, &closest2);
624 }
625 else
626 {
627 return spheroid_distance(&closest1, &closest2, spheroid);
628 }
629}
630
631
632/***********************************************************************
633* Internal node sorting routine to make distance calculations faster?
634*/
635
636struct sort_node {
638 double d;
639};
640
641static int
642circ_nodes_sort_cmp(const void *a, const void *b)
643{
644 struct sort_node *node_a = (struct sort_node *)(a);
645 struct sort_node *node_b = (struct sort_node *)(b);
646 if (node_a->d < node_b->d) return -1;
647 else if (node_a->d > node_b->d) return 1;
648 else return 0;
649}
650
651static void
652circ_internal_nodes_sort(CIRC_NODE **nodes, uint32_t num_nodes, const CIRC_NODE *target_node)
653{
654 uint32_t i;
655 struct sort_node sort_nodes[CIRC_NODE_SIZE];
656
657 /* Copy incoming nodes into sorting array and calculate */
658 /* distance to the target node */
659 for (i = 0; i < num_nodes; i++)
660 {
661 sort_nodes[i].node = nodes[i];
662 sort_nodes[i].d = sphere_distance(&(nodes[i]->center), &(target_node->center));
663 }
664
665 /* Sort the nodes and copy the result back into the input array */
666 qsort(sort_nodes, num_nodes, sizeof(struct sort_node), circ_nodes_sort_cmp);
667 for (i = 0; i < num_nodes; i++)
668 {
669 nodes[i] = sort_nodes[i].node;
670 }
671 return;
672}
673
674/***********************************************************************/
675
676double
677circ_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)
678{
679 double max;
680 double d, d_min;
681 uint32_t i;
682
683 LWDEBUGF(4, "entered, min_dist=%.8g max_dist=%.8g, type1=%d, type2=%d", *min_dist, *max_dist, n1->geom_type, n2->geom_type);
684
685 // printf("-==-\n");
686 // circ_tree_print(n1, 0);
687 // printf("--\n");
688 // circ_tree_print(n2, 0);
689
690 /* Short circuit if we've already hit the minimum */
691 if( *min_dist < threshold || *min_dist == 0.0 )
692 return *min_dist;
693
694 /* If your minimum is greater than anyone's maximum, you can't hold the winner */
695 if( circ_node_min_distance(n1, n2) > *max_dist )
696 {
697 LWDEBUGF(4, "pruning pair %p, %p", n1, n2);
698 return FLT_MAX;
699 }
700
701 /* If your maximum is a new low, we'll use that as our new global tolerance */
702 max = circ_node_max_distance(n1, n2);
703 LWDEBUGF(5, "max %.8g", max);
704 if( max < *max_dist )
705 *max_dist = max;
706
707 /* Polygon on one side, primitive type on the other. Check for point-in-polygon */
708 /* short circuit. */
709 if ( n1->geom_type == POLYGONTYPE && n2->geom_type && ! lwtype_is_collection(n2->geom_type) )
710 {
711 POINT2D pt;
712 circ_tree_get_point(n2, &pt);
713 LWDEBUGF(4, "n1 is polygon, testing if contains (%.5g,%.5g)", pt.x, pt.y);
714 if ( circ_tree_contains_point(n1, &pt, &(n1->pt_outside), 0, NULL) )
715 {
716 LWDEBUG(4, "it does");
717 *min_dist = 0.0;
718 geographic_point_init(pt.x, pt.y, closest1);
719 geographic_point_init(pt.x, pt.y, closest2);
720 return *min_dist;
721 }
722 }
723 /* Polygon on one side, primitive type on the other. Check for point-in-polygon */
724 /* short circuit. */
725 if ( n2->geom_type == POLYGONTYPE && n1->geom_type && ! lwtype_is_collection(n1->geom_type) )
726 {
727 POINT2D pt;
728 circ_tree_get_point(n1, &pt);
729 LWDEBUGF(4, "n2 is polygon, testing if contains (%.5g,%.5g)", pt.x, pt.y);
730 if ( circ_tree_contains_point(n2, &pt, &(n2->pt_outside), 0, NULL) )
731 {
732 LWDEBUG(4, "it does");
733 geographic_point_init(pt.x, pt.y, closest1);
734 geographic_point_init(pt.x, pt.y, closest2);
735 *min_dist = 0.0;
736 return *min_dist;
737 }
738 }
739
740 /* Both leaf nodes, do a real distance calculation */
741 if( circ_node_is_leaf(n1) && circ_node_is_leaf(n2) )
742 {
743 double d;
744 GEOGRAPHIC_POINT close1, close2;
745 LWDEBUGF(4, "testing leaf pair [%d], [%d]", n1->edge_num, n2->edge_num);
746 /* One of the nodes is a point */
747 if ( n1->p1 == n1->p2 || n2->p1 == n2->p2 )
748 {
750 GEOGRAPHIC_POINT gp1, gp2;
751
752 /* Both nodes are points! */
753 if ( n1->p1 == n1->p2 && n2->p1 == n2->p2 )
754 {
755 geographic_point_init(n1->p1->x, n1->p1->y, &gp1);
756 geographic_point_init(n2->p1->x, n2->p1->y, &gp2);
757 close1 = gp1; close2 = gp2;
758 d = sphere_distance(&gp1, &gp2);
759 }
760 /* Node 1 is a point */
761 else if ( n1->p1 == n1->p2 )
762 {
763 geographic_point_init(n1->p1->x, n1->p1->y, &gp1);
764 geographic_point_init(n2->p1->x, n2->p1->y, &(e.start));
765 geographic_point_init(n2->p2->x, n2->p2->y, &(e.end));
766 close1 = gp1;
767 d = edge_distance_to_point(&e, &gp1, &close2);
768 }
769 /* Node 2 is a point */
770 else
771 {
772 geographic_point_init(n2->p1->x, n2->p1->y, &gp2);
773 geographic_point_init(n1->p1->x, n1->p1->y, &(e.start));
774 geographic_point_init(n1->p2->x, n1->p2->y, &(e.end));
775 close2 = gp2;
776 d = edge_distance_to_point(&e, &gp2, &close1);
777 }
778 LWDEBUGF(4, " got distance %g", d);
779 }
780 /* Both nodes are edges */
781 else
782 {
783 GEOGRAPHIC_EDGE e1, e2;
785 POINT3D A1, A2, B1, B2;
786 geographic_point_init(n1->p1->x, n1->p1->y, &(e1.start));
787 geographic_point_init(n1->p2->x, n1->p2->y, &(e1.end));
788 geographic_point_init(n2->p1->x, n2->p1->y, &(e2.start));
789 geographic_point_init(n2->p2->x, n2->p2->y, &(e2.end));
790 geog2cart(&(e1.start), &A1);
791 geog2cart(&(e1.end), &A2);
792 geog2cart(&(e2.start), &B1);
793 geog2cart(&(e2.end), &B2);
794 if ( edge_intersects(&A1, &A2, &B1, &B2) )
795 {
796 d = 0.0;
797 edge_intersection(&e1, &e2, &g);
798 close1 = close2 = g;
799 }
800 else
801 {
802 d = edge_distance_to_edge(&e1, &e2, &close1, &close2);
803 }
804 LWDEBUGF(4, "edge_distance_to_edge returned %g", d);
805 }
806 if ( d < *min_dist )
807 {
808 *min_dist = d;
809 *closest1 = close1;
810 *closest2 = close2;
811 }
812 return d;
813 }
814 else
815 {
816 d_min = FLT_MAX;
817 /* Drive the recursion into the COLLECTION types first so we end up with */
818 /* pairings of primitive geometries that can be forced into the point-in-polygon */
819 /* tests above. */
820 if ( n1->geom_type && lwtype_is_collection(n1->geom_type) )
821 {
823 for ( i = 0; i < n1->num_nodes; i++ )
824 {
825 d = circ_tree_distance_tree_internal(n1->nodes[i], n2, threshold, min_dist, max_dist, closest1, closest2);
826 d_min = FP_MIN(d_min, d);
827 }
828 }
829 else if ( n2->geom_type && lwtype_is_collection(n2->geom_type) )
830 {
832 for ( i = 0; i < n2->num_nodes; i++ )
833 {
834 d = circ_tree_distance_tree_internal(n1, n2->nodes[i], threshold, min_dist, max_dist, closest1, closest2);
835 d_min = FP_MIN(d_min, d);
836 }
837 }
838 else if ( ! circ_node_is_leaf(n1) )
839 {
841 for ( i = 0; i < n1->num_nodes; i++ )
842 {
843 d = circ_tree_distance_tree_internal(n1->nodes[i], n2, threshold, min_dist, max_dist, closest1, closest2);
844 d_min = FP_MIN(d_min, d);
845 }
846 }
847 else if ( ! circ_node_is_leaf(n2) )
848 {
850 for ( i = 0; i < n2->num_nodes; i++ )
851 {
852 d = circ_tree_distance_tree_internal(n1, n2->nodes[i], threshold, min_dist, max_dist, closest1, closest2);
853 d_min = FP_MIN(d_min, d);
854 }
855 }
856 else
857 {
858 /* Never get here */
859 }
860
861 return d_min;
862 }
863}
864
865
866
867
868
869void circ_tree_print(const CIRC_NODE* node, int depth)
870{
871 uint32_t i;
872
874 {
875 printf("%*s[%d] C(%.8g %.8g) R(%.8g) ((%.8g %.8g),(%.8g,%.8g))",
876 3*depth + 6, "NODE", node->edge_num,
878 node->radius,
879 node->p1->x, node->p1->y,
880 node->p2->x, node->p2->y
881 );
882 if ( node->geom_type )
883 {
884 printf(" %s", lwtype_name(node->geom_type));
885 }
886 if ( node->geom_type == POLYGONTYPE )
887 {
888 printf(" O(%.8g %.8g)", node->pt_outside.x, node->pt_outside.y);
889 }
890 printf("\n");
891
892 }
893 else
894 {
895 printf("%*s C(%.8g %.8g) R(%.8g)",
896 3*depth + 6, "NODE",
898 node->radius
899 );
900 if ( node->geom_type )
901 {
902 printf(" %s", lwtype_name(node->geom_type));
903 }
904 if ( node->geom_type == POLYGONTYPE )
905 {
906 printf(" O(%.15g %.15g)", node->pt_outside.x, node->pt_outside.y);
907 }
908 printf("\n");
909 }
910 for ( i = 0; i < node->num_nodes; i++ )
911 {
912 circ_tree_print(node->nodes[i], depth + 1);
913 }
914 return;
915}
916
917
918static CIRC_NODE*
920{
922 node = circ_tree_new(lwpoint->point);
923 node->geom_type = lwgeom_get_type((LWGEOM*)lwpoint);;
924 return node;
925}
926
927static CIRC_NODE*
929{
931 node = circ_tree_new(lwline->points);
933 return node;
934}
935
936static CIRC_NODE*
938{
939 uint32_t i = 0, j = 0;
940 CIRC_NODE** nodes;
942
943 /* One ring? Handle it like a line. */
944 if ( lwpoly->nrings == 1 )
945 {
946 node = circ_tree_new(lwpoly->rings[0]);
947 }
948 else
949 {
950 /* Calculate a tree for each non-trivial ring of the polygon */
951 nodes = lwalloc(lwpoly->nrings * sizeof(CIRC_NODE*));
952 for ( i = 0; i < lwpoly->nrings; i++ )
953 {
954 node = circ_tree_new(lwpoly->rings[i]);
955 if ( node )
956 nodes[j++] = node;
957 }
958 /* Put the trees into a spatially correlated order */
959 circ_nodes_sort(nodes, j);
960 /* Merge the trees pairwise up to a parent node and return */
961 node = circ_nodes_merge(nodes, j);
962 /* Don't need the working list any more */
963 lwfree(nodes);
964 }
965
966 /* Metadata about polygons, we need this to apply P-i-P tests */
967 /* selectively when doing distance calculations */
969 lwpoly_pt_outside(lwpoly, &(node->pt_outside));
970
971 return node;
972}
973
974static CIRC_NODE*
976{
977 uint32_t i = 0, j = 0;
978 CIRC_NODE** nodes;
980
981 /* One geometry? Done! */
982 if ( lwcol->ngeoms == 1 )
983 return lwgeom_calculate_circ_tree(lwcol->geoms[0]);
984
985 /* Calculate a tree for each sub-geometry*/
986 nodes = lwalloc(lwcol->ngeoms * sizeof(CIRC_NODE*));
987 for ( i = 0; i < lwcol->ngeoms; i++ )
988 {
990 if ( node )
991 nodes[j++] = node;
992 }
993 /* Put the trees into a spatially correlated order */
994 circ_nodes_sort(nodes, j);
995 /* Merge the trees pairwise up to a parent node and return */
996 node = circ_nodes_merge(nodes, j);
997 /* Don't need the working list any more */
998 lwfree(nodes);
1000 return node;
1001}
1002
1003CIRC_NODE*
1005{
1006 if ( lwgeom_is_empty(lwgeom) )
1007 return NULL;
1008
1009 switch ( lwgeom->type )
1010 {
1011 case POINTTYPE:
1012 return lwpoint_calculate_circ_tree((LWPOINT*)lwgeom);
1013 case LINETYPE:
1014 return lwline_calculate_circ_tree((LWLINE*)lwgeom);
1015 case POLYGONTYPE:
1016 return lwpoly_calculate_circ_tree((LWPOLY*)lwgeom);
1017 case MULTIPOINTTYPE:
1018 case MULTILINETYPE:
1019 case MULTIPOLYGONTYPE:
1020 case COLLECTIONTYPE:
1022 default:
1023 lwerror("Unable to calculate spherical index tree for type %s", lwtype_name(lwgeom->type));
1024 return NULL;
1025 }
1026
1027}
1028
1029
1030/***********************************************************************
1031 * Closest point and closest line functions for geographies.
1032 ***********************************************************************/
1033
1034LWGEOM *
1035geography_tree_closestpoint(const LWGEOM* lwgeom1, const LWGEOM* lwgeom2, double threshold)
1036{
1037 CIRC_NODE* circ_tree1 = NULL;
1038 CIRC_NODE* circ_tree2 = NULL;
1039 double min_dist = FLT_MAX;
1040 double max_dist = FLT_MAX;
1041 GEOGRAPHIC_POINT closest1, closest2;
1042 LWGEOM *result;
1043 POINT4D p;
1044
1045 circ_tree1 = lwgeom_calculate_circ_tree(lwgeom1);
1046 circ_tree2 = lwgeom_calculate_circ_tree(lwgeom2);
1047
1048 /* Quietly decrease the threshold just a little to avoid cases where */
1049 /* the actual spheroid distance is larger than the sphere distance */
1050 /* causing the return value to be larger than the threshold value */
1051 // double threshold_radians = 0.95 * threshold / spheroid->radius;
1052 double threshold_radians = threshold / WGS84_RADIUS;
1053
1055 circ_tree1, circ_tree2, threshold_radians,
1056 &min_dist, &max_dist, &closest1, &closest2);
1057
1058 p.x = rad2deg(closest1.lon);
1059 p.y = rad2deg(closest1.lat);
1060 result = (LWGEOM *)lwpoint_make2d(lwgeom_get_srid(lwgeom1), p.x, p.y);
1061
1062 circ_tree_free(circ_tree1);
1063 circ_tree_free(circ_tree2);
1064 return result;
1065}
1066
1067
1068LWGEOM *
1069geography_tree_shortestline(const LWGEOM* lwgeom1, const LWGEOM* lwgeom2, double threshold, const SPHEROID *spheroid)
1070{
1071 CIRC_NODE* circ_tree1 = NULL;
1072 CIRC_NODE* circ_tree2 = NULL;
1073 double min_dist = FLT_MAX;
1074 double max_dist = FLT_MAX;
1075 GEOGRAPHIC_POINT closest1, closest2;
1076 LWGEOM *geoms[2];
1077 LWGEOM *result;
1078 POINT4D p1, p2;
1079 uint32_t srid = lwgeom1->srid;
1080
1081 circ_tree1 = lwgeom_calculate_circ_tree(lwgeom1);
1082 circ_tree2 = lwgeom_calculate_circ_tree(lwgeom2);
1083
1084 /* Quietly decrease the threshold just a little to avoid cases where */
1085 /* the actual spheroid distance is larger than the sphere distance */
1086 /* causing the return value to be larger than the threshold value */
1087 // double threshold_radians = 0.95 * threshold / spheroid->radius;
1088 double threshold_radians = threshold / spheroid->radius;
1089
1090 circ_tree_distance_tree_internal(circ_tree1, circ_tree2, threshold_radians,
1091 &min_dist, &max_dist, &closest1, &closest2);
1092
1093 p1.x = rad2deg(closest1.lon);
1094 p1.y = rad2deg(closest1.lat);
1095 p2.x = rad2deg(closest2.lon);
1096 p2.y = rad2deg(closest2.lat);
1097
1098 geoms[0] = (LWGEOM *)lwpoint_make2d(srid, p1.x, p1.y);
1099 geoms[1] = (LWGEOM *)lwpoint_make2d(srid, p2.x, p2.y);
1100 result = (LWGEOM *)lwline_from_lwgeom_array(srid, 2, geoms);
1101
1102 lwgeom_free(geoms[0]);
1103 lwgeom_free(geoms[1]);
1104 circ_tree_free(circ_tree1);
1105 circ_tree_free(circ_tree2);
1106 return result;
1107}
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition cu_print.c:267
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
uint32_t lwtype_get_collectiontype(uint8_t type)
Given an lwtype number, what homogeneous collection can hold it?
Definition lwgeom.c:1222
#define COLLECTIONTYPE
Definition liblwgeom.h:108
LWLINE * lwline_from_lwgeom_array(int32_t srid, uint32_t ngeoms, LWGEOM **geoms)
Definition lwline.c:151
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition lwgeom.c:955
#define LW_FAILURE
Definition liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
#define MULTILINETYPE
Definition liblwgeom.h:106
#define LINETYPE
Definition liblwgeom.h:103
#define WGS84_RADIUS
Definition liblwgeom.h:148
#define LW_SUCCESS
Definition liblwgeom.h:97
unsigned int geohash_point_as_int(POINT2D *pt)
#define MULTIPOINTTYPE
Definition liblwgeom.h:105
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition lwgeom.c:1196
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
void * lwalloc(size_t size)
Definition lwutil.c:227
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
void lwfree(void *mem)
Definition lwutil.c:248
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition lwpoint.c:163
#define POLYGONTYPE
Definition liblwgeom.h:104
#define FP_MIN(A, B)
#define FP_EQUALS(A, B)
#define FP_LTEQ(A, B)
void normalize(POINT3D *p)
Normalize to a unit vector.
Definition lwgeodetic.c:615
void vector_scale(POINT3D *n, double scale)
Scale a vector out by a factor.
Definition lwgeodetic.c:487
void cart2geog(const POINT3D *p, GEOGRAPHIC_POINT *g)
Convert cartesian coordinates on unit sphere to spherical coordinates.
Definition lwgeodetic.c:414
int lwpoly_pt_outside(const LWPOLY *poly, POINT2D *pt_outside)
int sphere_project(const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n)
Given a starting location r, a distance and an azimuth to the new point, compute the location of the ...
void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g)
Initialize a geographic point.
Definition lwgeodetic.c:180
double sphere_distance(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e)
Given two points on a unit sphere, calculate their distance apart in radians.
Definition lwgeodetic.c:896
double sphere_direction(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d)
Given two points on a unit sphere, calculate the direction from s to e.
Definition lwgeodetic.c:927
int edge_intersection(const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *g)
Returns true if an intersection can be calculated, and places it in *g.
void vector_sum(const POINT3D *a, const POINT3D *b, POINT3D *n)
Calculate the sum of two vectors.
Definition lwgeodetic.c:465
uint32_t edge_intersects(const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2)
Returns non-zero if edges A and B interact.
double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest)
void geog2cart(const GEOGRAPHIC_POINT *g, POINT3D *p)
Convert spherical coordinates to cartesian coordinates on unit sphere.
Definition lwgeodetic.c:404
double edge_distance_to_edge(const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *closest1, GEOGRAPHIC_POINT *closest2)
Calculate the distance between two edges.
#define rad2deg(r)
Definition lwgeodetic.h:81
double spheroid_distance(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid)
Computes the shortest distance along the surface of the spheroid between two points,...
Definition lwspheroid.c:79
#define PIR_COLINEAR
Definition lwgeodetic.h:89
#define PIR_INTERSECTS
Definition lwgeodetic.h:88
#define PIR_B_TOUCH_RIGHT
Definition lwgeodetic.h:92
static CIRC_NODE * lwcollection_calculate_circ_tree(const LWCOLLECTION *lwcol)
double circ_tree_distance_tree(const CIRC_NODE *n1, const CIRC_NODE *n2, const SPHEROID *spheroid, double threshold)
CIRC_NODE * lwgeom_calculate_circ_tree(const LWGEOM *lwgeom)
int circ_tree_get_point(const CIRC_NODE *node, POINT2D *pt)
Returns a POINT2D that is a vertex of the input shape.
static double circ_node_max_distance(const CIRC_NODE *n1, const CIRC_NODE *n2)
static CIRC_NODE * lwpoly_calculate_circ_tree(const LWPOLY *lwpoly)
static int circ_nodes_sort_cmp(const void *a, const void *b)
static CIRC_NODE * circ_node_leaf_point_new(const POINTARRAY *pa)
Return a point node (zero radius, referencing one point)
LWGEOM * geography_tree_closestpoint(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, double threshold)
void circ_tree_print(const CIRC_NODE *node, int depth)
static int circ_center_spherical(const GEOGRAPHIC_POINT *c1, const GEOGRAPHIC_POINT *c2, double distance, double offset, GEOGRAPHIC_POINT *center)
Given the centers of two circles, and the offset distance we want to put the new center between them ...
static int circ_node_is_leaf(const CIRC_NODE *node)
Internal nodes have their point references set to NULL.
void circ_tree_free(CIRC_NODE *node)
Recurse from top of node tree and free all children.
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)
static CIRC_NODE * circ_node_internal_new(CIRC_NODE **c, uint32_t num_nodes)
Create a new internal node, calculating the new measure range for the node, and storing pointers to t...
static void circ_internal_nodes_sort(CIRC_NODE **nodes, uint32_t num_nodes, const CIRC_NODE *target_node)
static int circ_center_cartesian(const GEOGRAPHIC_POINT *c1, const GEOGRAPHIC_POINT *c2, double distance, double offset, GEOGRAPHIC_POINT *center)
Where the circ_center_spherical() function fails, we need a fall-back.
CIRC_NODE * circ_tree_new(const POINTARRAY *pa)
Build a tree of nodes from a point array, one node per edge.
static CIRC_NODE * circ_node_leaf_new(const POINTARRAY *pa, int i)
Create a new leaf node, storing pointers back to the end points for later.
static CIRC_NODE * lwline_calculate_circ_tree(const LWLINE *lwline)
static CIRC_NODE * lwpoint_calculate_circ_tree(const LWPOINT *lwpoint)
int circ_tree_get_point_outside(const CIRC_NODE *node, POINT2D *pt)
static CIRC_NODE * circ_nodes_merge(CIRC_NODE **nodes, int num_nodes)
static double circ_node_min_distance(const CIRC_NODE *n1, const CIRC_NODE *n2)
static int circ_node_compare(const void *v1, const void *v2)
Comparing on geohash ensures that nearby nodes will be close to each other in the list.
static void circ_nodes_sort(CIRC_NODE **nodes, int num_nodes)
Given a list of nodes, sort them into a spatially consistent order, then pairwise merge them up into ...
LWGEOM * geography_tree_shortestline(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, double threshold, const SPHEROID *spheroid)
int circ_tree_contains_point(const CIRC_NODE *node, const POINT2D *pt, const POINT2D *pt_outside, int level, int *on_boundary)
Walk the tree and count intersections between the stab line and the edges.
#define CIRC_NODE_SIZE
#define LWDEBUG(level, msg)
Definition lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition lwgeom_log.h:106
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition lwinline.h:75
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition lwinline.h:141
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition lwinline.h:199
static double distance(double x1, double y1, double x2, double y2)
Definition lwtree.c:1032
GEOGRAPHIC_POINT start
Definition lwgeodetic.h:64
GEOGRAPHIC_POINT end
Definition lwgeodetic.h:65
Two-point great circle segment from a to b.
Definition lwgeodetic.h:63
Point in spherical coordinates on the world.
Definition lwgeodetic.h:54
uint32_t ngeoms
Definition liblwgeom.h:580
LWGEOM ** geoms
Definition liblwgeom.h:575
uint8_t type
Definition liblwgeom.h:462
int32_t srid
Definition liblwgeom.h:460
POINTARRAY * points
Definition liblwgeom.h:483
POINTARRAY * point
Definition liblwgeom.h:471
uint8_t type
Definition liblwgeom.h:474
POINTARRAY ** rings
Definition liblwgeom.h:519
uint32_t nrings
Definition liblwgeom.h:524
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
double z
Definition liblwgeom.h:402
double x
Definition liblwgeom.h:402
double y
Definition liblwgeom.h:402
double x
Definition liblwgeom.h:414
double y
Definition liblwgeom.h:414
uint32_t npoints
Definition liblwgeom.h:427
double radius
Definition liblwgeom.h:380
double a
Definition liblwgeom.h:375
double b
Definition liblwgeom.h:376
uint32_t num_nodes
POINT2D * p2
struct circ_node ** nodes
POINT2D * p1
POINT2D pt_outside
GEOGRAPHIC_POINT center
uint32_t geom_type
Note that p1 and p2 are pointers into an independent POINTARRAY, do not free them.
CIRC_NODE * node