PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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
33static 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
43static 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
58static LWGEOM *
59create_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
75LWGEOM *
76lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2)
77{
78 return lw_dist3d_distanceline(lw1, lw2, lw1->srid, DIST_MIN);
79}
80
81LWGEOM *
83{
84 return lw_dist3d_distanceline(lw1, lw2, lw1->srid, DIST_MAX);
85}
86
87LWGEOM *
88lwgeom_closest_point_3d(const LWGEOM *lw1, const LWGEOM *lw2)
89{
90 return lw_dist3d_distancepoint(lw1, lw2, lw1->srid, DIST_MIN);
91}
92
96LWGEOM *
97lw_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.");
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
200LWGEOM *
201lw_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.");
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.");
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
299double
300lwgeom_maxdistance3d(const LWGEOM *lw1, const LWGEOM *lw2)
301{
302 return lwgeom_maxdistance3d_tolerance(lw1, lw2, 0.0);
303}
304
309double
310lwgeom_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
334double
335lwgeom_mindistance3d(const LWGEOM *lw1, const LWGEOM *lw2)
336{
337 return lwgeom_mindistance3d_tolerance(lw1, lw2, 0.0);
338}
339
340static inline int
341gbox_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
347static 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
441double
442lwgeom_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/*------------------------------------------------------------------------------------------------------------
473End of Initializing functions
474--------------------------------------------------------------------------------------------------------------*/
475
476/*------------------------------------------------------------------------------------------------------------
477Preprocessing functions
478Functions preparing geometries for distance-calculations
479--------------------------------------------------------------------------------------------------------------*/
480
484int
485lw_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
563int
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/*------------------------------------------------------------------------------------------------------------
692End of Preprocessing functions
693--------------------------------------------------------------------------------------------------------------*/
694
695/*------------------------------------------------------------------------------------------------------------
696Brute force functions
697So far the only way to do 3D-calculations
698--------------------------------------------------------------------------------------------------------------*/
699
704int
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}
720int
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
743int
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 */
769int
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
791int
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
802int
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
819int
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
835int
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
876int
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
917int
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
961int
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
991int
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
1031double
1032distance3d_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
1047int
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
1087int
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
1138int
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
1250int
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
1276int
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
1292int
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
1372int
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 */
1450int
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
1523double
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
1566int
1567pt_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/*------------------------------------------------------------------------------------------------------------
1687End of Brute force functions
1688--------------------------------------------------------------------------------------------------------------*/
char * r
Definition cu_in_wkt.c:24
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
#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
LWPOINT * lwpoint_make3dz(int32_t srid, double x, double y, double z)
Definition lwpoint.c:173
#define LW_FAILURE
Definition liblwgeom.h:110
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
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,...
#define LINETYPE
Definition liblwgeom.h:117
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition lwgeom.c:215
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
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition lwgeom.c:1975
void lwcollection_free(LWCOLLECTION *col)
#define FLAGS_GET_SOLID(flags)
Definition liblwgeom.h:184
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
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
#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
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:107
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
LWLINE * lwline_from_ptarray(int32_t srid, uint32_t npoints, LWPOINT **points)
Definition lwline.c:228
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition lwgeom.c:511
#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:740
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)
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,...
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
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.
static int lwgeom_solid_contains_lwgeom(const LWGEOM *solid, const LWGEOM *g)
Definition measures3d.c:348
int define_plane(POINTARRAY *pa, PLANE3D *pl)
LWGEOM * lwgeom_furthest_line_3d(LWGEOM *lw1, LWGEOM *lw2)
Definition measures3d.c:82
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
LWGEOM * lwgeom_closest_point_3d(const LWGEOM *lw1, const LWGEOM *lw2)
Definition measures3d.c:88
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.
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)
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_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...
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.
int lw_dist3d_line_line(LWLINE *line1, LWLINE *line2, DISTPTS3D *dl)
line to line calculation
Definition measures3d.c:792
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
int lw_dist3d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly, PLANE3D *plane, DISTPTS3D *dl)
Computes pointarray to polygon distance.
static int gbox_contains_3d(const GBOX *g1, const GBOX *g2)
Definition measures3d.c:341
double lwgeom_maxdistance3d(const LWGEOM *lw1, const LWGEOM *lw2)
Function initializing 3d max distance calculation.
Definition measures3d.c:300
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
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.
int lw_dist3d_line_poly(LWLINE *line, LWPOLY *poly, DISTPTS3D *dl)
line to polygon calculation
Definition measures3d.c:803
LWGEOM * lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2)
Definition measures3d.c:76
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
int lw_dist3d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS3D *dl)
Finds all combinations of segments between two pointarrays.
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
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_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
Function initializing shortestline and longestline calculations.
Definition measures.c:81
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
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