PostGIS  3.0.6dev-r@@SVN_REVISION@@
measures3d.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 2011 Nicklas Avén
22  * Copyright 2019 Darafei Praliaskouski
23  *
24  **********************************************************************/
25 
26 #include <string.h>
27 #include <stdlib.h>
28 
29 #include "measures3d.h"
30 #include "lwrandom.h"
31 #include "lwgeom_log.h"
32 
33 static inline int
35 {
36  v->x = p2->x - p1->x;
37  v->y = p2->y - p1->y;
38  v->z = p2->z - p1->z;
39 
40  return (!FP_IS_ZERO(v->x) || !FP_IS_ZERO(v->y) || !FP_IS_ZERO(v->z));
41 }
42 
43 static inline int
45 {
46  v->x = (v1->y * v2->z) - (v1->z * v2->y);
47  v->y = (v1->z * v2->x) - (v1->x * v2->z);
48  v->z = (v1->x * v2->y) - (v1->y * v2->x);
49 
50  return (!FP_IS_ZERO(v->x) || !FP_IS_ZERO(v->y) || !FP_IS_ZERO(v->z));
51 }
52 
58 static LWGEOM *
59 create_v_line(const LWGEOM *lwgeom, double x, double y, int32_t srid)
60 {
61 
62  LWPOINT *lwpoints[2];
63  GBOX gbox;
64  int rv = lwgeom_calculate_gbox(lwgeom, &gbox);
65 
66  if (rv == LW_FAILURE)
67  return NULL;
68 
69  lwpoints[0] = lwpoint_make3dz(srid, x, y, gbox.zmin);
70  lwpoints[1] = lwpoint_make3dz(srid, x, y, gbox.zmax);
71 
72  return (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints);
73 }
74 
75 LWGEOM *
76 lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2)
77 {
78  return lw_dist3d_distanceline(lw1, lw2, lw1->srid, DIST_MIN);
79 }
80 
81 LWGEOM *
83 {
84  return lw_dist3d_distanceline(lw1, lw2, lw1->srid, DIST_MAX);
85 }
86 
87 LWGEOM *
88 lwgeom_closest_point_3d(const LWGEOM *lw1, const LWGEOM *lw2)
89 {
90  return lw_dist3d_distancepoint(lw1, lw2, lw1->srid, DIST_MIN);
91 }
92 
96 LWGEOM *
97 lw_dist3d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
98 {
99  LWDEBUG(2, "lw_dist3d_distanceline is called");
100  double x1, x2, y1, y2, z1, z2, x, y;
101  double initdistance = (mode == DIST_MIN ? DBL_MAX : -1.0);
102  DISTPTS3D thedl;
103  LWPOINT *lwpoints[2];
104  LWGEOM *result;
105 
106  thedl.mode = mode;
107  thedl.distance = initdistance;
108  thedl.tolerance = 0.0;
109 
110  /* Check if we really have 3D geometries
111  * If not, send it to 2D-calculations which will give the same result
112  * as an infinite z-value at one or two of the geometries */
113  if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2))
114  {
115 
116  lwnotice(
117  "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\"");
118 
119  if (!lwgeom_has_z(lw1) && !lwgeom_has_z(lw2))
120  return lw_dist2d_distanceline(lw1, lw2, srid, mode);
121 
122  DISTPTS thedl2d;
123  thedl2d.mode = mode;
124  thedl2d.distance = initdistance;
125  thedl2d.tolerance = 0.0;
126  if (!lw_dist2d_comp(lw1, lw2, &thedl2d))
127  {
128  /* should never get here. all cases ought to be error handled earlier */
129  lwerror("Some unspecified error.");
130  result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
131  }
132  LWGEOM *vertical_line;
133  if (!lwgeom_has_z(lw1))
134  {
135  x = thedl2d.p1.x;
136  y = thedl2d.p1.y;
137 
138  vertical_line = create_v_line(lw2, x, y, srid);
139  if (!lw_dist3d_recursive(vertical_line, lw2, &thedl))
140  {
141  /* should never get here. all cases ought to be error handled earlier */
142  lwfree(vertical_line);
143  lwerror("Some unspecified error.");
144  result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
145  }
146  lwfree(vertical_line);
147  }
148  if (!lwgeom_has_z(lw2))
149  {
150  x = thedl2d.p2.x;
151  y = thedl2d.p2.y;
152 
153  vertical_line = create_v_line(lw1, x, y, srid);
154  if (!lw_dist3d_recursive(lw1, vertical_line, &thedl))
155  {
156  /* should never get here. all cases ought to be error handled earlier */
157  lwfree(vertical_line);
158  lwerror("Some unspecified error.");
159  return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
160  }
161  lwfree(vertical_line);
162  }
163  }
164  else
165  {
166  if (!lw_dist3d_recursive(lw1, lw2, &thedl))
167  {
168  /* should never get here. all cases ought to be error handled earlier */
169  lwerror("Some unspecified error.");
170  result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
171  }
172  }
173  /*if thedl.distance is unchanged there where only empty geometries input*/
174  if (thedl.distance == initdistance)
175  {
176  LWDEBUG(3, "didn't find geometries to measure between, returning null");
177  result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
178  }
179  else
180  {
181  x1 = thedl.p1.x;
182  y1 = thedl.p1.y;
183  z1 = thedl.p1.z;
184  x2 = thedl.p2.x;
185  y2 = thedl.p2.y;
186  z2 = thedl.p2.z;
187 
188  lwpoints[0] = lwpoint_make3dz(srid, x1, y1, z1);
189  lwpoints[1] = lwpoint_make3dz(srid, x2, y2, z2);
190 
191  result = (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints);
192  }
193 
194  return result;
195 }
196 
200 LWGEOM *
201 lw_dist3d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
202 {
203 
204  double x, y, z;
205  DISTPTS3D thedl;
206  double initdistance = DBL_MAX;
207  LWGEOM *result;
208 
209  thedl.mode = mode;
210  thedl.distance = initdistance;
211  thedl.tolerance = 0;
212 
213  LWDEBUG(2, "lw_dist3d_distancepoint is called");
214 
215  /* Check if we really have 3D geometries
216  * If not, send it to 2D-calculations which will give the same result
217  * as an infinite z-value at one or two of the geometries
218  */
219  if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2))
220  {
221  lwnotice(
222  "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\"");
223 
224  if (!lwgeom_has_z(lw1) && !lwgeom_has_z(lw2))
225  return lw_dist2d_distancepoint(lw1, lw2, srid, mode);
226 
227  DISTPTS thedl2d;
228  thedl2d.mode = mode;
229  thedl2d.distance = initdistance;
230  thedl2d.tolerance = 0.0;
231  if (!lw_dist2d_comp(lw1, lw2, &thedl2d))
232  {
233  /* should never get here. all cases ought to be error handled earlier */
234  lwerror("Some unspecified error.");
235  return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
236  }
237 
238  LWGEOM *vertical_line;
239  if (!lwgeom_has_z(lw1))
240  {
241  x = thedl2d.p1.x;
242  y = thedl2d.p1.y;
243 
244  vertical_line = create_v_line(lw2, x, y, srid);
245  if (!lw_dist3d_recursive(vertical_line, lw2, &thedl))
246  {
247  /* should never get here. all cases ought to be error handled earlier */
248  lwfree(vertical_line);
249  lwerror("Some unspecified error.");
250  return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
251  }
252  lwfree(vertical_line);
253  }
254 
255  if (!lwgeom_has_z(lw2))
256  {
257  x = thedl2d.p2.x;
258  y = thedl2d.p2.y;
259 
260  vertical_line = create_v_line(lw1, x, y, srid);
261  if (!lw_dist3d_recursive(lw1, vertical_line, &thedl))
262  {
263  /* should never get here. all cases ought to be error handled earlier */
264  lwfree(vertical_line);
265  lwerror("Some unspecified error.");
266  result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
267  }
268  lwfree(vertical_line);
269  }
270  }
271  else
272  {
273  if (!lw_dist3d_recursive(lw1, lw2, &thedl))
274  {
275  /* should never get here. all cases ought to be error handled earlier */
276  lwerror("Some unspecified error.");
277  result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
278  }
279  }
280  if (thedl.distance == initdistance)
281  {
282  LWDEBUG(3, "didn't find geometries to measure between, returning null");
283  result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
284  }
285  else
286  {
287  x = thedl.p1.x;
288  y = thedl.p1.y;
289  z = thedl.p1.z;
290  result = (LWGEOM *)lwpoint_make3dz(srid, x, y, z);
291  }
292 
293  return result;
294 }
295 
299 double
300 lwgeom_maxdistance3d(const LWGEOM *lw1, const LWGEOM *lw2)
301 {
302  return lwgeom_maxdistance3d_tolerance(lw1, lw2, 0.0);
303 }
304 
309 double
310 lwgeom_maxdistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
311 {
312  if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2))
313  {
314  lwnotice(
315  "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\"");
316  return lwgeom_maxdistance2d_tolerance(lw1, lw2, tolerance);
317  }
318  DISTPTS3D thedl;
319  LWDEBUG(2, "lwgeom_maxdistance3d_tolerance is called");
320  thedl.mode = DIST_MAX;
321  thedl.distance = -1;
322  thedl.tolerance = tolerance;
323  if (lw_dist3d_recursive(lw1, lw2, &thedl))
324  return thedl.distance;
325 
326  /* should never get here. all cases ought to be error handled earlier */
327  lwerror("Some unspecified error.");
328  return -1;
329 }
330 
334 double
335 lwgeom_mindistance3d(const LWGEOM *lw1, const LWGEOM *lw2)
336 {
337  return lwgeom_mindistance3d_tolerance(lw1, lw2, 0.0);
338 }
339 
340 static inline int
341 gbox_contains_3d(const GBOX *g1, const GBOX *g2)
342 {
343  return (g2->xmin >= g1->xmin) && (g2->xmax <= g1->xmax) && (g2->ymin >= g1->ymin) && (g2->ymax <= g1->ymax) &&
344  (g2->zmin >= g1->zmin) && (g2->zmax <= g1->zmax);
345 }
346 
347 static inline int
349 {
350  const GBOX *b1;
351  const GBOX *b2;
352 
353  /* If solid isn't solid it can't contain anything */
354  if (!FLAGS_GET_SOLID(solid->flags))
355  return LW_FALSE;
356 
357  b1 = lwgeom_get_bbox(solid);
358  b2 = lwgeom_get_bbox(g);
359 
360  /* If box won't contain box, shape won't contain shape */
361  if (!gbox_contains_3d(b1, b2))
362  return LW_FALSE;
363  else /* Raycast upwards in Z */
364  {
365  POINT4D pt;
366  /* We'll skew copies if we're not lucky */
367  LWGEOM *solid_copy = lwgeom_clone_deep(solid);
368  LWGEOM *g_copy = lwgeom_clone_deep(g);
369 
370  while (LW_TRUE)
371  {
372  uint8_t is_boundary = LW_FALSE;
373  uint8_t is_inside = LW_FALSE;
374 
375  /* take first point */
376  if (!lwgeom_startpoint(g_copy, &pt))
377  return LW_FALSE;
378 
379  /* get part of solid that is upwards */
380  LWCOLLECTION *c = lwgeom_clip_to_ordinate_range(solid_copy, 'Z', pt.z, DBL_MAX, 0);
381 
382  for (uint32_t i = 0; i < c->ngeoms; i++)
383  {
384  if (c->geoms[i]->type == POLYGONTYPE)
385  {
386  /* 3D raycast along Z is 2D point in polygon */
387  int t = lwpoly_contains_point((LWPOLY *)c->geoms[i], (POINT2D *)&pt);
388 
389  if (t == LW_INSIDE)
390  is_inside = !is_inside;
391  else if (t == LW_BOUNDARY)
392  {
393  is_boundary = LW_TRUE;
394  break;
395  }
396  }
397  else if (c->geoms[i]->type == TRIANGLETYPE)
398  {
399  /* 3D raycast along Z is 2D point in polygon */
400  LWTRIANGLE *tri = (LWTRIANGLE *)c->geoms[i];
401  int t = ptarray_contains_point(tri->points, (POINT2D *)&pt);
402 
403  if (t == LW_INSIDE)
404  is_inside = !is_inside;
405  else if (t == LW_BOUNDARY)
406  {
407  is_boundary = LW_TRUE;
408  break;
409  }
410  }
411  }
412 
414  lwgeom_free(solid_copy);
415  lwgeom_free(g_copy);
416 
417  if (is_boundary)
418  {
419  /* randomly skew a bit and restart*/
420  double cx = lwrandom_uniform() - 0.5;
421  double cy = lwrandom_uniform() - 0.5;
422  AFFINE aff = {1, 0, cx, 0, 1, cy, 0, 0, 1, 0, 0, 0};
423 
424  solid_copy = lwgeom_clone_deep(solid);
425  lwgeom_affine(solid_copy, &aff);
426 
427  g_copy = lwgeom_clone_deep(g);
428  lwgeom_affine(g_copy, &aff);
429 
430  continue;
431  }
432  return is_inside;
433  }
434  }
435 }
436 
441 double
442 lwgeom_mindistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
443 {
444  assert(tolerance >= 0);
445  if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2))
446  {
447  lwnotice(
448  "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\"");
449 
450  return lwgeom_mindistance2d_tolerance(lw1, lw2, tolerance);
451  }
452  DISTPTS3D thedl;
453  thedl.mode = DIST_MIN;
454  thedl.distance = DBL_MAX;
455  thedl.tolerance = tolerance;
456 
457  if (lw_dist3d_recursive(lw1, lw2, &thedl))
458  {
459  if (thedl.distance <= tolerance)
460  return thedl.distance;
462  return 0;
463 
464  return thedl.distance;
465  }
466 
467  /* should never get here. all cases ought to be error handled earlier */
468  lwerror("Some unspecified error.");
469  return DBL_MAX;
470 }
471 
472 /*------------------------------------------------------------------------------------------------------------
473 End of Initializing functions
474 --------------------------------------------------------------------------------------------------------------*/
475 
476 /*------------------------------------------------------------------------------------------------------------
477 Preprocessing functions
478 Functions preparing geometries for distance-calculations
479 --------------------------------------------------------------------------------------------------------------*/
480 
484 int
485 lw_dist3d_recursive(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl)
486 {
487  int i, j;
488  int n1 = 1;
489  int n2 = 1;
490  LWGEOM *g1 = NULL;
491  LWGEOM *g2 = NULL;
492  LWCOLLECTION *c1 = NULL;
493  LWCOLLECTION *c2 = NULL;
494 
495  LWDEBUGF(2, "lw_dist3d_recursive is called with type1=%d, type2=%d", lwg1->type, lwg2->type);
496 
497  if (lwgeom_is_collection(lwg1))
498  {
499  LWDEBUG(3, "First geometry is collection");
500  c1 = lwgeom_as_lwcollection(lwg1);
501  n1 = c1->ngeoms;
502  }
503  if (lwgeom_is_collection(lwg2))
504  {
505  LWDEBUG(3, "Second geometry is collection");
506  c2 = lwgeom_as_lwcollection(lwg2);
507  n2 = c2->ngeoms;
508  }
509 
510  for (i = 0; i < n1; i++)
511  {
512  if (lwgeom_is_collection(lwg1))
513  g1 = c1->geoms[i];
514  else
515  g1 = (LWGEOM *)lwg1;
516 
517  if (lwgeom_is_empty(g1))
518  continue;
519 
520  if (lwgeom_is_collection(g1))
521  {
522  LWDEBUG(3, "Found collection inside first geometry collection, recursing");
523  if (!lw_dist3d_recursive(g1, lwg2, dl))
524  return LW_FALSE;
525  continue;
526  }
527  for (j = 0; j < n2; j++)
528  {
529  if (lwgeom_is_collection(lwg2))
530  g2 = c2->geoms[j];
531  else
532  g2 = (LWGEOM *)lwg2;
533 
534  if (lwgeom_is_empty(g2))
535  continue;
536 
537  if (lwgeom_is_collection(g2))
538  {
539  LWDEBUG(3, "Found collection inside second geometry collection, recursing");
540  if (!lw_dist3d_recursive(g1, g2, dl))
541  return LW_FALSE;
542  continue;
543  }
544 
545  /*If one of geometries is empty, return. True here only means continue searching. False would
546  * have stopped the process*/
547  if (lwgeom_is_empty(g1) || lwgeom_is_empty(g2))
548  return LW_TRUE;
549 
550  if (!lw_dist3d_distribute_bruteforce(g1, g2, dl))
551  return LW_FALSE;
552  if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
553  return LW_TRUE; /*just a check if the answer is already given*/
554  }
555  }
556  return LW_TRUE;
557 }
558 
563 int
565 {
566  int t1 = lwg1->type;
567  int t2 = lwg2->type;
568 
569  LWDEBUGF(2, "lw_dist3d_distribute_bruteforce is called with typ1=%d, type2=%d", lwg1->type, lwg2->type);
570 
571  if (t1 == POINTTYPE)
572  {
573  if (t2 == POINTTYPE)
574  {
575  dl->twisted = 1;
576  return lw_dist3d_point_point((LWPOINT *)lwg1, (LWPOINT *)lwg2, dl);
577  }
578  else if (t2 == LINETYPE)
579  {
580  dl->twisted = 1;
581  return lw_dist3d_point_line((LWPOINT *)lwg1, (LWLINE *)lwg2, dl);
582  }
583  else if (t2 == POLYGONTYPE)
584  {
585  dl->twisted = 1;
586  return lw_dist3d_point_poly((LWPOINT *)lwg1, (LWPOLY *)lwg2, dl);
587  }
588  else if (t2 == TRIANGLETYPE)
589  {
590  dl->twisted = 1;
591  return lw_dist3d_point_tri((LWPOINT *)lwg1, (LWTRIANGLE *)lwg2, dl);
592  }
593  else
594  {
595  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
596  return LW_FALSE;
597  }
598  }
599  else if (t1 == LINETYPE)
600  {
601  if (t2 == POINTTYPE)
602  {
603  dl->twisted = -1;
604  return lw_dist3d_point_line((LWPOINT *)lwg2, (LWLINE *)lwg1, dl);
605  }
606  else if (t2 == LINETYPE)
607  {
608  dl->twisted = 1;
609  return lw_dist3d_line_line((LWLINE *)lwg1, (LWLINE *)lwg2, dl);
610  }
611  else if (t2 == POLYGONTYPE)
612  {
613  dl->twisted = 1;
614  return lw_dist3d_line_poly((LWLINE *)lwg1, (LWPOLY *)lwg2, dl);
615  }
616  else if (t2 == TRIANGLETYPE)
617  {
618  dl->twisted = 1;
619  return lw_dist3d_line_tri((LWLINE *)lwg1, (LWTRIANGLE *)lwg2, dl);
620  }
621  else
622  {
623  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
624  return LW_FALSE;
625  }
626  }
627  else if (t1 == POLYGONTYPE)
628  {
629  if (t2 == POLYGONTYPE)
630  {
631  dl->twisted = 1;
632  return lw_dist3d_poly_poly((LWPOLY *)lwg1, (LWPOLY *)lwg2, dl);
633  }
634  else if (t2 == POINTTYPE)
635  {
636  dl->twisted = -1;
637  return lw_dist3d_point_poly((LWPOINT *)lwg2, (LWPOLY *)lwg1, dl);
638  }
639  else if (t2 == LINETYPE)
640  {
641  dl->twisted = -1;
642  return lw_dist3d_line_poly((LWLINE *)lwg2, (LWPOLY *)lwg1, dl);
643  }
644  else if (t2 == TRIANGLETYPE)
645  {
646  dl->twisted = 1;
647  return lw_dist3d_poly_tri((LWPOLY *)lwg1, (LWTRIANGLE *)lwg2, dl);
648  }
649  else
650  {
651  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
652  return LW_FALSE;
653  }
654  }
655  else if (t1 == TRIANGLETYPE)
656  {
657  if (t2 == POLYGONTYPE)
658  {
659  dl->twisted = -1;
660  return lw_dist3d_poly_tri((LWPOLY *)lwg2, (LWTRIANGLE *)lwg1, dl);
661  }
662  else if (t2 == POINTTYPE)
663  {
664  dl->twisted = -1;
665  return lw_dist3d_point_tri((LWPOINT *)lwg2, (LWTRIANGLE *)lwg1, dl);
666  }
667  else if (t2 == LINETYPE)
668  {
669  dl->twisted = -1;
670  return lw_dist3d_line_tri((LWLINE *)lwg2, (LWTRIANGLE *)lwg1, dl);
671  }
672  else if (t2 == TRIANGLETYPE)
673  {
674  dl->twisted = 1;
675  return lw_dist3d_tri_tri((LWTRIANGLE *)lwg1, (LWTRIANGLE *)lwg2, dl);
676  }
677  else
678  {
679  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
680  return LW_FALSE;
681  }
682  }
683 
684  else
685  {
686  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t1));
687  return LW_FALSE;
688  }
689 }
690 
691 /*------------------------------------------------------------------------------------------------------------
692 End of Preprocessing functions
693 --------------------------------------------------------------------------------------------------------------*/
694 
695 /*------------------------------------------------------------------------------------------------------------
696 Brute force functions
697 So far the only way to do 3D-calculations
698 --------------------------------------------------------------------------------------------------------------*/
699 
704 int
706 {
707  POINT3DZ p1;
708  POINT3DZ p2;
709  LWDEBUG(2, "lw_dist3d_point_point is called");
710 
711  getPoint3dz_p(point1->point, 0, &p1);
712  getPoint3dz_p(point2->point, 0, &p2);
713 
714  return lw_dist3d_pt_pt(&p1, &p2, dl);
715 }
720 int
722 {
723  POINT3DZ p;
724  POINTARRAY *pa = line->points;
725  LWDEBUG(2, "lw_dist3d_point_line is called");
726 
727  getPoint3dz_p(point->point, 0, &p);
728  return lw_dist3d_pt_ptarray(&p, pa, dl);
729 }
730 
743 int
745 {
746  POINT3DZ p, projp; /*projp is "point projected on plane"*/
747  PLANE3D plane;
748  LWDEBUG(2, "lw_dist3d_point_poly is called");
749  getPoint3dz_p(point->point, 0, &p);
750 
751  /* If we are looking for max distance, longestline or dfullywithin */
752  if (dl->mode == DIST_MAX)
753  return lw_dist3d_pt_ptarray(&p, poly->rings[0], dl);
754 
755  /* Find the plane of the polygon, the "holes" have to be on the same plane. so we only care about the boudary */
756  if (!define_plane(poly->rings[0], &plane))
757  {
758  /* Polygon does not define a plane: Return distance point -> line */
759  return lw_dist3d_pt_ptarray(&p, poly->rings[0], dl);
760  }
761 
762  /* Get our point projected on the plane of the polygon */
763  project_point_on_plane(&p, &plane, &projp);
764 
765  return lw_dist3d_pt_poly(&p, poly, &plane, &projp, dl);
766 }
767 
768 /* point to triangle calculation */
769 int
771 {
772  POINT3DZ p, projp; /*projp is "point projected on plane"*/
773  PLANE3D plane;
774  getPoint3dz_p(point->point, 0, &p);
775 
776  /* If we are looking for max distance, longestline or dfullywithin */
777  if (dl->mode == DIST_MAX)
778  return lw_dist3d_pt_ptarray(&p, tri->points, dl);
779 
780  /* If triangle does not define a plane, treat it as a line */
781  if (!define_plane(tri->points, &plane))
782  return lw_dist3d_pt_ptarray(&p, tri->points, dl);
783 
784  /* Get our point projected on the plane of triangle */
785  project_point_on_plane(&p, &plane, &projp);
786 
787  return lw_dist3d_pt_tri(&p, tri, &plane, &projp, dl);
788 }
789 
791 int
793 {
794  POINTARRAY *pa1 = line1->points;
795  POINTARRAY *pa2 = line2->points;
796  LWDEBUG(2, "lw_dist3d_line_line is called");
797 
798  return lw_dist3d_ptarray_ptarray(pa1, pa2, dl);
799 }
800 
802 int
804 {
805  PLANE3D plane;
806  LWDEBUG(2, "lw_dist3d_line_poly is called");
807 
808  if (dl->mode == DIST_MAX)
809  return lw_dist3d_ptarray_ptarray(line->points, poly->rings[0], dl);
810 
811  /* if polygon does not define a plane: Return distance line to line */
812  if (!define_plane(poly->rings[0], &plane))
813  return lw_dist3d_ptarray_ptarray(line->points, poly->rings[0], dl);
814 
815  return lw_dist3d_ptarray_poly(line->points, poly, &plane, dl);
816 }
817 
819 int
821 {
822  PLANE3D plane;
823 
824  if (dl->mode == DIST_MAX)
825  return lw_dist3d_ptarray_ptarray(line->points, tri->points, dl);
826 
827  /* if triangle does not define a plane: Return distance line to line */
828  if (!define_plane(tri->points, &plane))
829  return lw_dist3d_ptarray_ptarray(line->points, tri->points, dl);
830 
831  return lw_dist3d_ptarray_tri(line->points, tri, &plane, dl);
832 }
833 
835 int
837 {
838  PLANE3D plane1, plane2;
839  int planedef1, planedef2;
840  LWDEBUG(2, "lw_dist3d_poly_poly is called");
841  if (dl->mode == DIST_MAX)
842  return lw_dist3d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl);
843 
844  planedef1 = define_plane(poly1->rings[0], &plane1);
845  planedef2 = define_plane(poly2->rings[0], &plane2);
846 
847  if (!planedef1 || !planedef2)
848  {
849  /* Neither polygon define a plane: Return distance line to line */
850  if (!planedef1 && !planedef2)
851  return lw_dist3d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl);
852 
853  /* Only poly2 defines a plane: Return distance from line (poly1) to poly2 */
854  else if (!planedef1)
855  return lw_dist3d_ptarray_poly(poly1->rings[0], poly2, &plane2, dl);
856 
857  /* Only poly1 defines a plane: Return distance from line (poly2) to poly1 */
858  else
859  return lw_dist3d_ptarray_poly(poly2->rings[0], poly1, &plane1, dl);
860  }
861 
862  /* What we do here is to compare the boundary of one polygon with the other polygon
863  and then take the second boundary comparing with the first polygon */
864  dl->twisted = 1;
865  if (!lw_dist3d_ptarray_poly(poly1->rings[0], poly2, &plane2, dl))
866  return LW_FALSE;
867  if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/
868  return LW_TRUE;
869 
870  dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the
871  right order of points in shortest line. */
872  return lw_dist3d_ptarray_poly(poly2->rings[0], poly1, &plane1, dl);
873 }
874 
876 int
878 {
879  PLANE3D plane1, plane2;
880  int planedef1, planedef2;
881 
882  if (dl->mode == DIST_MAX)
883  return lw_dist3d_ptarray_ptarray(poly->rings[0], tri->points, dl);
884 
885  planedef1 = define_plane(poly->rings[0], &plane1);
886  planedef2 = define_plane(tri->points, &plane2);
887 
888  if (!planedef1 || !planedef2)
889  {
890  /* Neither define a plane: Return distance line to line */
891  if (!planedef1 && !planedef2)
892  return lw_dist3d_ptarray_ptarray(poly->rings[0], tri->points, dl);
893 
894  /* Only tri defines a plane: Return distance from line (poly) to tri */
895  else if (!planedef1)
896  return lw_dist3d_ptarray_tri(poly->rings[0], tri, &plane2, dl);
897 
898  /* Only poly defines a plane: Return distance from line (tri) to poly */
899  else
900  return lw_dist3d_ptarray_poly(tri->points, poly, &plane1, dl);
901  }
902 
903  /* What we do here is to compare the boundary of one polygon with the other polygon
904  and then take the second boundary comparing with the first polygon */
905  dl->twisted = 1;
906  if (!lw_dist3d_ptarray_tri(poly->rings[0], tri, &plane2, dl))
907  return LW_FALSE;
908  if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/
909  return LW_TRUE;
910 
911  dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the
912  right order of points in shortest line. */
913  return lw_dist3d_ptarray_poly(tri->points, poly, &plane1, dl);
914 }
915 
917 int
919 {
920  PLANE3D plane1, plane2;
921  int planedef1, planedef2;
922 
923  if (dl->mode == DIST_MAX)
924  return lw_dist3d_ptarray_ptarray(tri1->points, tri2->points, dl);
925 
926  planedef1 = define_plane(tri1->points, &plane1);
927  planedef2 = define_plane(tri2->points, &plane2);
928 
929  if (!planedef1 || !planedef2)
930  {
931  /* Neither define a plane: Return distance line to line */
932  if (!planedef1 && !planedef2)
933  return lw_dist3d_ptarray_ptarray(tri1->points, tri2->points, dl);
934 
935  /* Only tri defines a plane: Return distance from line (tri1) to tri2 */
936  else if (!planedef1)
937  return lw_dist3d_ptarray_tri(tri1->points, tri2, &plane2, dl);
938 
939  /* Only poly defines a plane: Return distance from line (tri2) to tri1 */
940  else
941  return lw_dist3d_ptarray_tri(tri2->points, tri1, &plane1, dl);
942  }
943 
944  /* What we do here is to compare the boundary of one polygon with the other polygon
945  and then take the second boundary comparing with the first polygon */
946  dl->twisted = 1;
947  if (!lw_dist3d_ptarray_tri(tri1->points, tri2, &plane2, dl))
948  return LW_FALSE;
949  if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/
950  return LW_TRUE;
951 
952  dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the
953  right order of points in shortest line. */
954  return lw_dist3d_ptarray_tri(tri2->points, tri1, &plane1, dl);
955 }
956 
961 int
963 {
964  uint32_t t;
965  POINT3DZ start, end;
966  int twist = dl->twisted;
967  if (!pa)
968  return LW_FALSE;
969 
970  getPoint3dz_p(pa, 0, &start);
971 
972  for (t = 1; t < pa->npoints; t++)
973  {
974  dl->twisted = twist;
975  getPoint3dz_p(pa, t, &end);
976  if (!lw_dist3d_pt_seg(p, &start, &end, dl))
977  return LW_FALSE;
978 
979  if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
980  return LW_TRUE; /*just a check if the answer is already given*/
981  start = end;
982  }
983 
984  return LW_TRUE;
985 }
986 
991 int
993 {
994  POINT3DZ c;
995  double r;
996  /*if start==end, then use pt distance */
997  if ((A->x == B->x) && (A->y == B->y) && (A->z == B->z))
998  {
999  return lw_dist3d_pt_pt(p, A, dl);
1000  }
1001 
1002  r = ((p->x - A->x) * (B->x - A->x) + (p->y - A->y) * (B->y - A->y) + (p->z - A->z) * (B->z - A->z)) /
1003  ((B->x - A->x) * (B->x - A->x) + (B->y - A->y) * (B->y - A->y) + (B->z - A->z) * (B->z - A->z));
1004 
1005  /*This is for finding the 3Dmaxdistance.
1006  the maxdistance have to be between two vertexes,
1007  compared to mindistance which can be between
1008  two vertexes vertex.*/
1009  if (dl->mode == DIST_MAX)
1010  {
1011  if (r >= 0.5)
1012  return lw_dist3d_pt_pt(p, A, dl);
1013  if (r < 0.5)
1014  return lw_dist3d_pt_pt(p, B, dl);
1015  }
1016 
1017  if (r <= 0) /*If the first vertex A is closest to the point p*/
1018  return lw_dist3d_pt_pt(p, A, dl);
1019  if (r >= 1) /*If the second vertex B is closest to the point p*/
1020  return lw_dist3d_pt_pt(p, B, dl);
1021 
1022  /*else if the point p is closer to some point between a and b
1023  then we find that point and send it to lw_dist3d_pt_pt*/
1024  c.x = A->x + r * (B->x - A->x);
1025  c.y = A->y + r * (B->y - A->y);
1026  c.z = A->z + r * (B->z - A->z);
1027 
1028  return lw_dist3d_pt_pt(p, &c, dl);
1029 }
1030 
1031 double
1032 distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
1033 {
1034  double dx = p2->x - p1->x;
1035  double dy = p2->y - p1->y;
1036  double dz = p2->z - p1->z;
1037  return sqrt(dx * dx + dy * dy + dz * dz);
1038 }
1039 
1047 int
1049 {
1050  double dx = thep2->x - thep1->x;
1051  double dy = thep2->y - thep1->y;
1052  double dz = thep2->z - thep1->z;
1053  double dist = sqrt(dx * dx + dy * dy + dz * dz);
1054  LWDEBUGF(2,
1055  "lw_dist3d_pt_pt called (with points: p1.x=%f, p1.y=%f,p1.z=%f,p2.x=%f, p2.y=%f,p2.z=%f)",
1056  thep1->x,
1057  thep1->y,
1058  thep1->z,
1059  thep2->x,
1060  thep2->y,
1061  thep2->z);
1062 
1063  if (((dl->distance - dist) * (dl->mode)) >
1064  0) /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/
1065  {
1066  dl->distance = dist;
1067 
1068  if (dl->twisted > 0) /*To get the points in right order. twisted is updated between 1 and (-1) every
1069  time the order is changed earlier in the chain*/
1070  {
1071  dl->p1 = *thep1;
1072  dl->p2 = *thep2;
1073  }
1074  else
1075  {
1076  dl->p1 = *thep2;
1077  dl->p2 = *thep1;
1078  }
1079  }
1080  return LW_TRUE;
1081 }
1082 
1087 int
1089 {
1090  uint32_t t, u;
1091  POINT3DZ start, end;
1092  POINT3DZ start2, end2;
1093  int twist = dl->twisted;
1094  LWDEBUGF(2, "lw_dist3d_ptarray_ptarray called (points: %d-%d)", l1->npoints, l2->npoints);
1095 
1096  if (dl->mode == DIST_MAX) /*If we are searching for maxdistance we go straight to point-point calculation since
1097  the maxdistance have to be between two vertexes*/
1098  {
1099  for (t = 0; t < l1->npoints; t++) /*for each segment in L1 */
1100  {
1101  getPoint3dz_p(l1, t, &start);
1102  for (u = 0; u < l2->npoints; u++) /*for each segment in L2 */
1103  {
1104  getPoint3dz_p(l2, u, &start2);
1105  lw_dist3d_pt_pt(&start, &start2, dl);
1106  }
1107  }
1108  }
1109  else
1110  {
1111  getPoint3dz_p(l1, 0, &start);
1112  for (t = 1; t < l1->npoints; t++) /*for each segment in L1 */
1113  {
1114  getPoint3dz_p(l1, t, &end);
1115  getPoint3dz_p(l2, 0, &start2);
1116  for (u = 1; u < l2->npoints; u++) /*for each segment in L2 */
1117  {
1118  getPoint3dz_p(l2, u, &end2);
1119  dl->twisted = twist;
1120  lw_dist3d_seg_seg(&start, &end, &start2, &end2, dl);
1121  LWDEBUGF(
1122  4, "mindist_ptarray_ptarray; seg %i * seg %i, dist = %g\n", t, u, dl->distance);
1123  LWDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f", t, u, dl->distance, dl->tolerance);
1124  if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1125  return LW_TRUE; /*just a check if the answer is already given*/
1126  start2 = end2;
1127  }
1128  start = end;
1129  }
1130  }
1131  return LW_TRUE;
1132 }
1133 
1138 int
1140 {
1141  VECTOR3D v1, v2, vl;
1142  double s1k, s2k; /*two variables representing where on Line 1 (s1k) and where on Line 2 (s2k) a connecting line
1143  between the two lines is perpendicular to both lines*/
1144  POINT3DZ p1, p2;
1145  double a, b, c, d, e, D;
1146 
1147  /*s1p1 and s1p2 are the same point */
1148  if ((s1p1->x == s1p2->x) && (s1p1->y == s1p2->y) && (s1p1->z == s1p2->z))
1149  {
1150  return lw_dist3d_pt_seg(s1p1, s2p1, s2p2, dl);
1151  }
1152  /*s2p1 and s2p2 are the same point */
1153  if ((s2p1->x == s2p2->x) && (s2p1->y == s2p2->y) && (s2p1->z == s2p2->z))
1154  {
1155  dl->twisted = ((dl->twisted) * (-1));
1156  return lw_dist3d_pt_seg(s2p1, s1p1, s1p2, dl);
1157  }
1158 
1159  /*
1160  Here we use algorithm from softsurfer.com
1161  that can be found here
1162  http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
1163  */
1164 
1165  if (!get_3dvector_from_points(s1p1, s1p2, &v1))
1166  return LW_FALSE;
1167 
1168  if (!get_3dvector_from_points(s2p1, s2p2, &v2))
1169  return LW_FALSE;
1170 
1171  if (!get_3dvector_from_points(s2p1, s1p1, &vl))
1172  return LW_FALSE;
1173 
1174  a = DOT(v1, v1);
1175  b = DOT(v1, v2);
1176  c = DOT(v2, v2);
1177  d = DOT(v1, vl);
1178  e = DOT(v2, vl);
1179  D = a * c - b * b;
1180 
1181  if (D < 0.000000001)
1182  { /* the lines are almost parallel*/
1183  s1k =
1184  0.0; /*If the lines are parallel we try by using the startpoint of first segment. If that gives a
1185  projected point on the second line outside segment 2 it wil be found that s2k is >1 or <0.*/
1186  if (b > c) /* use the largest denominator*/
1187  s2k = d / b;
1188  else
1189  s2k = e / c;
1190  }
1191  else
1192  {
1193  s1k = (b * e - c * d) / D;
1194  s2k = (a * e - b * d) / D;
1195  }
1196 
1197  /* Now we check if the projected closest point on the infinite lines is outside our segments. If so the
1198  * combinations with start and end points will be tested*/
1199 
1200  if (s1k <= 0.0 || s1k >= 1.0 || s2k <= 0.0 || s2k >= 1.0)
1201  {
1202  if (s1k <= 0.0)
1203  {
1204  if (!lw_dist3d_pt_seg(s1p1, s2p1, s2p2, dl))
1205  return LW_FALSE;
1206  }
1207  if (s1k >= 1.0)
1208  {
1209  if (!lw_dist3d_pt_seg(s1p2, s2p1, s2p2, dl))
1210  return LW_FALSE;
1211  }
1212  if (s2k <= 0.0)
1213  {
1214  dl->twisted = ((dl->twisted) * (-1));
1215  if (!lw_dist3d_pt_seg(s2p1, s1p1, s1p2, dl))
1216  return LW_FALSE;
1217  }
1218  if (s2k >= 1.0)
1219  {
1220  dl->twisted = ((dl->twisted) * (-1));
1221  if (!lw_dist3d_pt_seg(s2p2, s1p1, s1p2, dl))
1222  return LW_FALSE;
1223  }
1224  }
1225  else
1226  { /*Find the closest point on the edges of both segments*/
1227  p1.x = s1p1->x + s1k * (s1p2->x - s1p1->x);
1228  p1.y = s1p1->y + s1k * (s1p2->y - s1p1->y);
1229  p1.z = s1p1->z + s1k * (s1p2->z - s1p1->z);
1230 
1231  p2.x = s2p1->x + s2k * (s2p2->x - s2p1->x);
1232  p2.y = s2p1->y + s2k * (s2p2->y - s2p1->y);
1233  p2.z = s2p1->z + s2k * (s2p2->z - s2p1->z);
1234 
1235  if (!lw_dist3d_pt_pt(&p1, &p2, dl)) /* Send the closest points to point-point calculation*/
1236  {
1237  return LW_FALSE;
1238  }
1239  }
1240  return LW_TRUE;
1241 }
1242 
1250 int
1252 {
1253  uint32_t i;
1254 
1255  if (pt_in_ring_3d(projp, poly->rings[0], plane))
1256  {
1257  for (i = 1; i < poly->nrings; i++)
1258  {
1259  /* Inside a hole. Distance = pt -> ring */
1260  if (pt_in_ring_3d(projp, poly->rings[i], plane))
1261  return lw_dist3d_pt_ptarray(p, poly->rings[i], dl);
1262  }
1263 
1264  /* if the projected point is inside the polygon the shortest distance is between that point and the
1265  * input point */
1266  return lw_dist3d_pt_pt(p, projp, dl);
1267  }
1268  else
1269  /* if the projected point is outside the polygon we search for the closest distance against the boundary
1270  * instead */
1271  return lw_dist3d_pt_ptarray(p, poly->rings[0], dl);
1272 
1273  return LW_TRUE;
1274 }
1275 
1276 int
1278 {
1279  if (pt_in_ring_3d(projp, tri->points, plane))
1280  /* if the projected point is inside the polygon the shortest distance is between that point and the
1281  * input point */
1282  return lw_dist3d_pt_pt(p, projp, dl);
1283  else
1284  /* if the projected point is outside the polygon we search for the closest distance against the boundary
1285  * instead */
1286  return lw_dist3d_pt_ptarray(p, tri->points, dl);
1287 
1288  return LW_TRUE;
1289 }
1290 
1292 int
1294 {
1295  uint32_t i, j, k;
1296  double f, s1, s2;
1297  VECTOR3D projp1_projp2;
1298  POINT3DZ p1, p2, projp1, projp2, intersectionp;
1299 
1300  getPoint3dz_p(pa, 0, &p1);
1301 
1302  /* the sign of s1 tells us on which side of the plane the point is. */
1303  s1 = project_point_on_plane(&p1, plane, &projp1);
1304  lw_dist3d_pt_poly(&p1, poly, plane, &projp1, dl);
1305  if ((s1 == 0.0) && (dl->distance < dl->tolerance))
1306  return LW_TRUE;
1307 
1308  for (i = 1; i < pa->npoints; i++)
1309  {
1310  int intersects;
1311  getPoint3dz_p(pa, i, &p2);
1312  s2 = project_point_on_plane(&p2, plane, &projp2);
1313  lw_dist3d_pt_poly(&p2, poly, plane, &projp2, dl);
1314  if ((s2 == 0.0) && (dl->distance < dl->tolerance))
1315  return LW_TRUE;
1316 
1317  /* If s1 and s2 has different signs that means they are on different sides of the plane of the polygon.
1318  * That means that the edge between the points crosses the plane and might intersect with the polygon */
1319  if ((s1 * s2) < 0)
1320  {
1321  /* The size of s1 and s2 is the distance from the point to the plane. */
1322  f = fabs(s1) / (fabs(s1) + fabs(s2));
1323  get_3dvector_from_points(&projp1, &projp2, &projp1_projp2);
1324 
1325  /* Get the point where the line segment crosses the plane */
1326  intersectionp.x = projp1.x + f * projp1_projp2.x;
1327  intersectionp.y = projp1.y + f * projp1_projp2.y;
1328  intersectionp.z = projp1.z + f * projp1_projp2.z;
1329 
1330  /* We set intersects to true until the opposite is proved */
1331  intersects = LW_TRUE;
1332 
1333  if (pt_in_ring_3d(&intersectionp, poly->rings[0], plane)) /*Inside outer ring*/
1334  {
1335  for (k = 1; k < poly->nrings; k++)
1336  {
1337  /* Inside a hole, so no intersection with the polygon*/
1338  if (pt_in_ring_3d(&intersectionp, poly->rings[k], plane))
1339  {
1340  intersects = LW_FALSE;
1341  break;
1342  }
1343  }
1344  if (intersects)
1345  {
1346  dl->distance = 0.0;
1347  dl->p1.x = intersectionp.x;
1348  dl->p1.y = intersectionp.y;
1349  dl->p1.z = intersectionp.z;
1350 
1351  dl->p2.x = intersectionp.x;
1352  dl->p2.y = intersectionp.y;
1353  dl->p2.z = intersectionp.z;
1354  return LW_TRUE;
1355  }
1356  }
1357  }
1358 
1359  projp1 = projp2;
1360  s1 = s2;
1361  p1 = p2;
1362  }
1363 
1364  /* check our pointarray against boundary and inner boundaries of the polygon */
1365  for (j = 0; j < poly->nrings; j++)
1366  lw_dist3d_ptarray_ptarray(pa, poly->rings[j], dl);
1367 
1368  return LW_TRUE;
1369 }
1370 
1372 int
1374 {
1375  uint32_t i;
1376  double f, s1, s2;
1377  VECTOR3D projp1_projp2;
1378  POINT3DZ p1, p2, projp1, projp2, intersectionp;
1379 
1380  getPoint3dz_p(pa, 0, &p1);
1381 
1382  /* the sign of s1 tells us on which side of the plane the point is. */
1383  s1 = project_point_on_plane(&p1, plane, &projp1);
1384  lw_dist3d_pt_tri(&p1, tri, plane, &projp1, dl);
1385  if ((s1 == 0.0) && (dl->distance < dl->tolerance))
1386  return LW_TRUE;
1387 
1388  for (i = 1; i < pa->npoints; i++)
1389  {
1390  int intersects;
1391  getPoint3dz_p(pa, i, &p2);
1392  s2 = project_point_on_plane(&p2, plane, &projp2);
1393  lw_dist3d_pt_tri(&p2, tri, plane, &projp2, dl);
1394  if ((s2 == 0.0) && (dl->distance < dl->tolerance))
1395  return LW_TRUE;
1396 
1397  /* If s1 and s2 has different signs that means they are on different sides of the plane of the triangle.
1398  * That means that the edge between the points crosses the plane and might intersect with the triangle
1399  */
1400  if ((s1 * s2) < 0)
1401  {
1402  /* The size of s1 and s2 is the distance from the point to the plane. */
1403  f = fabs(s1) / (fabs(s1) + fabs(s2));
1404  get_3dvector_from_points(&projp1, &projp2, &projp1_projp2);
1405 
1406  /* Get the point where the line segment crosses the plane */
1407  intersectionp.x = projp1.x + f * projp1_projp2.x;
1408  intersectionp.y = projp1.y + f * projp1_projp2.y;
1409  intersectionp.z = projp1.z + f * projp1_projp2.z;
1410 
1411  /* We set intersects to true until the opposite is proved */
1412  intersects = LW_TRUE;
1413 
1414  if (pt_in_ring_3d(&intersectionp, tri->points, plane)) /*Inside outer ring*/
1415  {
1416  if (intersects)
1417  {
1418  dl->distance = 0.0;
1419  dl->p1.x = intersectionp.x;
1420  dl->p1.y = intersectionp.y;
1421  dl->p1.z = intersectionp.z;
1422 
1423  dl->p2.x = intersectionp.x;
1424  dl->p2.y = intersectionp.y;
1425  dl->p2.z = intersectionp.z;
1426  return LW_TRUE;
1427  }
1428  }
1429  }
1430 
1431  projp1 = projp2;
1432  s1 = s2;
1433  p1 = p2;
1434  }
1435 
1436  /* check our pointarray against triangle contour */
1437  lw_dist3d_ptarray_ptarray(pa, tri->points, dl);
1438  return LW_TRUE;
1439 }
1440 
1441 /* Here we define the plane of a polygon (boundary pointarray of a polygon)
1442  * the plane is stored as a point in plane (plane.pop) and a normal vector (plane.pv)
1443  *
1444  * We are calculating the average point and using it to break the polygon into
1445  * POL_BREAKS (3) parts. Then we calculate the normal of those 3 vectors and
1446  * use its average to define the normal of the plane.This is done to minimize
1447  * the error introduced by FP precision
1448  * We use [3] breaks to contemplate the special case of 3d triangles
1449  */
1450 int
1452 {
1453  const uint32_t POL_BREAKS = 3;
1454 
1455  assert(pa);
1456  assert(pa->npoints > 3);
1457  if (!pa)
1458  return LW_FALSE;
1459 
1460  uint32_t unique_points = pa->npoints - 1;
1461  uint32_t i;
1462 
1463  if (pa->npoints < 3)
1464  return LW_FALSE;
1465 
1466  /* Calculate the average point */
1467  pl->pop.x = 0.0;
1468  pl->pop.y = 0.0;
1469  pl->pop.z = 0.0;
1470  for (i = 0; i < unique_points; i++)
1471  {
1472  POINT3DZ p;
1473  getPoint3dz_p(pa, i, &p);
1474  pl->pop.x += p.x;
1475  pl->pop.y += p.y;
1476  pl->pop.z += p.z;
1477  }
1478 
1479  pl->pop.x /= unique_points;
1480  pl->pop.y /= unique_points;
1481  pl->pop.z /= unique_points;
1482 
1483  pl->pv.x = 0.0;
1484  pl->pv.y = 0.0;
1485  pl->pv.z = 0.0;
1486  for (i = 0; i < POL_BREAKS; i++)
1487  {
1488  POINT3DZ point1, point2;
1489  VECTOR3D v1, v2, vp;
1490  uint32_t n1, n2;
1491  n1 = i * unique_points / POL_BREAKS;
1492  n2 = n1 + unique_points / POL_BREAKS; /* May overflow back to the first / last point */
1493 
1494  if (n1 == n2)
1495  continue;
1496 
1497  getPoint3dz_p(pa, n1, &point1);
1498  if (!get_3dvector_from_points(&pl->pop, &point1, &v1))
1499  continue;
1500 
1501  getPoint3dz_p(pa, n2, &point2);
1502  if (!get_3dvector_from_points(&pl->pop, &point2, &v2))
1503  continue;
1504 
1505  if (get_3dcross_product(&v1, &v2, &vp))
1506  {
1507  /* If the cross product isn't zero these 3 points aren't collinear
1508  * We divide by its lengthsq to normalize the additions */
1509  double vl = vp.x * vp.x + vp.y * vp.y + vp.z * vp.z;
1510  pl->pv.x += vp.x / vl;
1511  pl->pv.y += vp.y / vl;
1512  pl->pv.z += vp.z / vl;
1513  }
1514  }
1515 
1516  return (!FP_IS_ZERO(pl->pv.x) || !FP_IS_ZERO(pl->pv.y) || !FP_IS_ZERO(pl->pv.z));
1517 }
1518 
1523 double
1525 {
1526  /*In our plane definition we have a point on the plane and a normal vector (pl.pv), perpendicular to the plane
1527  this vector will be parallel to the line between our inputted point above the plane and the point we are
1528  searching for on the plane. So, we already have a direction from p to find p0, but we don't know the distance.
1529  */
1530 
1531  VECTOR3D v1;
1532  double f;
1533 
1534  if (!get_3dvector_from_points(&(pl->pop), p, &v1))
1535  return 0.0;
1536 
1537  f = DOT(pl->pv, v1);
1538  if (FP_IS_ZERO(f))
1539  {
1540  /* Point is in the plane */
1541  *p0 = *p;
1542  return 0;
1543  }
1544 
1545  f = -f / DOT(pl->pv, pl->pv);
1546 
1547  p0->x = p->x + pl->pv.x * f;
1548  p0->y = p->y + pl->pv.y * f;
1549  p0->z = p->z + pl->pv.z * f;
1550 
1551  return f;
1552 }
1553 
1566 int
1567 pt_in_ring_3d(const POINT3DZ *p, const POINTARRAY *ring, PLANE3D *plane)
1568 {
1569 
1570  uint32_t cn = 0; /* the crossing number counter */
1571  uint32_t i;
1572  POINT3DZ v1, v2;
1573 
1574  POINT3DZ first, last;
1575 
1576  getPoint3dz_p(ring, 0, &first);
1577  getPoint3dz_p(ring, ring->npoints - 1, &last);
1578  if (memcmp(&first, &last, sizeof(POINT3DZ)))
1579  {
1580  lwerror("pt_in_ring_3d: V[n] != V[0] (%g %g %g!= %g %g %g)",
1581  first.x,
1582  first.y,
1583  first.z,
1584  last.x,
1585  last.y,
1586  last.z);
1587  return LW_FALSE;
1588  }
1589 
1590  LWDEBUGF(2, "pt_in_ring_3d called with point: %g %g %g", p->x, p->y, p->z);
1591  /* printPA(ring); */
1592 
1593  /* loop through all edges of the polygon */
1594  getPoint3dz_p(ring, 0, &v1);
1595 
1596  if (fabs(plane->pv.z) >= fabs(plane->pv.x) &&
1597  fabs(plane->pv.z) >= fabs(plane->pv.y)) /*If the z vector of the normal vector to the plane is larger than x
1598  and y vector we project the ring to the xy-plane*/
1599  {
1600  for (i = 0; i < ring->npoints - 1; i++)
1601  {
1602  double vt;
1603  getPoint3dz_p(ring, i + 1, &v2);
1604 
1605  /* edge from vertex i to vertex i+1 */
1606  if (
1607  /* an upward crossing */
1608  ((v1.y <= p->y) && (v2.y > p->y))
1609  /* a downward crossing */
1610  || ((v1.y > p->y) && (v2.y <= p->y)))
1611  {
1612 
1613  vt = (double)(p->y - v1.y) / (v2.y - v1.y);
1614 
1615  /* P.x <intersect */
1616  if (p->x < v1.x + vt * (v2.x - v1.x))
1617  {
1618  /* a valid crossing of y=p.y right of p.x */
1619  ++cn;
1620  }
1621  }
1622  v1 = v2;
1623  }
1624  }
1625  else if (fabs(plane->pv.y) >= fabs(plane->pv.x) &&
1626  fabs(plane->pv.y) >= fabs(plane->pv.z)) /*If the y vector of the normal vector to the plane is larger
1627  than x and z vector we project the ring to the xz-plane*/
1628  {
1629  for (i = 0; i < ring->npoints - 1; i++)
1630  {
1631  double vt;
1632  getPoint3dz_p(ring, i + 1, &v2);
1633 
1634  /* edge from vertex i to vertex i+1 */
1635  if (
1636  /* an upward crossing */
1637  ((v1.z <= p->z) && (v2.z > p->z))
1638  /* a downward crossing */
1639  || ((v1.z > p->z) && (v2.z <= p->z)))
1640  {
1641 
1642  vt = (double)(p->z - v1.z) / (v2.z - v1.z);
1643 
1644  /* P.x <intersect */
1645  if (p->x < v1.x + vt * (v2.x - v1.x))
1646  {
1647  /* a valid crossing of y=p.y right of p.x */
1648  ++cn;
1649  }
1650  }
1651  v1 = v2;
1652  }
1653  }
1654  else /*Hopefully we only have the cases where x part of the normal vector is largest left*/
1655  {
1656  for (i = 0; i < ring->npoints - 1; i++)
1657  {
1658  double vt;
1659  getPoint3dz_p(ring, i + 1, &v2);
1660 
1661  /* edge from vertex i to vertex i+1 */
1662  if (
1663  /* an upward crossing */
1664  ((v1.z <= p->z) && (v2.z > p->z))
1665  /* a downward crossing */
1666  || ((v1.z > p->z) && (v2.z <= p->z)))
1667  {
1668 
1669  vt = (double)(p->z - v1.z) / (v2.z - v1.z);
1670 
1671  /* P.x <intersect */
1672  if (p->y < v1.y + vt * (v2.y - v1.y))
1673  {
1674  /* a valid crossing of y=p.y right of p.x */
1675  ++cn;
1676  }
1677  }
1678  v1 = v2;
1679  }
1680  }
1681  LWDEBUGF(3, "pt_in_ring_3d returning %d", cn & 1);
1682 
1683  return (cn & 1); /* 0 if even (out), and 1 if odd (in) */
1684 }
1685 
1686 /*------------------------------------------------------------------------------------------------------------
1687 End of Brute force functions
1688 --------------------------------------------------------------------------------------------------------------*/
char * r
Definition: cu_in_wkt.c:24
#define LW_FALSE
Definition: liblwgeom.h:108
#define COLLECTIONTYPE
Definition: liblwgeom.h:122
int lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
Definition: lwgeom.c:2113
LWLINE * lwline_from_ptarray(int32_t srid, uint32_t npoints, LWPOINT **points)
Definition: lwline.c:228
#define LW_FAILURE
Definition: liblwgeom.h:110
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1138
#define LINETYPE
Definition: liblwgeom.h:117
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:511
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:916
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:116
int getPoint3dz_p(const POINTARRAY *pa, uint32_t n, POINT3DZ *point)
Definition: lwgeom_api.c:215
void lwfree(void *mem)
Definition: lwutil.c:242
double lwgeom_mindistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
Function handling min distance calculations and dwithin calculations.
Definition: measures.c:207
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition: lwgeom.c:1079
#define POLYGONTYPE
Definition: liblwgeom.h:118
LWPOINT * lwpoint_make3dz(int32_t srid, double x, double y, double z)
Definition: lwpoint.c:173
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition: lwgeom.c:1975
LWCOLLECTION * lwgeom_clip_to_ordinate_range(const LWGEOM *lwin, char ordinate, double from, double to, double offset)
Given a geometry clip based on the from/to range of one of its ordinates (x, y, z,...
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
Definition: lwcollection.c:92
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:357
#define FLAGS_GET_SOLID(flags)
Definition: liblwgeom.h:184
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:725
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
Calculate bounding box of a geometry, automatically taking into account whether it is cartesian or ge...
Definition: lwgeom.c:737
#define TRIANGLETYPE
Definition: liblwgeom.h:129
double lwgeom_maxdistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
Function handling max distance calculations and dfullywithin calculations.
Definition: measures.c:177
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:215
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:107
#define LW_INSIDE
Constants for point-in-polygon return values.
#define LW_BOUNDARY
int ptarray_contains_point(const POINTARRAY *pa, const POINT2D *pt)
Return 1 if the point is inside the POINTARRAY, -1 if it is outside, and 0 if it is on the boundary.
Definition: ptarray.c:732
int lwpoly_contains_point(const LWPOLY *poly, const POINT2D *pt)
Definition: lwpoly.c:532
#define FP_IS_ZERO(A)
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition: lwutil.c:177
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:193
double lwrandom_uniform(void)
Definition: lwrandom.c:94
int lw_dist3d_pt_tri(POINT3DZ *p, LWTRIANGLE *tri, PLANE3D *plane, POINT3DZ *projp, DISTPTS3D *dl)
Definition: measures3d.c:1277
int pt_in_ring_3d(const POINT3DZ *p, const POINTARRAY *ring, PLANE3D *plane)
pt_in_ring_3d(): crossing number test for a point in a polygon input: p = a point,...
Definition: measures3d.c:1567
int lw_dist3d_pt_ptarray(POINT3DZ *p, POINTARRAY *pa, DISTPTS3D *dl)
search all the segments of pointarray to see which one is closest to p Returns distance between point...
Definition: measures3d.c:962
int lw_dist3d_line_tri(LWLINE *line, LWTRIANGLE *tri, DISTPTS3D *dl)
line to triangle calculation
Definition: measures3d.c:820
static LWGEOM * create_v_line(const LWGEOM *lwgeom, double x, double y, int32_t srid)
This function is used to create a vertical line used for cases where one if the geometries lacks z-va...
Definition: measures3d.c:59
double lwgeom_maxdistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
Function handling 3d max distance calculations and dfullywithin calculations.
Definition: measures3d.c:310
int lw_dist3d_tri_tri(LWTRIANGLE *tri1, LWTRIANGLE *tri2, DISTPTS3D *dl)
triangle to triangle calculation
Definition: measures3d.c:918
int lw_dist3d_point_point(LWPOINT *point1, LWPOINT *point2, DISTPTS3D *dl)
point to point calculation
Definition: measures3d.c:705
int lw_dist3d_seg_seg(POINT3DZ *s1p1, POINT3DZ *s1p2, POINT3DZ *s2p1, POINT3DZ *s2p2, DISTPTS3D *dl)
Finds the two closest points on two linesegments.
Definition: measures3d.c:1139
LWGEOM * lwgeom_furthest_line_3d(LWGEOM *lw1, LWGEOM *lw2)
Definition: measures3d.c:82
static int lwgeom_solid_contains_lwgeom(const LWGEOM *solid, const LWGEOM *g)
Definition: measures3d.c:348
LWGEOM * lwgeom_closest_point_3d(const LWGEOM *lw1, const LWGEOM *lw2)
Definition: measures3d.c:88
int define_plane(POINTARRAY *pa, PLANE3D *pl)
Definition: measures3d.c:1451
int lw_dist3d_distribute_bruteforce(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl)
This function distributes the brute-force for 3D so far the only type, tasks depending on type.
Definition: measures3d.c:564
int lw_dist3d_point_tri(LWPOINT *point, LWTRIANGLE *tri, DISTPTS3D *dl)
Definition: measures3d.c:770
static int get_3dvector_from_points(POINT3DZ *p1, POINT3DZ *p2, VECTOR3D *v)
Definition: measures3d.c:34
static int get_3dcross_product(VECTOR3D *v1, VECTOR3D *v2, VECTOR3D *v)
Definition: measures3d.c:44
int lw_dist3d_pt_poly(POINT3DZ *p, LWPOLY *poly, PLANE3D *plane, POINT3DZ *projp, DISTPTS3D *dl)
Checking if the point projected on the plane of the polygon actually is inside that polygon.
Definition: measures3d.c:1251
int lw_dist3d_poly_tri(LWPOLY *poly, LWTRIANGLE *tri, DISTPTS3D *dl)
polygon to triangle calculation
Definition: measures3d.c:877
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
Definition: measures3d.c:1032
int lw_dist3d_pt_pt(POINT3DZ *thep1, POINT3DZ *thep2, DISTPTS3D *dl)
Compares incoming points and stores the points closest to each other or most far away from each other...
Definition: measures3d.c:1048
int lw_dist3d_pt_seg(POINT3DZ *p, POINT3DZ *A, POINT3DZ *B, DISTPTS3D *dl)
If searching for min distance, this one finds the closest point on segment A-B from p.
Definition: measures3d.c:992
double lwgeom_mindistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
Function handling 3d min distance calculations and dwithin calculations.
Definition: measures3d.c:442
int lw_dist3d_poly_poly(LWPOLY *poly1, LWPOLY *poly2, DISTPTS3D *dl)
polygon to polygon calculation
Definition: measures3d.c:836
int lw_dist3d_ptarray_tri(POINTARRAY *pa, LWTRIANGLE *tri, PLANE3D *plane, DISTPTS3D *dl)
Computes pointarray to triangle distance.
Definition: measures3d.c:1373
int lw_dist3d_line_line(LWLINE *line1, LWLINE *line2, DISTPTS3D *dl)
line to line calculation
Definition: measures3d.c:792
LWGEOM * lw_dist3d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
Function initializing 3dclosestpoint calculations.
Definition: measures3d.c:201
int lw_dist3d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly, PLANE3D *plane, DISTPTS3D *dl)
Computes pointarray to polygon distance.
Definition: measures3d.c:1293
static int gbox_contains_3d(const GBOX *g1, const GBOX *g2)
Definition: measures3d.c:341
LWGEOM * lw_dist3d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
Function initializing 3dshortestline and 3dlongestline calculations.
Definition: measures3d.c:97
double lwgeom_maxdistance3d(const LWGEOM *lw1, const LWGEOM *lw2)
Function initializing 3d max distance calculation.
Definition: measures3d.c:300
int lw_dist3d_point_poly(LWPOINT *point, LWPOLY *poly, DISTPTS3D *dl)
Computes point to polygon distance For mindistance that means: 1) find the plane of the polygon 2) pr...
Definition: measures3d.c:744
int lw_dist3d_point_line(LWPOINT *point, LWLINE *line, DISTPTS3D *dl)
point to line calculation
Definition: measures3d.c:721
double project_point_on_plane(POINT3DZ *p, PLANE3D *pl, POINT3DZ *p0)
Finds a point on a plane from where the original point is perpendicular to the plane.
Definition: measures3d.c:1524
int lw_dist3d_line_poly(LWLINE *line, LWPOLY *poly, DISTPTS3D *dl)
line to polygon calculation
Definition: measures3d.c:803
int lw_dist3d_recursive(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl)
This is a recursive function delivering every possible combination of subgeometries.
Definition: measures3d.c:485
LWGEOM * lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2)
Definition: measures3d.c:76
int lw_dist3d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS3D *dl)
Finds all combinations of segments between two pointarrays.
Definition: measures3d.c:1088
double lwgeom_mindistance3d(const LWGEOM *lw1, const LWGEOM *lw2)
Function initializing 3d min distance calculation.
Definition: measures3d.c:335
#define DOT(u, v)
Definition: measures3d.h:31
LWGEOM * lw_dist2d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
Function initializing shortestline and longestline calculations.
Definition: measures.c:81
int lw_dist2d_comp(const LWGEOM *lw1, const LWGEOM *lw2, DISTPTS *dl)
This function just deserializes geometries Bboxes is not checked here since it is the subgeometries b...
Definition: measures.c:236
LWGEOM * lw_dist2d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
Function initializing closestpoint calculations.
Definition: measures.c:128
#define DIST_MIN
Definition: measures.h:44
#define DIST_MAX
Definition: measures.h:43
POINT3DZ p2
Definition: measures3d.h:42
int twisted
Definition: measures3d.h:45
POINT3DZ p1
Definition: measures3d.h:41
double distance
Definition: measures3d.h:40
int mode
Definition: measures3d.h:43
double tolerance
Definition: measures3d.h:47
Structure used in distance-calculations.
Definition: measures3d.h:39
POINT2D p1
Definition: measures.h:52
POINT2D p2
Definition: measures.h:53
double tolerance
Definition: measures.h:56
int mode
Definition: measures.h:54
double distance
Definition: measures.h:51
Structure used in distance-calculations.
Definition: measures.h:50
double ymax
Definition: liblwgeom.h:343
double zmax
Definition: liblwgeom.h:345
double xmax
Definition: liblwgeom.h:341
double zmin
Definition: liblwgeom.h:344
double ymin
Definition: liblwgeom.h:342
double xmin
Definition: liblwgeom.h:340
uint32_t ngeoms
Definition: liblwgeom.h:566
LWGEOM ** geoms
Definition: liblwgeom.h:561
uint8_t type
Definition: liblwgeom.h:448
int32_t srid
Definition: liblwgeom.h:446
lwflags_t flags
Definition: liblwgeom.h:447
POINTARRAY * points
Definition: liblwgeom.h:469
POINTARRAY * point
Definition: liblwgeom.h:457
POINTARRAY ** rings
Definition: liblwgeom.h:505
uint32_t nrings
Definition: liblwgeom.h:510
POINTARRAY * points
Definition: liblwgeom.h:481
POINT3DZ pop
Definition: measures3d.h:57
VECTOR3D pv
Definition: measures3d.h:58
double y
Definition: liblwgeom.h:376
double x
Definition: liblwgeom.h:376
double z
Definition: liblwgeom.h:382
double x
Definition: liblwgeom.h:382
double y
Definition: liblwgeom.h:382
double z
Definition: liblwgeom.h:388
double x
Definition: liblwgeom.h:388
double y
Definition: liblwgeom.h:388
double z
Definition: liblwgeom.h:400
uint32_t npoints
Definition: liblwgeom.h:413
double z
Definition: measures3d.h:52
double x
Definition: measures3d.h:52
double y
Definition: measures3d.h:52