PostGIS  3.7.0dev-r@@SVN_REVISION@@
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 */
32 static CIRC_NODE* circ_nodes_merge(CIRC_NODE** nodes, int num_nodes);
33 
34 
38 static inline int
40 {
41  return (node->num_nodes == 0);
42 }
43 
48 void
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 
67 static CIRC_NODE*
68 circ_node_leaf_new(const POINTARRAY* pa, int i)
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 
121 static 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 
141 static int
142 circ_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 
165 static int
166 circ_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 
190 static int
191 circ_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 
229 static CIRC_NODE*
230 circ_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 
353 CIRC_NODE*
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 
401 static void
402 circ_nodes_sort(CIRC_NODE** nodes, int num_nodes)
403 {
404  qsort(nodes, num_nodes, sizeof(CIRC_NODE*), circ_node_compare);
405 }
406 
407 
408 static CIRC_NODE*
409 circ_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 
460 int circ_tree_get_point(const CIRC_NODE* node, POINT2D* pt)
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 
494 int 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 
588 static 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 
601 static double
603 {
604  return sphere_distance(&(n1->center), &(n2->center)) + n1->radius + n2->radius;
605 }
606 
607 double
608 circ_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 
636 struct sort_node {
638  double d;
639 };
640 
641 static int
642 circ_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 
651 static void
652 circ_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 
676 double
677 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)
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  {
749  GEOGRAPHIC_EDGE e;
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 
869 void circ_tree_print(const CIRC_NODE* node, int depth)
870 {
871  uint32_t i;
872 
873  if (circ_node_is_leaf(node))
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 
918 static CIRC_NODE*
920 {
921  CIRC_NODE* node;
922  node = circ_tree_new(lwpoint->point);
923  node->geom_type = lwgeom_get_type((LWGEOM*)lwpoint);;
924  return node;
925 }
926 
927 static CIRC_NODE*
929 {
930  CIRC_NODE* node;
931  node = circ_tree_new(lwline->points);
932  node->geom_type = lwgeom_get_type((LWGEOM*)lwline);
933  return node;
934 }
935 
936 static CIRC_NODE*
938 {
939  uint32_t i = 0, j = 0;
940  CIRC_NODE** nodes;
941  CIRC_NODE* node;
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 */
968  node->geom_type = lwgeom_get_type((LWGEOM*)lwpoly);
969  lwpoly_pt_outside(lwpoly, &(node->pt_outside));
970 
971  return node;
972 }
973 
974 static CIRC_NODE*
976 {
977  uint32_t i = 0, j = 0;
978  CIRC_NODE** nodes;
979  CIRC_NODE* node;
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);
999  node->geom_type = lwgeom_get_type((LWGEOM*)lwcol);
1000  return node;
1001 }
1002 
1003 CIRC_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 
1034 LWGEOM *
1035 geography_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 
1068 LWGEOM *
1069 geography_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
uint32_t lwtype_get_collectiontype(uint8_t type)
Given an lwtype number, what homogeneous collection can hold it?
Definition: lwgeom.c:1194
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:927
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition: lwpoint.c:163
#define LW_FAILURE
Definition: liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
#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)
Definition: lwalgorithm.c:669
#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:1168
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
void lwfree(void *mem)
Definition: lwutil.c:248
#define POLYGONTYPE
Definition: liblwgeom.h:104
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWLINE * lwline_from_lwgeom_array(int32_t srid, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwline.c:151
#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)
Definition: lwgeodetic.c:1481
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 ...
Definition: lwgeodetic.c:1268
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.
Definition: lwgeodetic.c:1079
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.
Definition: lwgeodetic.c:3389
double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest)
Definition: lwgeodetic.c:1170
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.
Definition: lwgeodetic.c:1223
#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
double circ_tree_distance_tree(const CIRC_NODE *n1, const CIRC_NODE *n2, const SPHEROID *spheroid, double threshold)
static CIRC_NODE * lwpoly_calculate_circ_tree(const LWPOLY *lwpoly)
CIRC_NODE * circ_tree_new(const POINTARRAY *pa)
Build a tree of nodes from a point array, one node per edge.
int circ_tree_get_point(const CIRC_NODE *node, POINT2D *pt)
Returns a POINT2D that is a vertex of the input shape.
static CIRC_NODE * lwcollection_calculate_circ_tree(const LWCOLLECTION *lwcol)
static double circ_node_max_distance(const CIRC_NODE *n1, const CIRC_NODE *n2)
static int circ_nodes_sort_cmp(const void *a, const void *b)
static CIRC_NODE * lwpoint_calculate_circ_tree(const LWPOINT *lwpoint)
static CIRC_NODE * lwline_calculate_circ_tree(const LWLINE *lwline)
void circ_tree_print(const CIRC_NODE *node, int depth)
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 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 void circ_internal_nodes_sort(CIRC_NODE **nodes, uint32_t num_nodes, const CIRC_NODE *target_node)
LWGEOM * geography_tree_closestpoint(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, double threshold)
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 * lwgeom_calculate_circ_tree(const LWGEOM *lwgeom)
int circ_tree_get_point_outside(const CIRC_NODE *node, POINT2D *pt)
static CIRC_NODE * circ_nodes_merge(CIRC_NODE **nodes, int num_nodes)
LWGEOM * geography_tree_shortestline(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, double threshold, const SPHEROID *spheroid)
static double circ_node_min_distance(const CIRC_NODE *n1, const CIRC_NODE *n2)
static CIRC_NODE * circ_node_leaf_point_new(const POINTARRAY *pa)
Return a point node (zero radius, referencing one point)
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 ...
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.
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...
#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 uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:141
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition: lwinline.h:75
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
double radius
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