PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwlinearreferencing.c
Go to the documentation of this file.
1/**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * PostGIS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * PostGIS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright (C) 2015 Sandro Santilli <strk@kbt.io>
22 * Copyright (C) 2011 Paul Ramsey
23 *
24 **********************************************************************/
25
26#include "liblwgeom_internal.h"
27#include "lwgeom_log.h"
28#include "measures3d.h"
29
30static int
31segment_locate_along(const POINT4D *p1, const POINT4D *p2, double m, double offset, POINT4D *pn)
32{
33 double m1 = p1->m;
34 double m2 = p2->m;
35 double mprop;
36
37 /* M is out of range, no new point generated. */
38 if ((m < FP_MIN(m1, m2)) || (m > FP_MAX(m1, m2)))
39 {
40 return LW_FALSE;
41 }
42
43 if (m1 == m2)
44 {
45 /* Degenerate case: same M on both points.
46 If they are the same point we just return one of them. */
47 if (p4d_same(p1, p2))
48 {
49 *pn = *p1;
50 return LW_TRUE;
51 }
52 /* If the points are different we split the difference */
53 mprop = 0.5;
54 }
55 else
56 {
57 mprop = (m - m1) / (m2 - m1);
58 }
59
60 /* M is in range, new point to be generated. */
61 pn->x = p1->x + (p2->x - p1->x) * mprop;
62 pn->y = p1->y + (p2->y - p1->y) * mprop;
63 pn->z = p1->z + (p2->z - p1->z) * mprop;
64 pn->m = m;
65
66 /* Offset to the left or right, if necessary. */
67 if (offset != 0.0)
68 {
69 double theta = atan2(p2->y - p1->y, p2->x - p1->x);
70 pn->x -= sin(theta) * offset;
71 pn->y += cos(theta) * offset;
72 }
73
74 return LW_TRUE;
75}
76
77static POINTARRAY *
78ptarray_locate_along(const POINTARRAY *pa, double m, double offset)
79{
80 uint32_t i;
81 POINT4D p1, p2, pn;
82 POINTARRAY *dpa = NULL;
83
84 /* Can't do anything with degenerate point arrays */
85 if (!pa || pa->npoints < 2)
86 return NULL;
87
88 /* Walk through each segment in the point array */
89 for (i = 1; i < pa->npoints; i++)
90 {
91 getPoint4d_p(pa, i - 1, &p1);
92 getPoint4d_p(pa, i, &p2);
93
94 /* No derived point? Move to next segment. */
95 if (segment_locate_along(&p1, &p2, m, offset, &pn) == LW_FALSE)
96 continue;
97
98 /* No pointarray, make a fresh one */
99 if (dpa == NULL)
101
102 /* Add our new point to the array */
103 ptarray_append_point(dpa, &pn, 0);
104 }
105
106 return dpa;
107}
108
109static LWMPOINT *
110lwline_locate_along(const LWLINE *lwline, double m, double offset)
111{
112 POINTARRAY *opa = NULL;
113 LWMPOINT *mp = NULL;
114 LWGEOM *lwg = lwline_as_lwgeom(lwline);
115 int hasz, hasm, srid;
116
117 /* Return degenerates upwards */
118 if (!lwline)
119 return NULL;
120
121 /* Create empty return shell */
122 srid = lwgeom_get_srid(lwg);
123 hasz = lwgeom_has_z(lwg);
124 hasm = lwgeom_has_m(lwg);
125
126 if (hasm)
127 {
128 /* Find points along */
129 opa = ptarray_locate_along(lwline->points, m, offset);
130 }
131 else
132 {
133 LWLINE *lwline_measured = lwline_measured_from_lwline(lwline, 0.0, 1.0);
134 opa = ptarray_locate_along(lwline_measured->points, m, offset);
135 lwline_free(lwline_measured);
136 }
137
138 /* Return NULL as EMPTY */
139 if (!opa)
140 return lwmpoint_construct_empty(srid, hasz, hasm);
141
142 /* Convert pointarray into a multipoint */
143 mp = lwmpoint_construct(srid, opa);
144 ptarray_free(opa);
145 return mp;
146}
147
148static LWMPOINT *
149lwmline_locate_along(const LWMLINE *lwmline, double m, double offset)
150{
151 LWMPOINT *lwmpoint = NULL;
152 LWGEOM *lwg = lwmline_as_lwgeom(lwmline);
153 uint32_t i, j;
154
155 /* Return degenerates upwards */
156 if ((!lwmline) || (lwmline->ngeoms < 1))
157 return NULL;
158
159 /* Construct return */
161
162 /* Locate along each sub-line */
163 for (i = 0; i < lwmline->ngeoms; i++)
164 {
165 LWMPOINT *along = lwline_locate_along(lwmline->geoms[i], m, offset);
166 if (along)
167 {
168 if (!lwgeom_is_empty((LWGEOM *)along))
169 {
170 for (j = 0; j < along->ngeoms; j++)
171 {
172 lwmpoint_add_lwpoint(lwmpoint, along->geoms[j]);
173 }
174 }
175 /* Free the containing geometry, but leave the sub-geometries around */
176 along->ngeoms = 0;
177 lwmpoint_free(along);
178 }
179 }
180 return lwmpoint;
181}
182
183static LWMPOINT *
184lwpoint_locate_along(const LWPOINT *lwpoint, double m, __attribute__((__unused__)) double offset)
185{
186 double point_m = lwpoint_get_m(lwpoint);
187 LWGEOM *lwg = lwpoint_as_lwgeom(lwpoint);
189 if (FP_EQUALS(m, point_m))
190 {
192 }
193 return r;
194}
195
196static LWMPOINT *
197lwmpoint_locate_along(const LWMPOINT *lwin, double m, __attribute__((__unused__)) double offset)
198{
199 LWGEOM *lwg = lwmpoint_as_lwgeom(lwin);
200 LWMPOINT *lwout = NULL;
201 uint32_t i;
202
203 /* Construct return */
205
206 for (i = 0; i < lwin->ngeoms; i++)
207 {
208 double point_m = lwpoint_get_m(lwin->geoms[i]);
209 if (FP_EQUALS(m, point_m))
210 {
211 lwmpoint_add_lwpoint(lwout, lwpoint_clone(lwin->geoms[i]));
212 }
213 }
214
215 return lwout;
216}
217
218LWGEOM *
219lwgeom_locate_along(const LWGEOM *lwin, double m, double offset)
220{
221 if (!lwin)
222 return NULL;
223
224 if (!lwgeom_has_m(lwin))
225 lwerror("Input geometry does not have a measure dimension");
226
227 switch (lwin->type)
228 {
229 case POINTTYPE:
230 return (LWGEOM *)lwpoint_locate_along((LWPOINT *)lwin, m, offset);
231 case MULTIPOINTTYPE:
232 return (LWGEOM *)lwmpoint_locate_along((LWMPOINT *)lwin, m, offset);
233 case LINETYPE:
234 return (LWGEOM *)lwline_locate_along((LWLINE *)lwin, m, offset);
235 case MULTILINETYPE:
236 return (LWGEOM *)lwmline_locate_along((LWMLINE *)lwin, m, offset);
237 /* Only line types supported right now */
238 /* TO DO: CurveString, CompoundCurve, MultiCurve */
239 /* TO DO: Point, MultiPoint */
240 default:
241 lwerror("Only linear geometries are supported, %s provided.", lwtype_name(lwin->type));
242 return NULL;
243 }
244 return NULL;
245}
246
254inline double
255lwpoint_get_ordinate(const POINT4D *p, char ordinate)
256{
257 if (!p)
258 {
259 lwerror("Null input geometry.");
260 return 0.0;
261 }
262
263 switch (ordinate)
264 {
265 case 'X':
266 return p->x;
267 case 'Y':
268 return p->y;
269 case 'Z':
270 return p->z;
271 case 'M':
272 return p->m;
273 }
274 lwerror("Cannot extract %c ordinate.", ordinate);
275 return 0.0;
276}
277
282inline void
283lwpoint_set_ordinate(POINT4D *p, char ordinate, double value)
284{
285 if (!p)
286 {
287 lwerror("Null input geometry.");
288 return;
289 }
290
291 switch (ordinate)
292 {
293 case 'X':
294 p->x = value;
295 return;
296 case 'Y':
297 p->y = value;
298 return;
299 case 'Z':
300 p->z = value;
301 return;
302 case 'M':
303 p->m = value;
304 return;
305 }
306 lwerror("Cannot set %c ordinate.", ordinate);
307 return;
308}
309
315inline int
317 const POINT4D *p2,
318 POINT4D *p,
319 int hasz,
320 int hasm,
321 char ordinate,
322 double interpolation_value)
323{
324 static char *dims = "XYZM";
325 double p1_value = lwpoint_get_ordinate(p1, ordinate);
326 double p2_value = lwpoint_get_ordinate(p2, ordinate);
327 double proportion;
328 int i = 0;
329
330 assert(ordinate == 'X' || ordinate == 'Y' ||
331 ordinate == 'Z' || ordinate == 'M');
332 assert(FP_MIN(p1_value, p2_value) <= interpolation_value &&
333 FP_MAX(p1_value, p2_value) >= interpolation_value);
334
335 proportion = (interpolation_value - p1_value) / (p2_value - p1_value);
336
337 for (i = 0; i < 4; i++)
338 {
339 if (dims[i] == 'Z' && !hasz)
340 continue;
341 if (dims[i] == 'M' && !hasm)
342 continue;
343 if (dims[i] == ordinate)
344 lwpoint_set_ordinate(p, dims[i], interpolation_value);
345 else
346 {
347 double newordinate = 0.0;
348 p1_value = lwpoint_get_ordinate(p1, dims[i]);
349 p2_value = lwpoint_get_ordinate(p2, dims[i]);
350 newordinate = p1_value + proportion * (p2_value - p1_value);
351 lwpoint_set_ordinate(p, dims[i], newordinate);
352 }
353 }
354
355 return LW_SUCCESS;
356}
357
361static inline LWCOLLECTION *
362lwpoint_clip_to_ordinate_range(const LWPOINT *point, char ordinate, double from, double to)
363{
364 LWCOLLECTION *lwgeom_out = NULL;
365 char hasz, hasm;
366 POINT4D p4d;
367 double ordinate_value;
368
369 /* Read Z/M info */
370 hasz = lwgeom_has_z(lwpoint_as_lwgeom(point));
371 hasm = lwgeom_has_m(lwpoint_as_lwgeom(point));
372
373 /* Prepare return object */
374 lwgeom_out = lwcollection_construct_empty(MULTIPOINTTYPE, point->srid, hasz, hasm);
375
376 /* Test if ordinate is in range */
377 lwpoint_getPoint4d_p(point, &p4d);
378 ordinate_value = lwpoint_get_ordinate(&p4d, ordinate);
379 if (from <= ordinate_value && to >= ordinate_value)
380 {
381 LWPOINT *lwp = lwpoint_clone(point);
383 }
384
385 return lwgeom_out;
386}
387
391static inline LWCOLLECTION *
392lwmpoint_clip_to_ordinate_range(const LWMPOINT *mpoint, char ordinate, double from, double to)
393{
394 LWCOLLECTION *lwgeom_out = NULL;
395 char hasz, hasm;
396 uint32_t i;
397
398 /* Read Z/M info */
399 hasz = lwgeom_has_z(lwmpoint_as_lwgeom(mpoint));
400 hasm = lwgeom_has_m(lwmpoint_as_lwgeom(mpoint));
401
402 /* Prepare return object */
403 lwgeom_out = lwcollection_construct_empty(MULTIPOINTTYPE, mpoint->srid, hasz, hasm);
404
405 /* For each point, is its ordinate value between from and to? */
406 for (i = 0; i < mpoint->ngeoms; i++)
407 {
408 POINT4D p4d;
409 double ordinate_value;
410
411 lwpoint_getPoint4d_p(mpoint->geoms[i], &p4d);
412 ordinate_value = lwpoint_get_ordinate(&p4d, ordinate);
413
414 if (from <= ordinate_value && to >= ordinate_value)
415 {
416 LWPOINT *lwp = lwpoint_clone(mpoint->geoms[i]);
418 }
419 }
420
421 /* Set the bbox, if necessary */
422 if (mpoint->bbox)
423 lwgeom_refresh_bbox((LWGEOM *)lwgeom_out);
424
425 return lwgeom_out;
426}
427
428static inline POINTARRAY *
429ptarray_clamp_to_ordinate_range(const POINTARRAY *ipa, char ordinate, double from, double to, uint8_t is_closed)
430{
431 POINT4D p1, p2;
432 POINTARRAY *opa;
433 double ovp1, ovp2;
434 POINT4D *t;
435 int8_t p1out, p2out; /* -1 - smaller than from, 0 - in range, 1 - larger than to */
436 uint32_t i;
437 uint8_t hasz = FLAGS_GET_Z(ipa->flags);
438 uint8_t hasm = FLAGS_GET_M(ipa->flags);
439
440 assert(from <= to);
441
442 t = lwalloc(sizeof(POINT4D));
443
444 /* Initial storage */
445 opa = ptarray_construct_empty(hasz, hasm, ipa->npoints);
446
447 /* Add first point */
448 getPoint4d_p(ipa, 0, &p1);
449 ovp1 = lwpoint_get_ordinate(&p1, ordinate);
450
451 p1out = (ovp1 < from) ? -1 : ((ovp1 > to) ? 1 : 0);
452
453 if (from <= ovp1 && ovp1 <= to)
455
456 /* Loop on all other input points */
457 for (i = 1; i < ipa->npoints; i++)
458 {
459 getPoint4d_p(ipa, i, &p2);
460 ovp2 = lwpoint_get_ordinate(&p2, ordinate);
461 p2out = (ovp2 < from) ? -1 : ((ovp2 > to) ? 1 : 0);
462
463 if (p1out == 0 && p2out == 0) /* both visible */
464 {
466 }
467 else if (p1out == p2out && p1out != 0) /* both invisible on the same side */
468 {
469 /* skip */
470 }
471 else if (p1out == -1 && p2out == 0)
472 {
473 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from);
476 }
477 else if (p1out == -1 && p2out == 1)
478 {
479 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from);
481 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to);
483 }
484 else if (p1out == 0 && p2out == -1)
485 {
486 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from);
488 }
489 else if (p1out == 0 && p2out == 1)
490 {
491 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to);
493 }
494 else if (p1out == 1 && p2out == -1)
495 {
496 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to);
498 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from);
500 }
501 else if (p1out == 1 && p2out == 0)
502 {
503 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to);
506 }
507
508 p1 = p2;
509 p1out = p2out;
510 LW_ON_INTERRUPT(ptarray_free(opa); return NULL);
511 }
512
513 if (is_closed && opa->npoints > 2)
514 {
515 getPoint4d_p(opa, 0, &p1);
517 }
518 lwfree(t);
519
520 return opa;
521}
522
527static inline LWCOLLECTION *
528lwline_clip_to_ordinate_range(const LWLINE *line, char ordinate, double from, double to)
529{
530 POINTARRAY *pa_in = NULL;
531 LWCOLLECTION *lwgeom_out = NULL;
532 POINTARRAY *dp = NULL;
533 uint32_t i;
534 int added_last_point = 0;
535 POINT4D *p = NULL, *q = NULL, *r = NULL;
536 double ordinate_value_p = 0.0, ordinate_value_q = 0.0;
537 char hasz, hasm;
538 char dims;
539
540 /* Null input, nothing we can do. */
541 assert(line);
542 hasz = lwgeom_has_z(lwline_as_lwgeom(line));
543 hasm = lwgeom_has_m(lwline_as_lwgeom(line));
544 dims = FLAGS_NDIMS(line->flags);
545
546 /* Asking for an ordinate we don't have. Error. */
547 if ((ordinate == 'Z' && !hasz) || (ordinate == 'M' && !hasm))
548 {
549 lwerror("Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims);
550 return NULL;
551 }
552
553 /* Prepare our working point objects. */
554 p = lwalloc(sizeof(POINT4D));
555 q = lwalloc(sizeof(POINT4D));
556 r = lwalloc(sizeof(POINT4D));
557
558 /* Construct a collection to hold our outputs. */
559 lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, line->srid, hasz, hasm);
560
561 /* Get our input point array */
562 pa_in = line->points;
563
564 for (i = 0; i < pa_in->npoints; i++)
565 {
566 if (i > 0)
567 {
568 *q = *p;
569 ordinate_value_q = ordinate_value_p;
570 }
571 getPoint4d_p(pa_in, i, p);
572 ordinate_value_p = lwpoint_get_ordinate(p, ordinate);
573
574 /* Is this point inside the ordinate range? Yes. */
575 if (ordinate_value_p >= from && ordinate_value_p <= to)
576 {
577
578 if (!added_last_point)
579 {
580 /* We didn't add the previous point, so this is a new segment.
581 * Make a new point array. */
582 dp = ptarray_construct_empty(hasz, hasm, 32);
583
584 /* We're transiting into the range so add an interpolated
585 * point at the range boundary.
586 * If we're on a boundary and crossing from the far side,
587 * we also need an interpolated point. */
588 if (i > 0 &&
589 (/* Don't try to interpolate if this is the first point */
590 (ordinate_value_p > from && ordinate_value_p < to) || /* Inside */
591 (ordinate_value_p == from && ordinate_value_q > to) || /* Hopping from above */
592 (ordinate_value_p == to && ordinate_value_q < from))) /* Hopping from below */
593 {
594 double interpolation_value;
595 (ordinate_value_q > to) ? (interpolation_value = to)
596 : (interpolation_value = from);
597 point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value);
599 }
600 }
601 /* Add the current vertex to the point array. */
603 if (ordinate_value_p == from || ordinate_value_p == to)
604 added_last_point = 2; /* Added on boundary. */
605 else
606 added_last_point = 1; /* Added inside range. */
607 }
608 /* Is this point inside the ordinate range? No. */
609 else
610 {
611 if (added_last_point == 1)
612 {
613 /* We're transiting out of the range, so add an interpolated point
614 * to the point array at the range boundary. */
615 double interpolation_value;
616 (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from);
617 point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value);
619 }
620 else if (added_last_point == 2)
621 {
622 /* We're out and the last point was on the boundary.
623 * If the last point was the near boundary, nothing to do.
624 * If it was the far boundary, we need an interpolated point. */
625 if (from != to && ((ordinate_value_q == from && ordinate_value_p > from) ||
626 (ordinate_value_q == to && ordinate_value_p < to)))
627 {
628 double interpolation_value;
629 (ordinate_value_p > to) ? (interpolation_value = to)
630 : (interpolation_value = from);
631 point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value);
633 }
634 }
635 else if (i && ordinate_value_q < from && ordinate_value_p > to)
636 {
637 /* We just hopped over the whole range, from bottom to top,
638 * so we need to add *two* interpolated points! */
639 dp = ptarray_construct(hasz, hasm, 2);
640 /* Interpolate lower point. */
641 point_interpolate(p, q, r, hasz, hasm, ordinate, from);
642 ptarray_set_point4d(dp, 0, r);
643 /* Interpolate upper point. */
644 point_interpolate(p, q, r, hasz, hasm, ordinate, to);
645 ptarray_set_point4d(dp, 1, r);
646 }
647 else if (i && ordinate_value_q > to && ordinate_value_p < from)
648 {
649 /* We just hopped over the whole range, from top to bottom,
650 * so we need to add *two* interpolated points! */
651 dp = ptarray_construct(hasz, hasm, 2);
652 /* Interpolate upper point. */
653 point_interpolate(p, q, r, hasz, hasm, ordinate, to);
654 ptarray_set_point4d(dp, 0, r);
655 /* Interpolate lower point. */
656 point_interpolate(p, q, r, hasz, hasm, ordinate, from);
657 ptarray_set_point4d(dp, 1, r);
658 }
659 /* We have an extant point-array, save it out to a multi-line. */
660 if (dp)
661 {
662 /* Only one point, so we have to make an lwpoint to hold this
663 * and set the overall output type to a generic collection. */
664 if (dp->npoints == 1)
665 {
666 LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp);
667 lwgeom_out->type = COLLECTIONTYPE;
668 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint));
669 }
670 else
671 {
672 LWLINE *oline = lwline_construct(line->srid, NULL, dp);
673 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline));
674 }
675
676 /* Pointarray is now owned by lwgeom_out, so drop reference to it */
677 dp = NULL;
678 }
679 added_last_point = 0;
680 }
681 }
682
683 /* Still some points left to be saved out. */
684 if (dp)
685 {
686 if (dp->npoints == 1)
687 {
688 LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp);
689 lwgeom_out->type = COLLECTIONTYPE;
690 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint));
691 }
692 else if (dp->npoints > 1)
693 {
694 LWLINE *oline = lwline_construct(line->srid, NULL, dp);
695 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline));
696 }
697 else
698 ptarray_free(dp);
699 }
700
701 lwfree(p);
702 lwfree(q);
703 lwfree(r);
704
705 if (line->bbox && lwgeom_out->ngeoms > 0)
706 lwgeom_refresh_bbox((LWGEOM *)lwgeom_out);
707
708 return lwgeom_out;
709}
710
714static inline LWCOLLECTION *
715lwpoly_clip_to_ordinate_range(const LWPOLY *poly, char ordinate, double from, double to)
716{
717 assert(poly);
718 char hasz = FLAGS_GET_Z(poly->flags), hasm = FLAGS_GET_M(poly->flags);
719 LWPOLY *poly_res = lwpoly_construct_empty(poly->srid, hasz, hasm);
720 LWCOLLECTION *lwgeom_out = lwcollection_construct_empty(MULTIPOLYGONTYPE, poly->srid, hasz, hasm);
721
722 for (uint32_t i = 0; i < poly->nrings; i++)
723 {
724 /* Ret number of points */
725 POINTARRAY *pa = ptarray_clamp_to_ordinate_range(poly->rings[i], ordinate, from, to, LW_TRUE);
726
727 if (!pa)
728 return NULL;
729
730 if (pa->npoints >= 4)
731 lwpoly_add_ring(poly_res, pa);
732 else
733 {
734 ptarray_free(pa);
735 if (i == 0)
736 break;
737 }
738 }
739 if (poly_res->nrings > 0)
740 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, (LWGEOM *)poly_res);
741 else
742 lwpoly_free(poly_res);
743
744 return lwgeom_out;
745}
746
750static inline LWCOLLECTION *
751lwtriangle_clip_to_ordinate_range(const LWTRIANGLE *tri, char ordinate, double from, double to)
752{
753 assert(tri);
754 char hasz = FLAGS_GET_Z(tri->flags), hasm = FLAGS_GET_M(tri->flags);
755 LWCOLLECTION *lwgeom_out = lwcollection_construct_empty(TINTYPE, tri->srid, hasz, hasm);
756 POINTARRAY *pa = ptarray_clamp_to_ordinate_range(tri->points, ordinate, from, to, LW_TRUE);
757
758 if (!pa)
759 return NULL;
760
761 if (pa->npoints >= 4)
762 {
763 POINT4D first = getPoint4d(pa, 0);
764 for (uint32_t i = 1; i < pa->npoints - 2; i++)
765 {
766 POINT4D p;
767 POINTARRAY *tpa = ptarray_construct_empty(hasz, hasm, 4);
768 ptarray_append_point(tpa, &first, LW_TRUE);
769 getPoint4d_p(pa, i, &p);
771 getPoint4d_p(pa, i + 1, &p);
773 ptarray_append_point(tpa, &first, LW_TRUE);
774 LWTRIANGLE *otri = lwtriangle_construct(tri->srid, NULL, tpa);
775 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, (LWGEOM *)otri);
776 }
777 }
778 ptarray_free(pa);
779 return lwgeom_out;
780}
781
785static inline LWCOLLECTION *
786lwcollection_clip_to_ordinate_range(const LWCOLLECTION *icol, char ordinate, double from, double to)
787{
788 LWCOLLECTION *lwgeom_out;
789
790 assert(icol);
791 if (icol->ngeoms == 1)
792 lwgeom_out = lwgeom_clip_to_ordinate_range(icol->geoms[0], ordinate, from, to, 0);
793 else
794 {
795 LWCOLLECTION *col;
796 char hasz = lwgeom_has_z(lwcollection_as_lwgeom(icol));
797 char hasm = lwgeom_has_m(lwcollection_as_lwgeom(icol));
798 uint32_t i;
799 lwgeom_out = lwcollection_construct_empty(icol->type, icol->srid, hasz, hasm);
800 FLAGS_SET_Z(lwgeom_out->flags, hasz);
801 FLAGS_SET_M(lwgeom_out->flags, hasm);
802 for (i = 0; i < icol->ngeoms; i++)
803 {
804 col = lwgeom_clip_to_ordinate_range(icol->geoms[i], ordinate, from, to, 0);
805 if (col)
806 {
807 if (col->type != icol->type)
808 lwgeom_out->type = COLLECTIONTYPE;
809 lwgeom_out = lwcollection_concat_in_place(lwgeom_out, col);
810 lwfree(col->geoms);
812 }
813 }
814 }
815
816 if (icol->bbox)
817 lwgeom_refresh_bbox((LWGEOM *)lwgeom_out);
818
819 return lwgeom_out;
820}
821
823lwgeom_clip_to_ordinate_range(const LWGEOM *lwin, char ordinate, double from, double to, double offset)
824{
825 LWCOLLECTION *out_col;
826 LWCOLLECTION *out_offset;
827 uint32_t i;
828
829 /* Ensure 'from' is less than 'to'. */
830 if (to < from)
831 {
832 double t = from;
833 from = to;
834 to = t;
835 }
836
837 if (!lwin)
838 lwerror("lwgeom_clip_to_ordinate_range: null input geometry!");
839
840 switch (lwin->type)
841 {
842 case LINETYPE:
843 out_col = lwline_clip_to_ordinate_range((LWLINE *)lwin, ordinate, from, to);
844 break;
845 case MULTIPOINTTYPE:
846 out_col = lwmpoint_clip_to_ordinate_range((LWMPOINT *)lwin, ordinate, from, to);
847 break;
848 case POINTTYPE:
849 out_col = lwpoint_clip_to_ordinate_range((LWPOINT *)lwin, ordinate, from, to);
850 break;
851 case POLYGONTYPE:
852 out_col = lwpoly_clip_to_ordinate_range((LWPOLY *)lwin, ordinate, from, to);
853 break;
854 case TRIANGLETYPE:
855 out_col = lwtriangle_clip_to_ordinate_range((LWTRIANGLE *)lwin, ordinate, from, to);
856 break;
857 case TINTYPE:
858 case MULTILINETYPE:
859 case MULTIPOLYGONTYPE:
860 case COLLECTIONTYPE:
862 out_col = lwcollection_clip_to_ordinate_range((LWCOLLECTION *)lwin, ordinate, from, to);
863 break;
864 default:
865 lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type));
866 return NULL;
867 }
868
869 /* Stop if result is NULL */
870 if (!out_col)
871 lwerror("lwgeom_clip_to_ordinate_range clipping routine returned NULL");
872
873 /* Return if we aren't going to offset the result */
874 if (FP_IS_ZERO(offset) || lwgeom_is_empty(lwcollection_as_lwgeom(out_col)))
875 return out_col;
876
877 /* Construct a collection to hold our outputs. */
878 /* Things get ugly: GEOS offset drops Z's and M's so we have to drop ours */
879 out_offset = lwcollection_construct_empty(MULTILINETYPE, lwin->srid, 0, 0);
880
881 /* Try and offset the linear portions of the return value */
882 for (i = 0; i < out_col->ngeoms; i++)
883 {
884 int type = out_col->geoms[i]->type;
885 if (type == POINTTYPE)
886 {
887 lwnotice("lwgeom_clip_to_ordinate_range cannot offset a clipped point");
888 continue;
889 }
890 else if (type == LINETYPE)
891 {
892 /* lwgeom_offsetcurve(line, offset, quadsegs, joinstyle (round), mitrelimit) */
893 LWGEOM *lwoff = lwgeom_offsetcurve(out_col->geoms[i], offset, 8, 1, 5.0);
894 if (!lwoff)
895 {
896 lwerror("lwgeom_offsetcurve returned null");
897 }
898 lwcollection_add_lwgeom(out_offset, lwoff);
899 }
900 else
901 {
902 lwerror("lwgeom_clip_to_ordinate_range found an unexpected type (%s) in the offset routine",
903 lwtype_name(type));
904 }
905 }
906
907 return out_offset;
908}
909
911lwgeom_locate_between(const LWGEOM *lwin, double from, double to, double offset)
912{
913 if (!lwgeom_has_m(lwin))
914 lwerror("Input geometry does not have a measure dimension");
915
916 return lwgeom_clip_to_ordinate_range(lwin, 'M', from, to, offset);
917}
918
919double
920lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt)
921{
922 POINT4D p, p_proj;
923 double ret = 0.0;
924
925 if (!lwin)
926 lwerror("lwgeom_interpolate_point: null input geometry!");
927
928 if (!lwgeom_has_m(lwin))
929 lwerror("Input geometry does not have a measure dimension");
930
931 if (lwgeom_is_empty(lwin) || lwpoint_is_empty(lwpt))
932 lwerror("Input geometry is empty");
933
934 switch (lwin->type)
935 {
936 case LINETYPE:
937 {
938 LWLINE *lwline = lwgeom_as_lwline(lwin);
939 lwpoint_getPoint4d_p(lwpt, &p);
940 ret = ptarray_locate_point(lwline->points, &p, NULL, &p_proj);
941 ret = p_proj.m;
942 break;
943 }
944 default:
945 lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type));
946 }
947 return ret;
948}
949
950/*
951 * Time of closest point of approach
952 *
953 * Given two vectors (p1-p2 and q1-q2) and
954 * a time range (t1-t2) return the time in which
955 * a point p is closest to a point q on their
956 * respective vectors, and the actual points
957 *
958 * Here we use algorithm from softsurfer.com
959 * that can be found here
960 * http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
961 *
962 * @param p0 start of first segment, will be set to actual
963 * closest point of approach on segment.
964 * @param p1 end of first segment
965 * @param q0 start of second segment, will be set to actual
966 * closest point of approach on segment.
967 * @param q1 end of second segment
968 * @param t0 start of travel time
969 * @param t1 end of travel time
970 *
971 * @return time of closest point of approach
972 *
973 */
974static double
975segments_tcpa(POINT4D *p0, const POINT4D *p1, POINT4D *q0, const POINT4D *q1, double t0, double t1)
976{
977 POINT3DZ pv; /* velocity of p, aka u */
978 POINT3DZ qv; /* velocity of q, aka v */
979 POINT3DZ dv; /* velocity difference */
980 POINT3DZ w0; /* vector between first points */
981
982 /*
983 lwnotice("FROM %g,%g,%g,%g -- %g,%g,%g,%g",
984 p0->x, p0->y, p0->z, p0->m,
985 p1->x, p1->y, p1->z, p1->m);
986 lwnotice(" TO %g,%g,%g,%g -- %g,%g,%g,%g",
987 q0->x, q0->y, q0->z, q0->m,
988 q1->x, q1->y, q1->z, q1->m);
989 */
990
991 /* PV aka U */
992 pv.x = (p1->x - p0->x);
993 pv.y = (p1->y - p0->y);
994 pv.z = (p1->z - p0->z);
995 /*lwnotice("PV: %g, %g, %g", pv.x, pv.y, pv.z);*/
996
997 /* QV aka V */
998 qv.x = (q1->x - q0->x);
999 qv.y = (q1->y - q0->y);
1000 qv.z = (q1->z - q0->z);
1001 /*lwnotice("QV: %g, %g, %g", qv.x, qv.y, qv.z);*/
1002
1003 dv.x = pv.x - qv.x;
1004 dv.y = pv.y - qv.y;
1005 dv.z = pv.z - qv.z;
1006 /*lwnotice("DV: %g, %g, %g", dv.x, dv.y, dv.z);*/
1007
1008 double dv2 = DOT(dv, dv);
1009 /*lwnotice("DOT: %g", dv2);*/
1010
1011 if (dv2 == 0.0)
1012 {
1013 /* Distance is the same at any time, we pick the earliest */
1014 return t0;
1015 }
1016
1017 /* Distance at any given time, with t0 */
1018 w0.x = (p0->x - q0->x);
1019 w0.y = (p0->y - q0->y);
1020 w0.z = (p0->z - q0->z);
1021
1022 /*lwnotice("W0: %g, %g, %g", w0.x, w0.y, w0.z);*/
1023
1024 /* Check that at distance dt w0 is distance */
1025
1026 /* This is the fraction of measure difference */
1027 double t = -DOT(w0, dv) / dv2;
1028 /*lwnotice("CLOSEST TIME (fraction): %g", t);*/
1029
1030 if (t > 1.0)
1031 {
1032 /* Getting closer as we move to the end */
1033 /*lwnotice("Converging");*/
1034 t = 1;
1035 }
1036 else if (t < 0.0)
1037 {
1038 /*lwnotice("Diverging");*/
1039 t = 0;
1040 }
1041
1042 /* Interpolate the actual points now */
1043
1044 p0->x += pv.x * t;
1045 p0->y += pv.y * t;
1046 p0->z += pv.z * t;
1047
1048 q0->x += qv.x * t;
1049 q0->y += qv.y * t;
1050 q0->z += qv.z * t;
1051
1052 t = t0 + (t1 - t0) * t;
1053 /*lwnotice("CLOSEST TIME (real): %g", t);*/
1054
1055 return t;
1056}
1057
1058static int
1059ptarray_collect_mvals(const POINTARRAY *pa, double tmin, double tmax, double *mvals)
1060{
1061 POINT4D pbuf;
1062 uint32_t i, n = 0;
1063 for (i = 0; i < pa->npoints; ++i)
1064 {
1065 getPoint4d_p(pa, i, &pbuf); /* could be optimized */
1066 if (pbuf.m >= tmin && pbuf.m <= tmax)
1067 mvals[n++] = pbuf.m;
1068 }
1069 return n;
1070}
1071
1072static int
1073compare_double(const void *pa, const void *pb)
1074{
1075 double a = *((double *)pa);
1076 double b = *((double *)pb);
1077 if (a < b)
1078 return -1;
1079 else if (a > b)
1080 return 1;
1081 else
1082 return 0;
1083}
1084
1085/* Return number of elements in unique array */
1086static int
1087uniq(double *vals, int nvals)
1088{
1089 int i, last = 0;
1090 for (i = 1; i < nvals; ++i)
1091 {
1092 // lwnotice("(I%d):%g", i, vals[i]);
1093 if (vals[i] != vals[last])
1094 {
1095 vals[++last] = vals[i];
1096 // lwnotice("(O%d):%g", last, vals[last]);
1097 }
1098 }
1099 return last + 1;
1100}
1101
1102/*
1103 * Find point at a given measure
1104 *
1105 * The function assumes measures are linear so that always a single point
1106 * is returned for a single measure.
1107 *
1108 * @param pa the point array to perform search on
1109 * @param m the measure to search for
1110 * @param p the point to write result into
1111 * @param from the segment number to start from
1112 *
1113 * @return the segment number the point was found into
1114 * or -1 if given measure was out of the known range.
1115 */
1116static int
1117ptarray_locate_along_linear(const POINTARRAY *pa, double m, POINT4D *p, uint32_t from)
1118{
1119 uint32_t i = from;
1120 POINT4D p1, p2;
1121
1122 /* Walk through each segment in the point array */
1123 getPoint4d_p(pa, i, &p1);
1124 for (i = from + 1; i < pa->npoints; i++)
1125 {
1126 getPoint4d_p(pa, i, &p2);
1127
1128 if (segment_locate_along(&p1, &p2, m, 0, p) == LW_TRUE)
1129 return i - 1; /* found */
1130
1131 p1 = p2;
1132 }
1133
1134 return -1; /* not found */
1135}
1136
1137double
1138lwgeom_tcpa(const LWGEOM *g1, const LWGEOM *g2, double *mindist)
1139{
1140 LWLINE *l1, *l2;
1141 int i;
1142 GBOX gbox1, gbox2;
1143 double tmin, tmax;
1144 double *mvals;
1145 int nmvals = 0;
1146 double mintime;
1147 double mindist2 = FLT_MAX; /* minimum distance, squared */
1148
1149 if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2))
1150 {
1151 lwerror("Both input geometries must have a measure dimension");
1152 return -1;
1153 }
1154
1155 l1 = lwgeom_as_lwline(g1);
1156 l2 = lwgeom_as_lwline(g2);
1157
1158 if (!l1 || !l2)
1159 {
1160 lwerror("Both input geometries must be linestrings");
1161 return -1;
1162 }
1163
1164 if (l1->points->npoints < 2 || l2->points->npoints < 2)
1165 {
1166 lwerror("Both input lines must have at least 2 points");
1167 return -1;
1168 }
1169
1170 /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */
1171 /* because we cannot afford the float rounding inaccuracy when */
1172 /* we compare the ranges for overlap below */
1173 lwgeom_calculate_gbox(g1, &gbox1);
1174 lwgeom_calculate_gbox(g2, &gbox2);
1175
1176 /*
1177 * Find overlapping M range
1178 * WARNING: may be larger than the real one
1179 */
1180
1181 tmin = FP_MAX(gbox1.mmin, gbox2.mmin);
1182 tmax = FP_MIN(gbox1.mmax, gbox2.mmax);
1183
1184 if (tmax < tmin)
1185 {
1186 LWDEBUG(1, "Inputs never exist at the same time");
1187 return -2;
1188 }
1189
1190 // lwnotice("Min:%g, Max:%g", tmin, tmax);
1191
1192 /*
1193 * Collect M values in common time range from inputs
1194 */
1195
1196 mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints));
1197
1198 /* TODO: also clip the lines ? */
1199 nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals);
1200 nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals);
1201
1202 /* Sort values in ascending order */
1203 qsort(mvals, nmvals, sizeof(double), compare_double);
1204
1205 /* Remove duplicated values */
1206 nmvals = uniq(mvals, nmvals);
1207
1208 if (nmvals < 2)
1209 {
1210 {
1211 /* there's a single time, must be that one... */
1212 double t0 = mvals[0];
1213 POINT4D p0, p1;
1214 LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0);
1215 if (mindist)
1216 {
1217 if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0))
1218 {
1219 lwfree(mvals);
1220 lwerror("Could not find point with M=%g on first geom", t0);
1221 return -1;
1222 }
1223 if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0))
1224 {
1225 lwfree(mvals);
1226 lwerror("Could not find point with M=%g on second geom", t0);
1227 return -1;
1228 }
1229 *mindist = distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1);
1230 }
1231 lwfree(mvals);
1232 return t0;
1233 }
1234 }
1235
1236 /*
1237 * For each consecutive pair of measures, compute time of closest point
1238 * approach and actual distance between points at that time
1239 */
1240 mintime = tmin;
1241 for (i = 1; i < nmvals; ++i)
1242 {
1243 double t0 = mvals[i - 1];
1244 double t1 = mvals[i];
1245 double t;
1246 POINT4D p0, p1, q0, q1;
1247 int seg;
1248 double dist2;
1249
1250 // lwnotice("T %g-%g", t0, t1);
1251
1252 seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0);
1253 if (-1 == seg)
1254 continue; /* possible, if GBOX is approximated */
1255 // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z);
1256
1257 seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg);
1258 if (-1 == seg)
1259 continue; /* possible, if GBOX is approximated */
1260 // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z);
1261
1262 seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0);
1263 if (-1 == seg)
1264 continue; /* possible, if GBOX is approximated */
1265 // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z);
1266
1267 seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg);
1268 if (-1 == seg)
1269 continue; /* possible, if GBOX is approximated */
1270 // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z);
1271
1272 t = segments_tcpa(&p0, &p1, &q0, &q1, t0, t1);
1273
1274 /*
1275 lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g",
1276 p0.x, p0.y, p0.z,
1277 q0.x, q0.y, q0.z, t);
1278 */
1279
1280 dist2 = (q0.x - p0.x) * (q0.x - p0.x) + (q0.y - p0.y) * (q0.y - p0.y) + (q0.z - p0.z) * (q0.z - p0.z);
1281 if (dist2 < mindist2)
1282 {
1283 mindist2 = dist2;
1284 mintime = t;
1285 // lwnotice("MINTIME: %g", mintime);
1286 }
1287 }
1288
1289 /*
1290 * Release memory
1291 */
1292
1293 lwfree(mvals);
1294
1295 if (mindist)
1296 {
1297 *mindist = sqrt(mindist2);
1298 }
1299 /*lwnotice("MINDIST: %g", sqrt(mindist2));*/
1300
1301 return mintime;
1302}
1303
1304int
1305lwgeom_cpa_within(const LWGEOM *g1, const LWGEOM *g2, double maxdist)
1306{
1307 LWLINE *l1, *l2;
1308 int i;
1309 GBOX gbox1, gbox2;
1310 double tmin, tmax;
1311 double *mvals;
1312 int nmvals = 0;
1313 double maxdist2 = maxdist * maxdist;
1314 int within = LW_FALSE;
1315
1316 if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2))
1317 {
1318 lwerror("Both input geometries must have a measure dimension");
1319 return LW_FALSE;
1320 }
1321
1322 l1 = lwgeom_as_lwline(g1);
1323 l2 = lwgeom_as_lwline(g2);
1324
1325 if (!l1 || !l2)
1326 {
1327 lwerror("Both input geometries must be linestrings");
1328 return LW_FALSE;
1329 }
1330
1331 if (l1->points->npoints < 2 || l2->points->npoints < 2)
1332 {
1333 /* TODO: return distance between these two points */
1334 lwerror("Both input lines must have at least 2 points");
1335 return LW_FALSE;
1336 }
1337
1338 /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */
1339 /* because we cannot afford the float rounding inaccuracy when */
1340 /* we compare the ranges for overlap below */
1341 lwgeom_calculate_gbox(g1, &gbox1);
1342 lwgeom_calculate_gbox(g2, &gbox2);
1343
1344 /*
1345 * Find overlapping M range
1346 * WARNING: may be larger than the real one
1347 */
1348
1349 tmin = FP_MAX(gbox1.mmin, gbox2.mmin);
1350 tmax = FP_MIN(gbox1.mmax, gbox2.mmax);
1351
1352 if (tmax < tmin)
1353 {
1354 LWDEBUG(1, "Inputs never exist at the same time");
1355 return LW_FALSE;
1356 }
1357
1358 /*
1359 * Collect M values in common time range from inputs
1360 */
1361
1362 mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints));
1363
1364 /* TODO: also clip the lines ? */
1365 nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals);
1366 nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals);
1367
1368 /* Sort values in ascending order */
1369 qsort(mvals, nmvals, sizeof(double), compare_double);
1370
1371 /* Remove duplicated values */
1372 nmvals = uniq(mvals, nmvals);
1373
1374 if (nmvals < 2)
1375 {
1376 /* there's a single time, must be that one... */
1377 double t0 = mvals[0];
1378 POINT4D p0, p1;
1379 LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0);
1380 if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0))
1381 {
1382 lwnotice("Could not find point with M=%g on first geom", t0);
1383 return LW_FALSE;
1384 }
1385 if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0))
1386 {
1387 lwnotice("Could not find point with M=%g on second geom", t0);
1388 return LW_FALSE;
1389 }
1390 if (distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1) <= maxdist)
1391 within = LW_TRUE;
1392 lwfree(mvals);
1393 return within;
1394 }
1395
1396 /*
1397 * For each consecutive pair of measures, compute time of closest point
1398 * approach and actual distance between points at that time
1399 */
1400 for (i = 1; i < nmvals; ++i)
1401 {
1402 double t0 = mvals[i - 1];
1403 double t1 = mvals[i];
1404#if POSTGIS_DEBUG_LEVEL >= 1
1405 double t;
1406#endif
1407 POINT4D p0, p1, q0, q1;
1408 int seg;
1409 double dist2;
1410
1411 // lwnotice("T %g-%g", t0, t1);
1412
1413 seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0);
1414 if (-1 == seg)
1415 continue; /* possible, if GBOX is approximated */
1416 // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z);
1417
1418 seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg);
1419 if (-1 == seg)
1420 continue; /* possible, if GBOX is approximated */
1421 // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z);
1422
1423 seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0);
1424 if (-1 == seg)
1425 continue; /* possible, if GBOX is approximated */
1426 // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z);
1427
1428 seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg);
1429 if (-1 == seg)
1430 continue; /* possible, if GBOX is approximated */
1431 // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z);
1432
1433#if POSTGIS_DEBUG_LEVEL >= 1
1434 t =
1435#endif
1436 segments_tcpa(&p0, &p1, &q0, &q1, t0, t1);
1437
1438 /*
1439 lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g",
1440 p0.x, p0.y, p0.z,
1441 q0.x, q0.y, q0.z, t);
1442 */
1443
1444 dist2 = (q0.x - p0.x) * (q0.x - p0.x) + (q0.y - p0.y) * (q0.y - p0.y) + (q0.z - p0.z) * (q0.z - p0.z);
1445 if (dist2 <= maxdist2)
1446 {
1447 LWDEBUGF(1, "Within distance %g at time %g, breaking", sqrt(dist2), t);
1448 within = LW_TRUE;
1449 break;
1450 }
1451 }
1452
1453 /*
1454 * Release memory
1455 */
1456
1457 lwfree(mvals);
1458
1459 return within;
1460}
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
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition lwgeom.c:332
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition lwgeom.c:735
int lwpoint_getPoint4d_p(const LWPOINT *point, POINT4D *out)
Definition lwpoint.c:57
POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n)
Definition lwgeom_api.c:107
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:372
#define LW_FALSE
Definition liblwgeom.h:94
#define COLLECTIONTYPE
Definition liblwgeom.h:108
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition lwgeom.c:955
LWMPOINT * lwmpoint_construct(int32_t srid, const POINTARRAY *pa)
Definition lwmpoint.c:52
LWLINE * lwline_measured_from_lwline(const LWLINE *lwline, double m_start, double m_end)
Add a measure dimension to a line, interpolating linearly from the start to the end value.
Definition lwline.c:389
void lwmpoint_free(LWMPOINT *mpt)
Definition lwmpoint.c:72
double lwpoint_get_m(const LWPOINT *point)
Definition lwpoint.c:107
#define MULTILINETYPE
Definition liblwgeom.h:106
#define LINETYPE
Definition liblwgeom.h:103
#define LW_SUCCESS
Definition liblwgeom.h:97
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition lwpoint.c:129
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition lwmpoint.c:45
#define MULTIPOINTTYPE
Definition liblwgeom.h:105
int lwpoly_add_ring(LWPOLY *poly, POINTARRAY *pa)
Add a ring, allocating extra space if necessary.
Definition lwpoly.c:247
double ptarray_locate_point(const POINTARRAY *pa, const POINT4D *pt, double *dist, POINT4D *p_located)
Definition ptarray.c:1518
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition ptarray.c:59
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition lwgeom.c:962
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
#define FLAGS_GET_Z(flags)
Definition liblwgeom.h:165
void * lwalloc(size_t size)
Definition lwutil.c:227
#define TINTYPE
Definition liblwgeom.h:116
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
void lwfree(void *mem)
Definition lwutil.c:248
#define FLAGS_NDIMS(flags)
Definition liblwgeom.h:179
#define POLYGONTYPE
Definition liblwgeom.h:104
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:367
#define __attribute__(x)
Definition liblwgeom.h:228
#define POLYHEDRALSURFACETYPE
Definition liblwgeom.h:114
void lwcollection_release(LWCOLLECTION *lwcollection)
#define FLAGS_GET_M(flags)
Definition liblwgeom.h:166
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition lwgeom_api.c:125
LWGEOM * lwmline_as_lwgeom(const LWMLINE *obj)
Definition lwgeom.c:327
void ptarray_free(POINTARRAY *pa)
Definition ptarray.c:327
LWMPOINT * lwmpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition lwmpoint.c:39
LWGEOM * lwgeom_offsetcurve(const LWGEOM *geom, double size, int quadsegs, int joinStyle, double mitreLimit)
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:783
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:207
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_FALSE,...
Definition ptarray.c:147
#define TRIANGLETYPE
Definition liblwgeom.h:115
void lwpoly_free(LWPOLY *poly)
Definition lwpoly.c:175
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:93
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
#define FLAGS_SET_M(flags, value)
Definition liblwgeom.h:173
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition lwpoly.c:161
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition lwgeom.c:969
#define FLAGS_SET_Z(flags, value)
Definition liblwgeom.h:172
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition lwgeom_api.c:369
void lwline_free(LWLINE *line)
Definition lwline.c:67
LWCOLLECTION * lwcollection_concat_in_place(LWCOLLECTION *col1, const LWCOLLECTION *col2)
Appends all geometries from col2 to col1 in place.
LWTRIANGLE * lwtriangle_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwtriangle.c:40
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition lwgeom.c:337
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
Definition ptarray.c:51
LWPOINT * lwpoint_clone(const LWPOINT *lwgeom)
Definition lwpoint.c:239
int p4d_same(const POINT4D *p1, const POINT4D *p2)
Definition lwalgorithm.c:32
#define LW_ON_INTERRUPT(x)
#define FP_MAX(A, B)
#define FP_MIN(A, B)
int lwpoint_is_empty(const LWPOINT *point)
#define FP_EQUALS(A, B)
int ptarray_has_z(const POINTARRAY *pa)
Definition ptarray.c:37
int ptarray_has_m(const POINTARRAY *pa)
Definition ptarray.c:44
#define FP_IS_ZERO(A)
Datum within(PG_FUNCTION_ARGS)
#define LWDEBUG(level, msg)
Definition lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition lwgeom_log.h:106
void lwnotice(const char *fmt,...) __attribute__((format(printf
Write a notice out to the notice handler.
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition lwinline.h:199
void lwpoint_set_ordinate(POINT4D *p, char ordinate, double value)
Given a point, ordinate number and value, set that ordinate on the point.
static LWMPOINT * lwmline_locate_along(const LWMLINE *lwmline, double m, double offset)
static LWMPOINT * lwpoint_locate_along(const LWPOINT *lwpoint, double m, __attribute__((__unused__)) double offset)
static LWCOLLECTION * lwcollection_clip_to_ordinate_range(const LWCOLLECTION *icol, char ordinate, double from, double to)
Clip an input COLLECTION between two values, on any ordinate input.
static POINTARRAY * ptarray_clamp_to_ordinate_range(const POINTARRAY *ipa, char ordinate, double from, double to, uint8_t is_closed)
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,...
double lwpoint_get_ordinate(const POINT4D *p, char ordinate)
Given a POINT4D and an ordinate number, return the value of the ordinate.
static int segment_locate_along(const POINT4D *p1, const POINT4D *p2, double m, double offset, POINT4D *pn)
double lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt)
Find the measure value at the location on the line closest to the point.
static int ptarray_collect_mvals(const POINTARRAY *pa, double tmin, double tmax, double *mvals)
static LWCOLLECTION * lwpoint_clip_to_ordinate_range(const LWPOINT *point, char ordinate, double from, double to)
Clip an input POINT between two values, on any ordinate input.
static LWCOLLECTION * lwline_clip_to_ordinate_range(const LWLINE *line, char ordinate, double from, double to)
Take in a LINESTRING and return a MULTILINESTRING of those portions of the LINESTRING between the fro...
double lwgeom_tcpa(const LWGEOM *g1, const LWGEOM *g2, double *mindist)
Find the time of closest point of approach.
static int compare_double(const void *pa, const void *pb)
static LWCOLLECTION * lwtriangle_clip_to_ordinate_range(const LWTRIANGLE *tri, char ordinate, double from, double to)
Clip an input LWTRIANGLE between two values, on any ordinate input.
static double segments_tcpa(POINT4D *p0, const POINT4D *p1, POINT4D *q0, const POINT4D *q1, double t0, double t1)
static LWCOLLECTION * lwpoly_clip_to_ordinate_range(const LWPOLY *poly, char ordinate, double from, double to)
Clip an input LWPOLY between two values, on any ordinate input.
static POINTARRAY * ptarray_locate_along(const POINTARRAY *pa, double m, double offset)
LWGEOM * lwgeom_locate_along(const LWGEOM *lwin, double m, double offset)
Determine the location(s) along a measured line where m occurs and return as a multipoint.
static LWMPOINT * lwmpoint_locate_along(const LWMPOINT *lwin, double m, __attribute__((__unused__)) double offset)
static LWMPOINT * lwline_locate_along(const LWLINE *lwline, double m, double offset)
static int ptarray_locate_along_linear(const POINTARRAY *pa, double m, POINT4D *p, uint32_t from)
static int uniq(double *vals, int nvals)
LWCOLLECTION * lwgeom_locate_between(const LWGEOM *lwin, double from, double to, double offset)
Determine the segments along a measured line that fall within the m-range given.
int point_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int hasz, int hasm, char ordinate, double interpolation_value)
Given two points, a dimensionality, an ordinate, and an interpolation value generate a new point that...
static LWCOLLECTION * lwmpoint_clip_to_ordinate_range(const LWMPOINT *mpoint, char ordinate, double from, double to)
Clip an input MULTIPOINT between two values, on any ordinate input.
int lwgeom_cpa_within(const LWGEOM *g1, const LWGEOM *g2, double maxdist)
Is the closest point of approach within a distance ?
#define DOT(u, v)
Definition measures3d.h:31
double mmax
Definition liblwgeom.h:361
double mmin
Definition liblwgeom.h:360
lwflags_t flags
Definition liblwgeom.h:577
uint32_t ngeoms
Definition liblwgeom.h:580
uint8_t type
Definition liblwgeom.h:578
GBOX * bbox
Definition liblwgeom.h:574
LWGEOM ** geoms
Definition liblwgeom.h:575
int32_t srid
Definition liblwgeom.h:576
uint8_t type
Definition liblwgeom.h:462
int32_t srid
Definition liblwgeom.h:460
lwflags_t flags
Definition liblwgeom.h:485
GBOX * bbox
Definition liblwgeom.h:482
POINTARRAY * points
Definition liblwgeom.h:483
uint8_t type
Definition liblwgeom.h:486
int32_t srid
Definition liblwgeom.h:484
LWLINE ** geoms
Definition liblwgeom.h:547
uint32_t ngeoms
Definition liblwgeom.h:552
int32_t srid
Definition liblwgeom.h:534
GBOX * bbox
Definition liblwgeom.h:532
uint32_t ngeoms
Definition liblwgeom.h:538
LWPOINT ** geoms
Definition liblwgeom.h:533
uint8_t type
Definition liblwgeom.h:474
int32_t srid
Definition liblwgeom.h:472
POINTARRAY ** rings
Definition liblwgeom.h:519
uint32_t nrings
Definition liblwgeom.h:524
lwflags_t flags
Definition liblwgeom.h:521
int32_t srid
Definition liblwgeom.h:520
int32_t srid
Definition liblwgeom.h:496
lwflags_t flags
Definition liblwgeom.h:497
POINTARRAY * points
Definition liblwgeom.h:495
double z
Definition liblwgeom.h:396
double x
Definition liblwgeom.h:396
double y
Definition liblwgeom.h:396
double m
Definition liblwgeom.h:414
double x
Definition liblwgeom.h:414
double z
Definition liblwgeom.h:414
double y
Definition liblwgeom.h:414
lwflags_t flags
Definition liblwgeom.h:431
uint32_t npoints
Definition liblwgeom.h:427