PostGIS  3.4.0dev-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.");
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.");
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.");
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");
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.");
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.");
278  }
279  }
280  if (thedl.distance == initdistance)
281  {
282  LWDEBUG(3, "didn't find geometries to measure between, returning null");
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  return LW_TRUE;
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_collection(g2))
535  {
536  LWDEBUG(3, "Found collection inside second geometry collection, recursing");
537  if (!lw_dist3d_recursive(g1, g2, dl))
538  return LW_FALSE;
539  continue;
540  }
541 
542  /*If one of geometries is empty, return. True here only means continue searching. False would
543  * have stopped the process*/
544  if (lwgeom_is_empty(g1) || lwgeom_is_empty(g2))
545  return LW_TRUE;
546 
547  if (!lw_dist3d_distribute_bruteforce(g1, g2, dl))
548  return LW_FALSE;
549  if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
550  return LW_TRUE; /*just a check if the answer is already given*/
551  }
552  }
553  return LW_TRUE;
554 }
555 
560 int
562 {
563  int t1 = lwg1->type;
564  int t2 = lwg2->type;
565 
566  LWDEBUGF(2, "lw_dist3d_distribute_bruteforce is called with typ1=%d, type2=%d", lwg1->type, lwg2->type);
567 
568  if (t1 == POINTTYPE)
569  {
570  if (t2 == POINTTYPE)
571  {
572  dl->twisted = 1;
573  return lw_dist3d_point_point((LWPOINT *)lwg1, (LWPOINT *)lwg2, dl);
574  }
575  else if (t2 == LINETYPE)
576  {
577  dl->twisted = 1;
578  return lw_dist3d_point_line((LWPOINT *)lwg1, (LWLINE *)lwg2, dl);
579  }
580  else if (t2 == POLYGONTYPE)
581  {
582  dl->twisted = 1;
583  return lw_dist3d_point_poly((LWPOINT *)lwg1, (LWPOLY *)lwg2, dl);
584  }
585  else if (t2 == TRIANGLETYPE)
586  {
587  dl->twisted = 1;
588  return lw_dist3d_point_tri((LWPOINT *)lwg1, (LWTRIANGLE *)lwg2, dl);
589  }
590  else
591  {
592  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
593  return LW_FALSE;
594  }
595  }
596  else if (t1 == LINETYPE)
597  {
598  if (t2 == POINTTYPE)
599  {
600  dl->twisted = -1;
601  return lw_dist3d_point_line((LWPOINT *)lwg2, (LWLINE *)lwg1, dl);
602  }
603  else if (t2 == LINETYPE)
604  {
605  dl->twisted = 1;
606  return lw_dist3d_line_line((LWLINE *)lwg1, (LWLINE *)lwg2, dl);
607  }
608  else if (t2 == POLYGONTYPE)
609  {
610  dl->twisted = 1;
611  return lw_dist3d_line_poly((LWLINE *)lwg1, (LWPOLY *)lwg2, dl);
612  }
613  else if (t2 == TRIANGLETYPE)
614  {
615  dl->twisted = 1;
616  return lw_dist3d_line_tri((LWLINE *)lwg1, (LWTRIANGLE *)lwg2, dl);
617  }
618  else
619  {
620  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
621  return LW_FALSE;
622  }
623  }
624  else if (t1 == POLYGONTYPE)
625  {
626  if (t2 == POLYGONTYPE)
627  {
628  dl->twisted = 1;
629  return lw_dist3d_poly_poly((LWPOLY *)lwg1, (LWPOLY *)lwg2, dl);
630  }
631  else if (t2 == POINTTYPE)
632  {
633  dl->twisted = -1;
634  return lw_dist3d_point_poly((LWPOINT *)lwg2, (LWPOLY *)lwg1, dl);
635  }
636  else if (t2 == LINETYPE)
637  {
638  dl->twisted = -1;
639  return lw_dist3d_line_poly((LWLINE *)lwg2, (LWPOLY *)lwg1, dl);
640  }
641  else if (t2 == TRIANGLETYPE)
642  {
643  dl->twisted = 1;
644  return lw_dist3d_poly_tri((LWPOLY *)lwg1, (LWTRIANGLE *)lwg2, dl);
645  }
646  else
647  {
648  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
649  return LW_FALSE;
650  }
651  }
652  else if (t1 == TRIANGLETYPE)
653  {
654  if (t2 == POLYGONTYPE)
655  {
656  dl->twisted = -1;
657  return lw_dist3d_poly_tri((LWPOLY *)lwg2, (LWTRIANGLE *)lwg1, dl);
658  }
659  else if (t2 == POINTTYPE)
660  {
661  dl->twisted = -1;
662  return lw_dist3d_point_tri((LWPOINT *)lwg2, (LWTRIANGLE *)lwg1, dl);
663  }
664  else if (t2 == LINETYPE)
665  {
666  dl->twisted = -1;
667  return lw_dist3d_line_tri((LWLINE *)lwg2, (LWTRIANGLE *)lwg1, dl);
668  }
669  else if (t2 == TRIANGLETYPE)
670  {
671  dl->twisted = 1;
672  return lw_dist3d_tri_tri((LWTRIANGLE *)lwg1, (LWTRIANGLE *)lwg2, dl);
673  }
674  else
675  {
676  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
677  return LW_FALSE;
678  }
679  }
680 
681  else
682  {
683  lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t1));
684  return LW_FALSE;
685  }
686 }
687 
688 /*------------------------------------------------------------------------------------------------------------
689 End of Preprocessing functions
690 --------------------------------------------------------------------------------------------------------------*/
691 
692 /*------------------------------------------------------------------------------------------------------------
693 Brute force functions
694 So far the only way to do 3D-calculations
695 --------------------------------------------------------------------------------------------------------------*/
696 
701 int
703 {
704  POINT3DZ p1;
705  POINT3DZ p2;
706  LWDEBUG(2, "lw_dist3d_point_point is called");
707 
708  getPoint3dz_p(point1->point, 0, &p1);
709  getPoint3dz_p(point2->point, 0, &p2);
710 
711  return lw_dist3d_pt_pt(&p1, &p2, dl);
712 }
717 int
719 {
720  POINT3DZ p;
721  POINTARRAY *pa = line->points;
722  LWDEBUG(2, "lw_dist3d_point_line is called");
723 
724  getPoint3dz_p(point->point, 0, &p);
725  return lw_dist3d_pt_ptarray(&p, pa, dl);
726 }
727 
740 int
742 {
743  POINT3DZ p, projp; /*projp is "point projected on plane"*/
744  PLANE3D plane;
745  LWDEBUG(2, "lw_dist3d_point_poly is called");
746  getPoint3dz_p(point->point, 0, &p);
747 
748  /* If we are looking for max distance, longestline or dfullywithin */
749  if (dl->mode == DIST_MAX)
750  return lw_dist3d_pt_ptarray(&p, poly->rings[0], dl);
751 
752  /* Find the plane of the polygon, the "holes" have to be on the same plane. so we only care about the boudary */
753  if (!define_plane(poly->rings[0], &plane))
754  {
755  /* Polygon does not define a plane: Return distance point -> line */
756  return lw_dist3d_pt_ptarray(&p, poly->rings[0], dl);
757  }
758 
759  /* Get our point projected on the plane of the polygon */
760  project_point_on_plane(&p, &plane, &projp);
761 
762  return lw_dist3d_pt_poly(&p, poly, &plane, &projp, dl);
763 }
764 
765 /* point to triangle calculation */
766 int
768 {
769  POINT3DZ p, projp; /*projp is "point projected on plane"*/
770  PLANE3D plane;
771  getPoint3dz_p(point->point, 0, &p);
772 
773  /* If we are looking for max distance, longestline or dfullywithin */
774  if (dl->mode == DIST_MAX)
775  return lw_dist3d_pt_ptarray(&p, tri->points, dl);
776 
777  /* If triangle does not define a plane, treat it as a line */
778  if (!define_plane(tri->points, &plane))
779  return lw_dist3d_pt_ptarray(&p, tri->points, dl);
780 
781  /* Get our point projected on the plane of triangle */
782  project_point_on_plane(&p, &plane, &projp);
783 
784  return lw_dist3d_pt_tri(&p, tri, &plane, &projp, dl);
785 }
786 
788 int
790 {
791  POINTARRAY *pa1 = line1->points;
792  POINTARRAY *pa2 = line2->points;
793  LWDEBUG(2, "lw_dist3d_line_line is called");
794 
795  return lw_dist3d_ptarray_ptarray(pa1, pa2, dl);
796 }
797 
799 int
801 {
802  PLANE3D plane;
803  LWDEBUG(2, "lw_dist3d_line_poly is called");
804 
805  if (dl->mode == DIST_MAX)
806  return lw_dist3d_ptarray_ptarray(line->points, poly->rings[0], dl);
807 
808  /* if polygon does not define a plane: Return distance line to line */
809  if (!define_plane(poly->rings[0], &plane))
810  return lw_dist3d_ptarray_ptarray(line->points, poly->rings[0], dl);
811 
812  return lw_dist3d_ptarray_poly(line->points, poly, &plane, dl);
813 }
814 
816 int
818 {
819  PLANE3D plane;
820 
821  if (dl->mode == DIST_MAX)
822  return lw_dist3d_ptarray_ptarray(line->points, tri->points, dl);
823 
824  /* if triangle does not define a plane: Return distance line to line */
825  if (!define_plane(tri->points, &plane))
826  return lw_dist3d_ptarray_ptarray(line->points, tri->points, dl);
827 
828  return lw_dist3d_ptarray_tri(line->points, tri, &plane, dl);
829 }
830 
832 int
834 {
835  PLANE3D plane1, plane2;
836  int planedef1, planedef2;
837  LWDEBUG(2, "lw_dist3d_poly_poly is called");
838  if (dl->mode == DIST_MAX)
839  return lw_dist3d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl);
840 
841  planedef1 = define_plane(poly1->rings[0], &plane1);
842  planedef2 = define_plane(poly2->rings[0], &plane2);
843 
844  if (!planedef1 || !planedef2)
845  {
846  /* Neither polygon define a plane: Return distance line to line */
847  if (!planedef1 && !planedef2)
848  return lw_dist3d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl);
849 
850  /* Only poly2 defines a plane: Return distance from line (poly1) to poly2 */
851  else if (!planedef1)
852  return lw_dist3d_ptarray_poly(poly1->rings[0], poly2, &plane2, dl);
853 
854  /* Only poly1 defines a plane: Return distance from line (poly2) to poly1 */
855  else
856  return lw_dist3d_ptarray_poly(poly2->rings[0], poly1, &plane1, dl);
857  }
858 
859  /* What we do here is to compare the boundary of one polygon with the other polygon
860  and then take the second boundary comparing with the first polygon */
861  dl->twisted = 1;
862  if (!lw_dist3d_ptarray_poly(poly1->rings[0], poly2, &plane2, dl))
863  return LW_FALSE;
864  if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/
865  return LW_TRUE;
866 
867  dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the
868  right order of points in shortest line. */
869  return lw_dist3d_ptarray_poly(poly2->rings[0], poly1, &plane1, dl);
870 }
871 
873 int
875 {
876  PLANE3D plane1, plane2;
877  int planedef1, planedef2;
878 
879  if (dl->mode == DIST_MAX)
880  return lw_dist3d_ptarray_ptarray(poly->rings[0], tri->points, dl);
881 
882  planedef1 = define_plane(poly->rings[0], &plane1);
883  planedef2 = define_plane(tri->points, &plane2);
884 
885  if (!planedef1 || !planedef2)
886  {
887  /* Neither define a plane: Return distance line to line */
888  if (!planedef1 && !planedef2)
889  return lw_dist3d_ptarray_ptarray(poly->rings[0], tri->points, dl);
890 
891  /* Only tri defines a plane: Return distance from line (poly) to tri */
892  else if (!planedef1)
893  return lw_dist3d_ptarray_tri(poly->rings[0], tri, &plane2, dl);
894 
895  /* Only poly defines a plane: Return distance from line (tri) to poly */
896  else
897  return lw_dist3d_ptarray_poly(tri->points, poly, &plane1, dl);
898  }
899 
900  /* What we do here is to compare the boundary of one polygon with the other polygon
901  and then take the second boundary comparing with the first polygon */
902  dl->twisted = 1;
903  if (!lw_dist3d_ptarray_tri(poly->rings[0], tri, &plane2, dl))
904  return LW_FALSE;
905  if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/
906  return LW_TRUE;
907 
908  dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the
909  right order of points in shortest line. */
910  return lw_dist3d_ptarray_poly(tri->points, poly, &plane1, dl);
911 }
912 
914 int
916 {
917  PLANE3D plane1, plane2;
918  int planedef1, planedef2;
919 
920  if (dl->mode == DIST_MAX)
921  return lw_dist3d_ptarray_ptarray(tri1->points, tri2->points, dl);
922 
923  planedef1 = define_plane(tri1->points, &plane1);
924  planedef2 = define_plane(tri2->points, &plane2);
925 
926  if (!planedef1 || !planedef2)
927  {
928  /* Neither define a plane: Return distance line to line */
929  if (!planedef1 && !planedef2)
930  return lw_dist3d_ptarray_ptarray(tri1->points, tri2->points, dl);
931 
932  /* Only tri defines a plane: Return distance from line (tri1) to tri2 */
933  else if (!planedef1)
934  return lw_dist3d_ptarray_tri(tri1->points, tri2, &plane2, dl);
935 
936  /* Only poly defines a plane: Return distance from line (tri2) to tri1 */
937  else
938  return lw_dist3d_ptarray_tri(tri2->points, tri1, &plane1, dl);
939  }
940 
941  /* What we do here is to compare the boundary of one polygon with the other polygon
942  and then take the second boundary comparing with the first polygon */
943  dl->twisted = 1;
944  if (!lw_dist3d_ptarray_tri(tri1->points, tri2, &plane2, dl))
945  return LW_FALSE;
946  if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/
947  return LW_TRUE;
948 
949  dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the
950  right order of points in shortest line. */
951  return lw_dist3d_ptarray_tri(tri2->points, tri1, &plane1, dl);
952 }
953 
958 int
960 {
961  uint32_t t;
962  POINT3DZ start, end;
963  int twist = dl->twisted;
964  if (!pa)
965  return LW_FALSE;
966 
967  getPoint3dz_p(pa, 0, &start);
968 
969  for (t = 1; t < pa->npoints; t++)
970  {
971  dl->twisted = twist;
972  getPoint3dz_p(pa, t, &end);
973  if (!lw_dist3d_pt_seg(p, &start, &end, dl))
974  return LW_FALSE;
975 
976  if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
977  return LW_TRUE; /*just a check if the answer is already given*/
978  start = end;
979  }
980 
981  return LW_TRUE;
982 }
983 
988 int
990 {
991  POINT3DZ c;
992  double r;
993  /*if start==end, then use pt distance */
994  if ((A->x == B->x) && (A->y == B->y) && (A->z == B->z))
995  {
996  return lw_dist3d_pt_pt(p, A, dl);
997  }
998 
999  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)) /
1000  ((B->x - A->x) * (B->x - A->x) + (B->y - A->y) * (B->y - A->y) + (B->z - A->z) * (B->z - A->z));
1001 
1002  /*This is for finding the 3Dmaxdistance.
1003  the maxdistance have to be between two vertexes,
1004  compared to mindistance which can be between
1005  two vertexes vertex.*/
1006  if (dl->mode == DIST_MAX)
1007  {
1008  if (r >= 0.5)
1009  return lw_dist3d_pt_pt(p, A, dl);
1010  if (r < 0.5)
1011  return lw_dist3d_pt_pt(p, B, dl);
1012  }
1013 
1014  if (r <= 0) /*If the first vertex A is closest to the point p*/
1015  return lw_dist3d_pt_pt(p, A, dl);
1016  if (r >= 1) /*If the second vertex B is closest to the point p*/
1017  return lw_dist3d_pt_pt(p, B, dl);
1018 
1019  /*else if the point p is closer to some point between a and b
1020  then we find that point and send it to lw_dist3d_pt_pt*/
1021  c.x = A->x + r * (B->x - A->x);
1022  c.y = A->y + r * (B->y - A->y);
1023  c.z = A->z + r * (B->z - A->z);
1024 
1025  return lw_dist3d_pt_pt(p, &c, dl);
1026 }
1027 
1028 double
1029 distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
1030 {
1031  double dx = p2->x - p1->x;
1032  double dy = p2->y - p1->y;
1033  double dz = p2->z - p1->z;
1034  return sqrt(dx * dx + dy * dy + dz * dz);
1035 }
1036 
1044 int
1046 {
1047  double dx = thep2->x - thep1->x;
1048  double dy = thep2->y - thep1->y;
1049  double dz = thep2->z - thep1->z;
1050  double dist = sqrt(dx * dx + dy * dy + dz * dz);
1051  LWDEBUGF(2,
1052  "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)",
1053  thep1->x,
1054  thep1->y,
1055  thep1->z,
1056  thep2->x,
1057  thep2->y,
1058  thep2->z);
1059 
1060  if (((dl->distance - dist) * (dl->mode)) >
1061  0) /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/
1062  {
1063  dl->distance = dist;
1064 
1065  if (dl->twisted > 0) /*To get the points in right order. twisted is updated between 1 and (-1) every
1066  time the order is changed earlier in the chain*/
1067  {
1068  dl->p1 = *thep1;
1069  dl->p2 = *thep2;
1070  }
1071  else
1072  {
1073  dl->p1 = *thep2;
1074  dl->p2 = *thep1;
1075  }
1076  }
1077  return LW_TRUE;
1078 }
1079 
1084 int
1086 {
1087  uint32_t t, u;
1088  POINT3DZ start, end;
1089  POINT3DZ start2, end2;
1090  int twist = dl->twisted;
1091  LWDEBUGF(2, "lw_dist3d_ptarray_ptarray called (points: %d-%d)", l1->npoints, l2->npoints);
1092 
1093  if (dl->mode == DIST_MAX) /*If we are searching for maxdistance we go straight to point-point calculation since
1094  the maxdistance have to be between two vertexes*/
1095  {
1096  for (t = 0; t < l1->npoints; t++) /*for each segment in L1 */
1097  {
1098  getPoint3dz_p(l1, t, &start);
1099  for (u = 0; u < l2->npoints; u++) /*for each segment in L2 */
1100  {
1101  getPoint3dz_p(l2, u, &start2);
1102  lw_dist3d_pt_pt(&start, &start2, dl);
1103  }
1104  }
1105  }
1106  else
1107  {
1108  getPoint3dz_p(l1, 0, &start);
1109  for (t = 1; t < l1->npoints; t++) /*for each segment in L1 */
1110  {
1111  getPoint3dz_p(l1, t, &end);
1112  getPoint3dz_p(l2, 0, &start2);
1113  for (u = 1; u < l2->npoints; u++) /*for each segment in L2 */
1114  {
1115  getPoint3dz_p(l2, u, &end2);
1116  dl->twisted = twist;
1117  lw_dist3d_seg_seg(&start, &end, &start2, &end2, dl);
1118  LWDEBUGF(
1119  4, "mindist_ptarray_ptarray; seg %i * seg %i, dist = %g\n", t, u, dl->distance);
1120  LWDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f", t, u, dl->distance, dl->tolerance);
1121  if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1122  return LW_TRUE; /*just a check if the answer is already given*/
1123  start2 = end2;
1124  }
1125  start = end;
1126  }
1127  }
1128  return LW_TRUE;
1129 }
1130 
1135 int
1137 {
1138  VECTOR3D v1, v2, vl;
1139  double s1k, s2k; /*two variables representing where on Line 1 (s1k) and where on Line 2 (s2k) a connecting line
1140  between the two lines is perpendicular to both lines*/
1141  POINT3DZ p1, p2;
1142  double a, b, c, d, e, D;
1143 
1144  /*s1p1 and s1p2 are the same point */
1145  if ((s1p1->x == s1p2->x) && (s1p1->y == s1p2->y) && (s1p1->z == s1p2->z))
1146  {
1147  return lw_dist3d_pt_seg(s1p1, s2p1, s2p2, dl);
1148  }
1149  /*s2p1 and s2p2 are the same point */
1150  if ((s2p1->x == s2p2->x) && (s2p1->y == s2p2->y) && (s2p1->z == s2p2->z))
1151  {
1152  dl->twisted = ((dl->twisted) * (-1));
1153  return lw_dist3d_pt_seg(s2p1, s1p1, s1p2, dl);
1154  }
1155 
1156  /*
1157  Here we use algorithm from softsurfer.com
1158  that can be found here
1159  http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
1160  */
1161 
1162  if (!get_3dvector_from_points(s1p1, s1p2, &v1))
1163  return LW_FALSE;
1164 
1165  if (!get_3dvector_from_points(s2p1, s2p2, &v2))
1166  return LW_FALSE;
1167 
1168  if (!get_3dvector_from_points(s2p1, s1p1, &vl))
1169  return LW_FALSE;
1170 
1171  a = DOT(v1, v1);
1172  b = DOT(v1, v2);
1173  c = DOT(v2, v2);
1174  d = DOT(v1, vl);
1175  e = DOT(v2, vl);
1176  D = a * c - b * b;
1177 
1178  if (D < 0.000000001)
1179  { /* the lines are almost parallel*/
1180  s1k =
1181  0.0; /*If the lines are parallel we try by using the startpoint of first segment. If that gives a
1182  projected point on the second line outside segment 2 it wil be found that s2k is >1 or <0.*/
1183  if (b > c) /* use the largest denominator*/
1184  s2k = d / b;
1185  else
1186  s2k = e / c;
1187  }
1188  else
1189  {
1190  s1k = (b * e - c * d) / D;
1191  s2k = (a * e - b * d) / D;
1192  }
1193 
1194  /* Now we check if the projected closest point on the infinite lines is outside our segments. If so the
1195  * combinations with start and end points will be tested*/
1196 
1197  if (s1k <= 0.0 || s1k >= 1.0 || s2k <= 0.0 || s2k >= 1.0)
1198  {
1199  if (s1k <= 0.0)
1200  {
1201  if (!lw_dist3d_pt_seg(s1p1, s2p1, s2p2, dl))
1202  return LW_FALSE;
1203  }
1204  if (s1k >= 1.0)
1205  {
1206  if (!lw_dist3d_pt_seg(s1p2, s2p1, s2p2, dl))
1207  return LW_FALSE;
1208  }
1209  if (s2k <= 0.0)
1210  {
1211  dl->twisted = ((dl->twisted) * (-1));
1212  if (!lw_dist3d_pt_seg(s2p1, s1p1, s1p2, dl))
1213  return LW_FALSE;
1214  }
1215  if (s2k >= 1.0)
1216  {
1217  dl->twisted = ((dl->twisted) * (-1));
1218  if (!lw_dist3d_pt_seg(s2p2, s1p1, s1p2, dl))
1219  return LW_FALSE;
1220  }
1221  }
1222  else
1223  { /*Find the closest point on the edges of both segments*/
1224  p1.x = s1p1->x + s1k * (s1p2->x - s1p1->x);
1225  p1.y = s1p1->y + s1k * (s1p2->y - s1p1->y);
1226  p1.z = s1p1->z + s1k * (s1p2->z - s1p1->z);
1227 
1228  p2.x = s2p1->x + s2k * (s2p2->x - s2p1->x);
1229  p2.y = s2p1->y + s2k * (s2p2->y - s2p1->y);
1230  p2.z = s2p1->z + s2k * (s2p2->z - s2p1->z);
1231 
1232  if (!lw_dist3d_pt_pt(&p1, &p2, dl)) /* Send the closest points to point-point calculation*/
1233  {
1234  return LW_FALSE;
1235  }
1236  }
1237  return LW_TRUE;
1238 }
1239 
1247 int
1249 {
1250  uint32_t i;
1251 
1252  if (pt_in_ring_3d(projp, poly->rings[0], plane))
1253  {
1254  for (i = 1; i < poly->nrings; i++)
1255  {
1256  /* Inside a hole. Distance = pt -> ring */
1257  if (pt_in_ring_3d(projp, poly->rings[i], plane))
1258  return lw_dist3d_pt_ptarray(p, poly->rings[i], dl);
1259  }
1260 
1261  /* if the projected point is inside the polygon the shortest distance is between that point and the
1262  * input point */
1263  return lw_dist3d_pt_pt(p, projp, dl);
1264  }
1265  else
1266  /* if the projected point is outside the polygon we search for the closest distance against the boundary
1267  * instead */
1268  return lw_dist3d_pt_ptarray(p, poly->rings[0], dl);
1269 
1270  return LW_TRUE;
1271 }
1272 
1273 int
1275 {
1276  if (pt_in_ring_3d(projp, tri->points, plane))
1277  /* if the projected point is inside the polygon the shortest distance is between that point and the
1278  * input point */
1279  return lw_dist3d_pt_pt(p, projp, dl);
1280  else
1281  /* if the projected point is outside the polygon we search for the closest distance against the boundary
1282  * instead */
1283  return lw_dist3d_pt_ptarray(p, tri->points, dl);
1284 
1285  return LW_TRUE;
1286 }
1287 
1289 int
1291 {
1292  uint32_t i, j, k;
1293  double f, s1, s2;
1294  VECTOR3D projp1_projp2;
1295  POINT3DZ p1, p2, projp1, projp2, intersectionp;
1296 
1297  getPoint3dz_p(pa, 0, &p1);
1298 
1299  /* the sign of s1 tells us on which side of the plane the point is. */
1300  s1 = project_point_on_plane(&p1, plane, &projp1);
1301  lw_dist3d_pt_poly(&p1, poly, plane, &projp1, dl);
1302  if ((s1 == 0.0) && (dl->distance < dl->tolerance))
1303  return LW_TRUE;
1304 
1305  for (i = 1; i < pa->npoints; i++)
1306  {
1307  int intersects;
1308  getPoint3dz_p(pa, i, &p2);
1309  s2 = project_point_on_plane(&p2, plane, &projp2);
1310  lw_dist3d_pt_poly(&p2, poly, plane, &projp2, dl);
1311  if ((s2 == 0.0) && (dl->distance < dl->tolerance))
1312  return LW_TRUE;
1313 
1314  /* If s1 and s2 has different signs that means they are on different sides of the plane of the polygon.
1315  * That means that the edge between the points crosses the plane and might intersect with the polygon */
1316  if ((s1 * s2) < 0)
1317  {
1318  /* The size of s1 and s2 is the distance from the point to the plane. */
1319  f = fabs(s1) / (fabs(s1) + fabs(s2));
1320  get_3dvector_from_points(&projp1, &projp2, &projp1_projp2);
1321 
1322  /* Get the point where the line segment crosses the plane */
1323  intersectionp.x = projp1.x + f * projp1_projp2.x;
1324  intersectionp.y = projp1.y + f * projp1_projp2.y;
1325  intersectionp.z = projp1.z + f * projp1_projp2.z;
1326 
1327  /* We set intersects to true until the opposite is proved */
1328  intersects = LW_TRUE;
1329 
1330  if (pt_in_ring_3d(&intersectionp, poly->rings[0], plane)) /*Inside outer ring*/
1331  {
1332  for (k = 1; k < poly->nrings; k++)
1333  {
1334  /* Inside a hole, so no intersection with the polygon*/
1335  if (pt_in_ring_3d(&intersectionp, poly->rings[k], plane))
1336  {
1337  intersects = LW_FALSE;
1338  break;
1339  }
1340  }
1341  if (intersects)
1342  {
1343  dl->distance = 0.0;
1344  dl->p1.x = intersectionp.x;
1345  dl->p1.y = intersectionp.y;
1346  dl->p1.z = intersectionp.z;
1347 
1348  dl->p2.x = intersectionp.x;
1349  dl->p2.y = intersectionp.y;
1350  dl->p2.z = intersectionp.z;
1351  return LW_TRUE;
1352  }
1353  }
1354  }
1355 
1356  projp1 = projp2;
1357  s1 = s2;
1358  p1 = p2;
1359  }
1360 
1361  /* check our pointarray against boundary and inner boundaries of the polygon */
1362  for (j = 0; j < poly->nrings; j++)
1363  lw_dist3d_ptarray_ptarray(pa, poly->rings[j], dl);
1364 
1365  return LW_TRUE;
1366 }
1367 
1369 int
1371 {
1372  uint32_t i;
1373  double f, s1, s2;
1374  VECTOR3D projp1_projp2;
1375  POINT3DZ p1, p2, projp1, projp2, intersectionp;
1376 
1377  getPoint3dz_p(pa, 0, &p1);
1378 
1379  /* the sign of s1 tells us on which side of the plane the point is. */
1380  s1 = project_point_on_plane(&p1, plane, &projp1);
1381  lw_dist3d_pt_tri(&p1, tri, plane, &projp1, dl);
1382  if ((s1 == 0.0) && (dl->distance < dl->tolerance))
1383  return LW_TRUE;
1384 
1385  for (i = 1; i < pa->npoints; i++)
1386  {
1387  int intersects;
1388  getPoint3dz_p(pa, i, &p2);
1389  s2 = project_point_on_plane(&p2, plane, &projp2);
1390  lw_dist3d_pt_tri(&p2, tri, plane, &projp2, dl);
1391  if ((s2 == 0.0) && (dl->distance < dl->tolerance))
1392  return LW_TRUE;
1393 
1394  /* If s1 and s2 has different signs that means they are on different sides of the plane of the triangle.
1395  * That means that the edge between the points crosses the plane and might intersect with the triangle
1396  */
1397  if ((s1 * s2) < 0)
1398  {
1399  /* The size of s1 and s2 is the distance from the point to the plane. */
1400  f = fabs(s1) / (fabs(s1) + fabs(s2));
1401  get_3dvector_from_points(&projp1, &projp2, &projp1_projp2);
1402 
1403  /* Get the point where the line segment crosses the plane */
1404  intersectionp.x = projp1.x + f * projp1_projp2.x;
1405  intersectionp.y = projp1.y + f * projp1_projp2.y;
1406  intersectionp.z = projp1.z + f * projp1_projp2.z;
1407 
1408  /* We set intersects to true until the opposite is proved */
1409  intersects = LW_TRUE;
1410 
1411  if (pt_in_ring_3d(&intersectionp, tri->points, plane)) /*Inside outer ring*/
1412  {
1413  if (intersects)
1414  {
1415  dl->distance = 0.0;
1416  dl->p1.x = intersectionp.x;
1417  dl->p1.y = intersectionp.y;
1418  dl->p1.z = intersectionp.z;
1419 
1420  dl->p2.x = intersectionp.x;
1421  dl->p2.y = intersectionp.y;
1422  dl->p2.z = intersectionp.z;
1423  return LW_TRUE;
1424  }
1425  }
1426  }
1427 
1428  projp1 = projp2;
1429  s1 = s2;
1430  p1 = p2;
1431  }
1432 
1433  /* check our pointarray against triangle contour */
1434  lw_dist3d_ptarray_ptarray(pa, tri->points, dl);
1435  return LW_TRUE;
1436 }
1437 
1438 /* Here we define the plane of a polygon (boundary pointarray of a polygon)
1439  * the plane is stored as a point in plane (plane.pop) and a normal vector (plane.pv)
1440  *
1441  * We are calculating the average point and using it to break the polygon into
1442  * POL_BREAKS (3) parts. Then we calculate the normal of those 3 vectors and
1443  * use its average to define the normal of the plane.This is done to minimize
1444  * the error introduced by FP precision
1445  * We use [3] breaks to contemplate the special case of 3d triangles
1446  */
1447 int
1449 {
1450  const uint32_t POL_BREAKS = 3;
1451 
1452  assert(pa);
1453  assert(pa->npoints > 3);
1454  if (!pa)
1455  return LW_FALSE;
1456 
1457  uint32_t unique_points = pa->npoints - 1;
1458  uint32_t i;
1459 
1460  if (pa->npoints < 3)
1461  return LW_FALSE;
1462 
1463  /* Calculate the average point */
1464  pl->pop.x = 0.0;
1465  pl->pop.y = 0.0;
1466  pl->pop.z = 0.0;
1467  for (i = 0; i < unique_points; i++)
1468  {
1469  POINT3DZ p;
1470  getPoint3dz_p(pa, i, &p);
1471  pl->pop.x += p.x;
1472  pl->pop.y += p.y;
1473  pl->pop.z += p.z;
1474  }
1475 
1476  pl->pop.x /= unique_points;
1477  pl->pop.y /= unique_points;
1478  pl->pop.z /= unique_points;
1479 
1480  pl->pv.x = 0.0;
1481  pl->pv.y = 0.0;
1482  pl->pv.z = 0.0;
1483  for (i = 0; i < POL_BREAKS; i++)
1484  {
1485  POINT3DZ point1, point2;
1486  VECTOR3D v1, v2, vp;
1487  uint32_t n1, n2;
1488  n1 = i * unique_points / POL_BREAKS;
1489  n2 = n1 + unique_points / POL_BREAKS; /* May overflow back to the first / last point */
1490 
1491  if (n1 == n2)
1492  continue;
1493 
1494  getPoint3dz_p(pa, n1, &point1);
1495  if (!get_3dvector_from_points(&pl->pop, &point1, &v1))
1496  continue;
1497 
1498  getPoint3dz_p(pa, n2, &point2);
1499  if (!get_3dvector_from_points(&pl->pop, &point2, &v2))
1500  continue;
1501 
1502  if (get_3dcross_product(&v1, &v2, &vp))
1503  {
1504  /* If the cross product isn't zero these 3 points aren't collinear
1505  * We divide by its lengthsq to normalize the additions */
1506  double vl = vp.x * vp.x + vp.y * vp.y + vp.z * vp.z;
1507  pl->pv.x += vp.x / vl;
1508  pl->pv.y += vp.y / vl;
1509  pl->pv.z += vp.z / vl;
1510  }
1511  }
1512 
1513  return (!FP_IS_ZERO(pl->pv.x) || !FP_IS_ZERO(pl->pv.y) || !FP_IS_ZERO(pl->pv.z));
1514 }
1515 
1520 double
1522 {
1523  /*In our plane definition we have a point on the plane and a normal vector (pl.pv), perpendicular to the plane
1524  this vector will be parallel to the line between our inputted point above the plane and the point we are
1525  searching for on the plane. So, we already have a direction from p to find p0, but we don't know the distance.
1526  */
1527 
1528  VECTOR3D v1;
1529  double f;
1530 
1531  if (!get_3dvector_from_points(&(pl->pop), p, &v1))
1532  return 0.0;
1533 
1534  f = DOT(pl->pv, v1);
1535  if (FP_IS_ZERO(f))
1536  {
1537  /* Point is in the plane */
1538  *p0 = *p;
1539  return 0;
1540  }
1541 
1542  f = -f / DOT(pl->pv, pl->pv);
1543 
1544  p0->x = p->x + pl->pv.x * f;
1545  p0->y = p->y + pl->pv.y * f;
1546  p0->z = p->z + pl->pv.z * f;
1547 
1548  return f;
1549 }
1550 
1563 int
1564 pt_in_ring_3d(const POINT3DZ *p, const POINTARRAY *ring, PLANE3D *plane)
1565 {
1566 
1567  uint32_t cn = 0; /* the crossing number counter */
1568  uint32_t i;
1569  POINT3DZ v1, v2;
1570 
1571  POINT3DZ first, last;
1572 
1573  getPoint3dz_p(ring, 0, &first);
1574  getPoint3dz_p(ring, ring->npoints - 1, &last);
1575  if (memcmp(&first, &last, sizeof(POINT3DZ)))
1576  {
1577  lwerror("pt_in_ring_3d: V[n] != V[0] (%g %g %g!= %g %g %g)",
1578  first.x,
1579  first.y,
1580  first.z,
1581  last.x,
1582  last.y,
1583  last.z);
1584  return LW_FALSE;
1585  }
1586 
1587  LWDEBUGF(2, "pt_in_ring_3d called with point: %g %g %g", p->x, p->y, p->z);
1588  /* printPA(ring); */
1589 
1590  /* loop through all edges of the polygon */
1591  getPoint3dz_p(ring, 0, &v1);
1592 
1593  if (fabs(plane->pv.z) >= fabs(plane->pv.x) &&
1594  fabs(plane->pv.z) >= fabs(plane->pv.y)) /*If the z vector of the normal vector to the plane is larger than x
1595  and y vector we project the ring to the xy-plane*/
1596  {
1597  for (i = 0; i < ring->npoints - 1; i++)
1598  {
1599  double vt;
1600  getPoint3dz_p(ring, i + 1, &v2);
1601 
1602  /* edge from vertex i to vertex i+1 */
1603  if (
1604  /* an upward crossing */
1605  ((v1.y <= p->y) && (v2.y > p->y))
1606  /* a downward crossing */
1607  || ((v1.y > p->y) && (v2.y <= p->y)))
1608  {
1609 
1610  vt = (double)(p->y - v1.y) / (v2.y - v1.y);
1611 
1612  /* P.x <intersect */
1613  if (p->x < v1.x + vt * (v2.x - v1.x))
1614  {
1615  /* a valid crossing of y=p.y right of p.x */
1616  ++cn;
1617  }
1618  }
1619  v1 = v2;
1620  }
1621  }
1622  else if (fabs(plane->pv.y) >= fabs(plane->pv.x) &&
1623  fabs(plane->pv.y) >= fabs(plane->pv.z)) /*If the y vector of the normal vector to the plane is larger
1624  than x and z vector we project the ring to the xz-plane*/
1625  {
1626  for (i = 0; i < ring->npoints - 1; i++)
1627  {
1628  double vt;
1629  getPoint3dz_p(ring, i + 1, &v2);
1630 
1631  /* edge from vertex i to vertex i+1 */
1632  if (
1633  /* an upward crossing */
1634  ((v1.z <= p->z) && (v2.z > p->z))
1635  /* a downward crossing */
1636  || ((v1.z > p->z) && (v2.z <= p->z)))
1637  {
1638 
1639  vt = (double)(p->z - v1.z) / (v2.z - v1.z);
1640 
1641  /* P.x <intersect */
1642  if (p->x < v1.x + vt * (v2.x - v1.x))
1643  {
1644  /* a valid crossing of y=p.y right of p.x */
1645  ++cn;
1646  }
1647  }
1648  v1 = v2;
1649  }
1650  }
1651  else /*Hopefully we only have the cases where x part of the normal vector is largest left*/
1652  {
1653  for (i = 0; i < ring->npoints - 1; i++)
1654  {
1655  double vt;
1656  getPoint3dz_p(ring, i + 1, &v2);
1657 
1658  /* edge from vertex i to vertex i+1 */
1659  if (
1660  /* an upward crossing */
1661  ((v1.z <= p->z) && (v2.z > p->z))
1662  /* a downward crossing */
1663  || ((v1.z > p->z) && (v2.z <= p->z)))
1664  {
1665 
1666  vt = (double)(p->z - v1.z) / (v2.z - v1.z);
1667 
1668  /* P.x <intersect */
1669  if (p->y < v1.y + vt * (v2.y - v1.y))
1670  {
1671  /* a valid crossing of y=p.y right of p.x */
1672  ++cn;
1673  }
1674  }
1675  v1 = v2;
1676  }
1677  }
1678  LWDEBUGF(3, "pt_in_ring_3d returning %d", cn & 1);
1679 
1680  return (cn & 1); /* 0 if even (out), and 1 if odd (in) */
1681 }
1682 
1683 /*------------------------------------------------------------------------------------------------------------
1684 End of Brute force functions
1685 --------------------------------------------------------------------------------------------------------------*/
char * r
Definition: cu_in_wkt.c:24
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
#define LW_FALSE
Definition: liblwgeom.h:94
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
int lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
Definition: lwgeom.c:2135
LWLINE * lwline_from_ptarray(int32_t srid, uint32_t npoints, LWPOINT **points)
Definition: lwline.c:228
#define LW_FAILURE
Definition: liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
#define LINETYPE
Definition: liblwgeom.h:103
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:529
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
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:1097
#define POLYGONTYPE
Definition: liblwgeom.h:104
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:1997
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:170
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:743
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:755
#define TRIANGLETYPE
Definition: liblwgeom.h:115
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:233
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
#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:753
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:203
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:1274
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:1564
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:959
int lw_dist3d_line_tri(LWLINE *line, LWTRIANGLE *tri, DISTPTS3D *dl)
line to triangle calculation
Definition: measures3d.c:817
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:915
int lw_dist3d_point_point(LWPOINT *point1, LWPOINT *point2, DISTPTS3D *dl)
point to point calculation
Definition: measures3d.c:702
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:1136
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:1448
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:561
int lw_dist3d_point_tri(LWPOINT *point, LWTRIANGLE *tri, DISTPTS3D *dl)
Definition: measures3d.c:767
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:1248
int lw_dist3d_poly_tri(LWPOLY *poly, LWTRIANGLE *tri, DISTPTS3D *dl)
polygon to triangle calculation
Definition: measures3d.c:874
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
Definition: measures3d.c:1029
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:1045
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:989
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:833
int lw_dist3d_ptarray_tri(POINTARRAY *pa, LWTRIANGLE *tri, PLANE3D *plane, DISTPTS3D *dl)
Computes pointarray to triangle distance.
Definition: measures3d.c:1370
int lw_dist3d_line_line(LWLINE *line1, LWLINE *line2, DISTPTS3D *dl)
line to line calculation
Definition: measures3d.c:789
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:1290
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:741
int lw_dist3d_point_line(LWPOINT *point, LWLINE *line, DISTPTS3D *dl)
point to line calculation
Definition: measures3d.c:718
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:1521
int lw_dist3d_line_poly(LWLINE *line, LWPOLY *poly, DISTPTS3D *dl)
line to polygon calculation
Definition: measures3d.c:800
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:1085
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:357
double zmax
Definition: liblwgeom.h:359
double xmax
Definition: liblwgeom.h:355
double zmin
Definition: liblwgeom.h:358
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
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
lwflags_t flags
Definition: liblwgeom.h:461
POINTARRAY * points
Definition: liblwgeom.h:483
POINTARRAY * point
Definition: liblwgeom.h:471
POINTARRAY ** rings
Definition: liblwgeom.h:519
uint32_t nrings
Definition: liblwgeom.h:524
POINTARRAY * points
Definition: liblwgeom.h:495
POINT3DZ pop
Definition: measures3d.h:57
VECTOR3D pv
Definition: measures3d.h:58
double y
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:390
double z
Definition: liblwgeom.h:396
double x
Definition: liblwgeom.h:396
double y
Definition: liblwgeom.h:396
double z
Definition: liblwgeom.h:402
double x
Definition: liblwgeom.h:402
double y
Definition: liblwgeom.h:402
double z
Definition: liblwgeom.h:414
uint32_t npoints
Definition: liblwgeom.h:427
double z
Definition: measures3d.h:52
double x
Definition: measures3d.h:52
double y
Definition: measures3d.h:52