PostGIS 3.0.6dev-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#if PARANOIA_LEVEL > 0
331 if (!(ordinate == 'X' || ordinate == 'Y' || ordinate == 'Z' || ordinate == 'M'))
332 {
333 lwerror("Cannot interpolate over %c ordinate.", ordinate);
334 return LW_FAILURE;
335 }
336
337 if (FP_MIN(p1_value, p2_value) > interpolation_value || FP_MAX(p1_value, p2_value) < interpolation_value)
338 {
339 lwerror("Cannot interpolate to a value (%g) not between the input points (%g, %g).",
340 interpolation_value,
341 p1_value,
342 p2_value);
343 return LW_FAILURE;
344 }
345#endif
346
347 proportion = (interpolation_value - p1_value) / (p2_value - p1_value);
348
349 for (i = 0; i < 4; i++)
350 {
351 if (dims[i] == 'Z' && !hasz)
352 continue;
353 if (dims[i] == 'M' && !hasm)
354 continue;
355 if (dims[i] == ordinate)
356 lwpoint_set_ordinate(p, dims[i], interpolation_value);
357 else
358 {
359 double newordinate = 0.0;
360 p1_value = lwpoint_get_ordinate(p1, dims[i]);
361 p2_value = lwpoint_get_ordinate(p2, dims[i]);
362 newordinate = p1_value + proportion * (p2_value - p1_value);
363 lwpoint_set_ordinate(p, dims[i], newordinate);
364 }
365 }
366
367 return LW_SUCCESS;
368}
369
373static inline LWCOLLECTION *
374lwpoint_clip_to_ordinate_range(const LWPOINT *point, char ordinate, double from, double to)
375{
376 LWCOLLECTION *lwgeom_out = NULL;
377 char hasz, hasm;
378 POINT4D p4d;
379 double ordinate_value;
380
381 /* Read Z/M info */
382 hasz = lwgeom_has_z(lwpoint_as_lwgeom(point));
383 hasm = lwgeom_has_m(lwpoint_as_lwgeom(point));
384
385 /* Prepare return object */
386 lwgeom_out = lwcollection_construct_empty(MULTIPOINTTYPE, point->srid, hasz, hasm);
387
388 /* Test if ordinate is in range */
389 lwpoint_getPoint4d_p(point, &p4d);
390 ordinate_value = lwpoint_get_ordinate(&p4d, ordinate);
391 if (from <= ordinate_value && to >= ordinate_value)
392 {
393 LWPOINT *lwp = lwpoint_clone(point);
395 }
396
397 return lwgeom_out;
398}
399
403static inline LWCOLLECTION *
404lwmpoint_clip_to_ordinate_range(const LWMPOINT *mpoint, char ordinate, double from, double to)
405{
406 LWCOLLECTION *lwgeom_out = NULL;
407 char hasz, hasm;
408 uint32_t i;
409
410 /* Read Z/M info */
411 hasz = lwgeom_has_z(lwmpoint_as_lwgeom(mpoint));
412 hasm = lwgeom_has_m(lwmpoint_as_lwgeom(mpoint));
413
414 /* Prepare return object */
415 lwgeom_out = lwcollection_construct_empty(MULTIPOINTTYPE, mpoint->srid, hasz, hasm);
416
417 /* For each point, is its ordinate value between from and to? */
418 for (i = 0; i < mpoint->ngeoms; i++)
419 {
420 POINT4D p4d;
421 double ordinate_value;
422
423 lwpoint_getPoint4d_p(mpoint->geoms[i], &p4d);
424 ordinate_value = lwpoint_get_ordinate(&p4d, ordinate);
425
426 if (from <= ordinate_value && to >= ordinate_value)
427 {
428 LWPOINT *lwp = lwpoint_clone(mpoint->geoms[i]);
430 }
431 }
432
433 /* Set the bbox, if necessary */
434 if (mpoint->bbox)
435 lwgeom_refresh_bbox((LWGEOM *)lwgeom_out);
436
437 return lwgeom_out;
438}
439
440static inline POINTARRAY *
441ptarray_clamp_to_ordinate_range(const POINTARRAY *ipa, char ordinate, double from, double to, uint8_t is_closed)
442{
443 POINT4D p1, p2;
444 POINTARRAY *opa;
445 double ovp1, ovp2;
446 POINT4D *t;
447 int8_t p1out, p2out; /* -1 - smaller than from, 0 - in range, 1 - larger than to */
448 uint32_t i;
449 uint8_t hasz = FLAGS_GET_Z(ipa->flags);
450 uint8_t hasm = FLAGS_GET_M(ipa->flags);
451
452 assert(from <= to);
453
454 t = lwalloc(sizeof(POINT4D));
455
456 /* Initial storage */
457 opa = ptarray_construct_empty(hasz, hasm, ipa->npoints);
458
459 /* Add first point */
460 getPoint4d_p(ipa, 0, &p1);
461 ovp1 = lwpoint_get_ordinate(&p1, ordinate);
462
463 p1out = (ovp1 < from) ? -1 : ((ovp1 > to) ? 1 : 0);
464
465 if (from <= ovp1 && ovp1 <= to)
467
468 /* Loop on all other input points */
469 for (i = 1; i < ipa->npoints; i++)
470 {
471 getPoint4d_p(ipa, i, &p2);
472 ovp2 = lwpoint_get_ordinate(&p2, ordinate);
473 p2out = (ovp2 < from) ? -1 : ((ovp2 > to) ? 1 : 0);
474
475 if (p1out == 0 && p2out == 0) /* both visible */
476 {
478 }
479 else if (p1out == p2out && p1out != 0) /* both invisible on the same side */
480 {
481 /* skip */
482 }
483 else if (p1out == -1 && p2out == 0)
484 {
485 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from);
488 }
489 else if (p1out == -1 && p2out == 1)
490 {
491 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from);
493 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to);
495 }
496 else if (p1out == 0 && p2out == -1)
497 {
498 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from);
500 }
501 else if (p1out == 0 && p2out == 1)
502 {
503 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to);
505 }
506 else if (p1out == 1 && p2out == -1)
507 {
508 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to);
510 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, from);
512 }
513 else if (p1out == 1 && p2out == 0)
514 {
515 point_interpolate(&p1, &p2, t, hasz, hasm, ordinate, to);
518 }
519
520 p1 = p2;
521 p1out = p2out;
522 LW_ON_INTERRUPT(ptarray_free(opa); return NULL);
523 }
524
525 if (is_closed && opa->npoints > 2)
526 {
527 getPoint4d_p(opa, 0, &p1);
529 }
530 lwfree(t);
531
532 return opa;
533}
534
539static inline LWCOLLECTION *
540lwline_clip_to_ordinate_range(const LWLINE *line, char ordinate, double from, double to)
541{
542 POINTARRAY *pa_in = NULL;
543 LWCOLLECTION *lwgeom_out = NULL;
544 POINTARRAY *dp = NULL;
545 uint32_t i;
546 int added_last_point = 0;
547 POINT4D *p = NULL, *q = NULL, *r = NULL;
548 double ordinate_value_p = 0.0, ordinate_value_q = 0.0;
549 char hasz, hasm;
550 char dims;
551
552 /* Null input, nothing we can do. */
553 assert(line);
554 hasz = lwgeom_has_z(lwline_as_lwgeom(line));
555 hasm = lwgeom_has_m(lwline_as_lwgeom(line));
556 dims = FLAGS_NDIMS(line->flags);
557
558 /* Asking for an ordinate we don't have. Error. */
559 if ((ordinate == 'Z' && !hasz) || (ordinate == 'M' && !hasm))
560 {
561 lwerror("Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims);
562 return NULL;
563 }
564
565 /* Prepare our working point objects. */
566 p = lwalloc(sizeof(POINT4D));
567 q = lwalloc(sizeof(POINT4D));
568 r = lwalloc(sizeof(POINT4D));
569
570 /* Construct a collection to hold our outputs. */
571 lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, line->srid, hasz, hasm);
572
573 /* Get our input point array */
574 pa_in = line->points;
575
576 for (i = 0; i < pa_in->npoints; i++)
577 {
578 if (i > 0)
579 {
580 *q = *p;
581 ordinate_value_q = ordinate_value_p;
582 }
583 getPoint4d_p(pa_in, i, p);
584 ordinate_value_p = lwpoint_get_ordinate(p, ordinate);
585
586 /* Is this point inside the ordinate range? Yes. */
587 if (ordinate_value_p >= from && ordinate_value_p <= to)
588 {
589
590 if (!added_last_point)
591 {
592 /* We didn't add the previous point, so this is a new segment.
593 * Make a new point array. */
594 dp = ptarray_construct_empty(hasz, hasm, 32);
595
596 /* We're transiting into the range so add an interpolated
597 * point at the range boundary.
598 * If we're on a boundary and crossing from the far side,
599 * we also need an interpolated point. */
600 if (i > 0 &&
601 (/* Don't try to interpolate if this is the first point */
602 (ordinate_value_p > from && ordinate_value_p < to) || /* Inside */
603 (ordinate_value_p == from && ordinate_value_q > to) || /* Hopping from above */
604 (ordinate_value_p == to && ordinate_value_q < from))) /* Hopping from below */
605 {
606 double interpolation_value;
607 (ordinate_value_q > to) ? (interpolation_value = to)
608 : (interpolation_value = from);
609 point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value);
611 }
612 }
613 /* Add the current vertex to the point array. */
615 if (ordinate_value_p == from || ordinate_value_p == to)
616 added_last_point = 2; /* Added on boundary. */
617 else
618 added_last_point = 1; /* Added inside range. */
619 }
620 /* Is this point inside the ordinate range? No. */
621 else
622 {
623 if (added_last_point == 1)
624 {
625 /* We're transiting out of the range, so add an interpolated point
626 * to the point array at the range boundary. */
627 double interpolation_value;
628 (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from);
629 point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value);
631 }
632 else if (added_last_point == 2)
633 {
634 /* We're out and the last point was on the boundary.
635 * If the last point was the near boundary, nothing to do.
636 * If it was the far boundary, we need an interpolated point. */
637 if (from != to && ((ordinate_value_q == from && ordinate_value_p > from) ||
638 (ordinate_value_q == to && ordinate_value_p < to)))
639 {
640 double interpolation_value;
641 (ordinate_value_p > to) ? (interpolation_value = to)
642 : (interpolation_value = from);
643 point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value);
645 }
646 }
647 else if (i && ordinate_value_q < from && ordinate_value_p > to)
648 {
649 /* We just hopped over the whole range, from bottom to top,
650 * so we need to add *two* interpolated points! */
651 dp = ptarray_construct(hasz, hasm, 2);
652 /* Interpolate lower point. */
653 point_interpolate(p, q, r, hasz, hasm, ordinate, from);
654 ptarray_set_point4d(dp, 0, r);
655 /* Interpolate upper point. */
656 point_interpolate(p, q, r, hasz, hasm, ordinate, to);
657 ptarray_set_point4d(dp, 1, r);
658 }
659 else if (i && ordinate_value_q > to && ordinate_value_p < from)
660 {
661 /* We just hopped over the whole range, from top to bottom,
662 * so we need to add *two* interpolated points! */
663 dp = ptarray_construct(hasz, hasm, 2);
664 /* Interpolate upper point. */
665 point_interpolate(p, q, r, hasz, hasm, ordinate, to);
666 ptarray_set_point4d(dp, 0, r);
667 /* Interpolate lower point. */
668 point_interpolate(p, q, r, hasz, hasm, ordinate, from);
669 ptarray_set_point4d(dp, 1, r);
670 }
671 /* We have an extant point-array, save it out to a multi-line. */
672 if (dp)
673 {
674 /* Only one point, so we have to make an lwpoint to hold this
675 * and set the overall output type to a generic collection. */
676 if (dp->npoints == 1)
677 {
678 LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp);
679 lwgeom_out->type = COLLECTIONTYPE;
680 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint));
681 }
682 else
683 {
684 LWLINE *oline = lwline_construct(line->srid, NULL, dp);
685 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline));
686 }
687
688 /* Pointarray is now owned by lwgeom_out, so drop reference to it */
689 dp = NULL;
690 }
691 added_last_point = 0;
692 }
693 }
694
695 /* Still some points left to be saved out. */
696 if (dp)
697 {
698 if (dp->npoints == 1)
699 {
700 LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp);
701 lwgeom_out->type = COLLECTIONTYPE;
702 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint));
703 }
704 else if (dp->npoints > 1)
705 {
706 LWLINE *oline = lwline_construct(line->srid, NULL, dp);
707 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline));
708 }
709 else
710 ptarray_free(dp);
711 }
712
713 lwfree(p);
714 lwfree(q);
715 lwfree(r);
716
717 if (line->bbox && lwgeom_out->ngeoms > 0)
718 lwgeom_refresh_bbox((LWGEOM *)lwgeom_out);
719
720 return lwgeom_out;
721}
722
726static inline LWCOLLECTION *
727lwpoly_clip_to_ordinate_range(const LWPOLY *poly, char ordinate, double from, double to)
728{
729 assert(poly);
730 char hasz = FLAGS_GET_Z(poly->flags), hasm = FLAGS_GET_M(poly->flags);
731 LWPOLY *poly_res = lwpoly_construct_empty(poly->srid, hasz, hasm);
732 LWCOLLECTION *lwgeom_out = lwcollection_construct_empty(MULTIPOLYGONTYPE, poly->srid, hasz, hasm);
733
734 for (uint32_t i = 0; i < poly->nrings; i++)
735 {
736 /* Ret number of points */
737 POINTARRAY *pa = ptarray_clamp_to_ordinate_range(poly->rings[i], ordinate, from, to, LW_TRUE);
738
739 if (pa->npoints >= 4)
740 lwpoly_add_ring(poly_res, pa);
741 else
742 {
743 ptarray_free(pa);
744 if (i == 0)
745 break;
746 }
747 }
748 if (poly_res->nrings > 0)
749 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, (LWGEOM *)poly_res);
750 else
751 lwpoly_free(poly_res);
752
753 return lwgeom_out;
754}
755
759static inline LWCOLLECTION *
760lwtriangle_clip_to_ordinate_range(const LWTRIANGLE *tri, char ordinate, double from, double to)
761{
762 assert(tri);
763 char hasz = FLAGS_GET_Z(tri->flags), hasm = FLAGS_GET_M(tri->flags);
764 LWCOLLECTION *lwgeom_out = lwcollection_construct_empty(TINTYPE, tri->srid, hasz, hasm);
765 POINTARRAY *pa = ptarray_clamp_to_ordinate_range(tri->points, ordinate, from, to, LW_TRUE);
766
767 if (pa->npoints >= 4)
768 {
769 POINT4D first = getPoint4d(pa, 0);
770 for (uint32_t i = 1; i < pa->npoints - 2; i++)
771 {
772 POINT4D p;
773 POINTARRAY *tpa = ptarray_construct_empty(hasz, hasm, 4);
774 ptarray_append_point(tpa, &first, LW_TRUE);
775 getPoint4d_p(pa, i, &p);
777 getPoint4d_p(pa, i + 1, &p);
779 ptarray_append_point(tpa, &first, LW_TRUE);
780 LWTRIANGLE *otri = lwtriangle_construct(tri->srid, NULL, tpa);
781 lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, (LWGEOM *)otri);
782 }
783 }
784 ptarray_free(pa);
785 return lwgeom_out;
786}
787
791static inline LWCOLLECTION *
792lwcollection_clip_to_ordinate_range(const LWCOLLECTION *icol, char ordinate, double from, double to)
793{
794 LWCOLLECTION *lwgeom_out;
795
796 assert(icol);
797 if (icol->ngeoms == 1)
798 lwgeom_out = lwgeom_clip_to_ordinate_range(icol->geoms[0], ordinate, from, to, 0);
799 else
800 {
801 LWCOLLECTION *col;
802 char hasz = lwgeom_has_z(lwcollection_as_lwgeom(icol));
803 char hasm = lwgeom_has_m(lwcollection_as_lwgeom(icol));
804 uint32_t i;
805 lwgeom_out = lwcollection_construct_empty(icol->type, icol->srid, hasz, hasm);
806 FLAGS_SET_Z(lwgeom_out->flags, hasz);
807 FLAGS_SET_M(lwgeom_out->flags, hasm);
808 for (i = 0; i < icol->ngeoms; i++)
809 {
810 col = lwgeom_clip_to_ordinate_range(icol->geoms[i], ordinate, from, to, 0);
811 if (col)
812 {
813 if (col->type != icol->type)
814 lwgeom_out->type = COLLECTIONTYPE;
815 lwgeom_out = lwcollection_concat_in_place(lwgeom_out, col);
816 lwfree(col->geoms);
818 }
819 }
820 }
821
822 if (icol->bbox)
823 lwgeom_refresh_bbox((LWGEOM *)lwgeom_out);
824
825 return lwgeom_out;
826}
827
829lwgeom_clip_to_ordinate_range(const LWGEOM *lwin, char ordinate, double from, double to, double offset)
830{
831 LWCOLLECTION *out_col;
832 LWCOLLECTION *out_offset;
833 uint32_t i;
834
835 /* Ensure 'from' is less than 'to'. */
836 if (to < from)
837 {
838 double t = from;
839 from = to;
840 to = t;
841 }
842
843 if (!lwin)
844 lwerror("lwgeom_clip_to_ordinate_range: null input geometry!");
845
846 switch (lwin->type)
847 {
848 case LINETYPE:
849 out_col = lwline_clip_to_ordinate_range((LWLINE *)lwin, ordinate, from, to);
850 break;
851 case MULTIPOINTTYPE:
852 out_col = lwmpoint_clip_to_ordinate_range((LWMPOINT *)lwin, ordinate, from, to);
853 break;
854 case POINTTYPE:
855 out_col = lwpoint_clip_to_ordinate_range((LWPOINT *)lwin, ordinate, from, to);
856 break;
857 case POLYGONTYPE:
858 out_col = lwpoly_clip_to_ordinate_range((LWPOLY *)lwin, ordinate, from, to);
859 break;
860 case TRIANGLETYPE:
861 out_col = lwtriangle_clip_to_ordinate_range((LWTRIANGLE *)lwin, ordinate, from, to);
862 break;
863 case TINTYPE:
864 case MULTILINETYPE:
865 case MULTIPOLYGONTYPE:
866 case COLLECTIONTYPE:
868 out_col = lwcollection_clip_to_ordinate_range((LWCOLLECTION *)lwin, ordinate, from, to);
869 break;
870 default:
871 lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type));
872 return NULL;
873 }
874
875 /* Stop if result is NULL */
876 if (!out_col)
877 lwerror("lwgeom_clip_to_ordinate_range clipping routine returned NULL");
878
879 /* Return if we aren't going to offset the result */
880 if (FP_IS_ZERO(offset) || lwgeom_is_empty(lwcollection_as_lwgeom(out_col)))
881 return out_col;
882
883 /* Construct a collection to hold our outputs. */
884 /* Things get ugly: GEOS offset drops Z's and M's so we have to drop ours */
885 out_offset = lwcollection_construct_empty(MULTILINETYPE, lwin->srid, 0, 0);
886
887 /* Try and offset the linear portions of the return value */
888 for (i = 0; i < out_col->ngeoms; i++)
889 {
890 int type = out_col->geoms[i]->type;
891 if (type == POINTTYPE)
892 {
893 lwnotice("lwgeom_clip_to_ordinate_range cannot offset a clipped point");
894 continue;
895 }
896 else if (type == LINETYPE)
897 {
898 /* lwgeom_offsetcurve(line, offset, quadsegs, joinstyle (round), mitrelimit) */
899 LWGEOM *lwoff = lwgeom_offsetcurve(out_col->geoms[i], offset, 8, 1, 5.0);
900 if (!lwoff)
901 {
902 lwerror("lwgeom_offsetcurve returned null");
903 }
904 lwcollection_add_lwgeom(out_offset, lwoff);
905 }
906 else
907 {
908 lwerror("lwgeom_clip_to_ordinate_range found an unexpected type (%s) in the offset routine",
909 lwtype_name(type));
910 }
911 }
912
913 return out_offset;
914}
915
917lwgeom_locate_between(const LWGEOM *lwin, double from, double to, double offset)
918{
919 if (!lwgeom_has_m(lwin))
920 lwerror("Input geometry does not have a measure dimension");
921
922 return lwgeom_clip_to_ordinate_range(lwin, 'M', from, to, offset);
923}
924
925double
926lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt)
927{
928 POINT4D p, p_proj;
929 double ret = 0.0;
930
931 if (!lwin)
932 lwerror("lwgeom_interpolate_point: null input geometry!");
933
934 if (!lwgeom_has_m(lwin))
935 lwerror("Input geometry does not have a measure dimension");
936
937 if (lwgeom_is_empty(lwin) || lwpoint_is_empty(lwpt))
938 lwerror("Input geometry is empty");
939
940 switch (lwin->type)
941 {
942 case LINETYPE:
943 {
944 LWLINE *lwline = lwgeom_as_lwline(lwin);
945 lwpoint_getPoint4d_p(lwpt, &p);
946 ret = ptarray_locate_point(lwline->points, &p, NULL, &p_proj);
947 ret = p_proj.m;
948 break;
949 }
950 default:
951 lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type));
952 }
953 return ret;
954}
955
956/*
957 * Time of closest point of approach
958 *
959 * Given two vectors (p1-p2 and q1-q2) and
960 * a time range (t1-t2) return the time in which
961 * a point p is closest to a point q on their
962 * respective vectors, and the actual points
963 *
964 * Here we use algorithm from softsurfer.com
965 * that can be found here
966 * http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
967 *
968 * @param p0 start of first segment, will be set to actual
969 * closest point of approach on segment.
970 * @param p1 end of first segment
971 * @param q0 start of second segment, will be set to actual
972 * closest point of approach on segment.
973 * @param q1 end of second segment
974 * @param t0 start of travel time
975 * @param t1 end of travel time
976 *
977 * @return time of closest point of approach
978 *
979 */
980static double
981segments_tcpa(POINT4D *p0, const POINT4D *p1, POINT4D *q0, const POINT4D *q1, double t0, double t1)
982{
983 POINT3DZ pv; /* velocity of p, aka u */
984 POINT3DZ qv; /* velocity of q, aka v */
985 POINT3DZ dv; /* velocity difference */
986 POINT3DZ w0; /* vector between first points */
987
988 /*
989 lwnotice("FROM %g,%g,%g,%g -- %g,%g,%g,%g",
990 p0->x, p0->y, p0->z, p0->m,
991 p1->x, p1->y, p1->z, p1->m);
992 lwnotice(" TO %g,%g,%g,%g -- %g,%g,%g,%g",
993 q0->x, q0->y, q0->z, q0->m,
994 q1->x, q1->y, q1->z, q1->m);
995 */
996
997 /* PV aka U */
998 pv.x = (p1->x - p0->x);
999 pv.y = (p1->y - p0->y);
1000 pv.z = (p1->z - p0->z);
1001 /*lwnotice("PV: %g, %g, %g", pv.x, pv.y, pv.z);*/
1002
1003 /* QV aka V */
1004 qv.x = (q1->x - q0->x);
1005 qv.y = (q1->y - q0->y);
1006 qv.z = (q1->z - q0->z);
1007 /*lwnotice("QV: %g, %g, %g", qv.x, qv.y, qv.z);*/
1008
1009 dv.x = pv.x - qv.x;
1010 dv.y = pv.y - qv.y;
1011 dv.z = pv.z - qv.z;
1012 /*lwnotice("DV: %g, %g, %g", dv.x, dv.y, dv.z);*/
1013
1014 double dv2 = DOT(dv, dv);
1015 /*lwnotice("DOT: %g", dv2);*/
1016
1017 if (dv2 == 0.0)
1018 {
1019 /* Distance is the same at any time, we pick the earliest */
1020 return t0;
1021 }
1022
1023 /* Distance at any given time, with t0 */
1024 w0.x = (p0->x - q0->x);
1025 w0.y = (p0->y - q0->y);
1026 w0.z = (p0->z - q0->z);
1027
1028 /*lwnotice("W0: %g, %g, %g", w0.x, w0.y, w0.z);*/
1029
1030 /* Check that at distance dt w0 is distance */
1031
1032 /* This is the fraction of measure difference */
1033 double t = -DOT(w0, dv) / dv2;
1034 /*lwnotice("CLOSEST TIME (fraction): %g", t);*/
1035
1036 if (t > 1.0)
1037 {
1038 /* Getting closer as we move to the end */
1039 /*lwnotice("Converging");*/
1040 t = 1;
1041 }
1042 else if (t < 0.0)
1043 {
1044 /*lwnotice("Diverging");*/
1045 t = 0;
1046 }
1047
1048 /* Interpolate the actual points now */
1049
1050 p0->x += pv.x * t;
1051 p0->y += pv.y * t;
1052 p0->z += pv.z * t;
1053
1054 q0->x += qv.x * t;
1055 q0->y += qv.y * t;
1056 q0->z += qv.z * t;
1057
1058 t = t0 + (t1 - t0) * t;
1059 /*lwnotice("CLOSEST TIME (real): %g", t);*/
1060
1061 return t;
1062}
1063
1064static int
1065ptarray_collect_mvals(const POINTARRAY *pa, double tmin, double tmax, double *mvals)
1066{
1067 POINT4D pbuf;
1068 uint32_t i, n = 0;
1069 for (i = 0; i < pa->npoints; ++i)
1070 {
1071 getPoint4d_p(pa, i, &pbuf); /* could be optimized */
1072 if (pbuf.m >= tmin && pbuf.m <= tmax)
1073 mvals[n++] = pbuf.m;
1074 }
1075 return n;
1076}
1077
1078static int
1079compare_double(const void *pa, const void *pb)
1080{
1081 double a = *((double *)pa);
1082 double b = *((double *)pb);
1083 if (a < b)
1084 return -1;
1085 else if (a > b)
1086 return 1;
1087 else
1088 return 0;
1089}
1090
1091/* Return number of elements in unique array */
1092static int
1093uniq(double *vals, int nvals)
1094{
1095 int i, last = 0;
1096 for (i = 1; i < nvals; ++i)
1097 {
1098 // lwnotice("(I%d):%g", i, vals[i]);
1099 if (vals[i] != vals[last])
1100 {
1101 vals[++last] = vals[i];
1102 // lwnotice("(O%d):%g", last, vals[last]);
1103 }
1104 }
1105 return last + 1;
1106}
1107
1108/*
1109 * Find point at a given measure
1110 *
1111 * The function assumes measures are linear so that always a single point
1112 * is returned for a single measure.
1113 *
1114 * @param pa the point array to perform search on
1115 * @param m the measure to search for
1116 * @param p the point to write result into
1117 * @param from the segment number to start from
1118 *
1119 * @return the segment number the point was found into
1120 * or -1 if given measure was out of the known range.
1121 */
1122static int
1123ptarray_locate_along_linear(const POINTARRAY *pa, double m, POINT4D *p, uint32_t from)
1124{
1125 uint32_t i = from;
1126 POINT4D p1, p2;
1127
1128 /* Walk through each segment in the point array */
1129 getPoint4d_p(pa, i, &p1);
1130 for (i = from + 1; i < pa->npoints; i++)
1131 {
1132 getPoint4d_p(pa, i, &p2);
1133
1134 if (segment_locate_along(&p1, &p2, m, 0, p) == LW_TRUE)
1135 return i - 1; /* found */
1136
1137 p1 = p2;
1138 }
1139
1140 return -1; /* not found */
1141}
1142
1143double
1144lwgeom_tcpa(const LWGEOM *g1, const LWGEOM *g2, double *mindist)
1145{
1146 LWLINE *l1, *l2;
1147 int i;
1148 GBOX gbox1, gbox2;
1149 double tmin, tmax;
1150 double *mvals;
1151 int nmvals = 0;
1152 double mintime;
1153 double mindist2 = FLT_MAX; /* minimum distance, squared */
1154
1155 if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2))
1156 {
1157 lwerror("Both input geometries must have a measure dimension");
1158 return -1;
1159 }
1160
1161 l1 = lwgeom_as_lwline(g1);
1162 l2 = lwgeom_as_lwline(g2);
1163
1164 if (!l1 || !l2)
1165 {
1166 lwerror("Both input geometries must be linestrings");
1167 return -1;
1168 }
1169
1170 if (l1->points->npoints < 2 || l2->points->npoints < 2)
1171 {
1172 lwerror("Both input lines must have at least 2 points");
1173 return -1;
1174 }
1175
1176 /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */
1177 /* because we cannot afford the float rounding inaccuracy when */
1178 /* we compare the ranges for overlap below */
1179 lwgeom_calculate_gbox(g1, &gbox1);
1180 lwgeom_calculate_gbox(g2, &gbox2);
1181
1182 /*
1183 * Find overlapping M range
1184 * WARNING: may be larger than the real one
1185 */
1186
1187 tmin = FP_MAX(gbox1.mmin, gbox2.mmin);
1188 tmax = FP_MIN(gbox1.mmax, gbox2.mmax);
1189
1190 if (tmax < tmin)
1191 {
1192 LWDEBUG(1, "Inputs never exist at the same time");
1193 return -2;
1194 }
1195
1196 // lwnotice("Min:%g, Max:%g", tmin, tmax);
1197
1198 /*
1199 * Collect M values in common time range from inputs
1200 */
1201
1202 mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints));
1203
1204 /* TODO: also clip the lines ? */
1205 nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals);
1206 nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals);
1207
1208 /* Sort values in ascending order */
1209 qsort(mvals, nmvals, sizeof(double), compare_double);
1210
1211 /* Remove duplicated values */
1212 nmvals = uniq(mvals, nmvals);
1213
1214 if (nmvals < 2)
1215 {
1216 {
1217 /* there's a single time, must be that one... */
1218 double t0 = mvals[0];
1219 POINT4D p0, p1;
1220 LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0);
1221 if (mindist)
1222 {
1223 if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0))
1224 {
1225 lwfree(mvals);
1226 lwerror("Could not find point with M=%g on first geom", t0);
1227 return -1;
1228 }
1229 if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0))
1230 {
1231 lwfree(mvals);
1232 lwerror("Could not find point with M=%g on second geom", t0);
1233 return -1;
1234 }
1235 *mindist = distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1);
1236 }
1237 lwfree(mvals);
1238 return t0;
1239 }
1240 }
1241
1242 /*
1243 * For each consecutive pair of measures, compute time of closest point
1244 * approach and actual distance between points at that time
1245 */
1246 mintime = tmin;
1247 for (i = 1; i < nmvals; ++i)
1248 {
1249 double t0 = mvals[i - 1];
1250 double t1 = mvals[i];
1251 double t;
1252 POINT4D p0, p1, q0, q1;
1253 int seg;
1254 double dist2;
1255
1256 // lwnotice("T %g-%g", t0, t1);
1257
1258 seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0);
1259 if (-1 == seg)
1260 continue; /* possible, if GBOX is approximated */
1261 // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z);
1262
1263 seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg);
1264 if (-1 == seg)
1265 continue; /* possible, if GBOX is approximated */
1266 // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z);
1267
1268 seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0);
1269 if (-1 == seg)
1270 continue; /* possible, if GBOX is approximated */
1271 // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z);
1272
1273 seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg);
1274 if (-1 == seg)
1275 continue; /* possible, if GBOX is approximated */
1276 // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z);
1277
1278 t = segments_tcpa(&p0, &p1, &q0, &q1, t0, t1);
1279
1280 /*
1281 lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g",
1282 p0.x, p0.y, p0.z,
1283 q0.x, q0.y, q0.z, t);
1284 */
1285
1286 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);
1287 if (dist2 < mindist2)
1288 {
1289 mindist2 = dist2;
1290 mintime = t;
1291 // lwnotice("MINTIME: %g", mintime);
1292 }
1293 }
1294
1295 /*
1296 * Release memory
1297 */
1298
1299 lwfree(mvals);
1300
1301 if (mindist)
1302 {
1303 *mindist = sqrt(mindist2);
1304 }
1305 /*lwnotice("MINDIST: %g", sqrt(mindist2));*/
1306
1307 return mintime;
1308}
1309
1310int
1311lwgeom_cpa_within(const LWGEOM *g1, const LWGEOM *g2, double maxdist)
1312{
1313 LWLINE *l1, *l2;
1314 int i;
1315 GBOX gbox1, gbox2;
1316 double tmin, tmax;
1317 double *mvals;
1318 int nmvals = 0;
1319 double maxdist2 = maxdist * maxdist;
1320 int within = LW_FALSE;
1321
1322 if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2))
1323 {
1324 lwerror("Both input geometries must have a measure dimension");
1325 return LW_FALSE;
1326 }
1327
1328 l1 = lwgeom_as_lwline(g1);
1329 l2 = lwgeom_as_lwline(g2);
1330
1331 if (!l1 || !l2)
1332 {
1333 lwerror("Both input geometries must be linestrings");
1334 return LW_FALSE;
1335 }
1336
1337 if (l1->points->npoints < 2 || l2->points->npoints < 2)
1338 {
1339 /* TODO: return distance between these two points */
1340 lwerror("Both input lines must have at least 2 points");
1341 return LW_FALSE;
1342 }
1343
1344 /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */
1345 /* because we cannot afford the float rounding inaccuracy when */
1346 /* we compare the ranges for overlap below */
1347 lwgeom_calculate_gbox(g1, &gbox1);
1348 lwgeom_calculate_gbox(g2, &gbox2);
1349
1350 /*
1351 * Find overlapping M range
1352 * WARNING: may be larger than the real one
1353 */
1354
1355 tmin = FP_MAX(gbox1.mmin, gbox2.mmin);
1356 tmax = FP_MIN(gbox1.mmax, gbox2.mmax);
1357
1358 if (tmax < tmin)
1359 {
1360 LWDEBUG(1, "Inputs never exist at the same time");
1361 return LW_FALSE;
1362 }
1363
1364 /*
1365 * Collect M values in common time range from inputs
1366 */
1367
1368 mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints));
1369
1370 /* TODO: also clip the lines ? */
1371 nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals);
1372 nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals);
1373
1374 /* Sort values in ascending order */
1375 qsort(mvals, nmvals, sizeof(double), compare_double);
1376
1377 /* Remove duplicated values */
1378 nmvals = uniq(mvals, nmvals);
1379
1380 if (nmvals < 2)
1381 {
1382 /* there's a single time, must be that one... */
1383 double t0 = mvals[0];
1384 POINT4D p0, p1;
1385 LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0);
1386 if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0))
1387 {
1388 lwnotice("Could not find point with M=%g on first geom", t0);
1389 return LW_FALSE;
1390 }
1391 if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0))
1392 {
1393 lwnotice("Could not find point with M=%g on second geom", t0);
1394 return LW_FALSE;
1395 }
1396 if (distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1) <= maxdist)
1397 within = LW_TRUE;
1398 lwfree(mvals);
1399 return within;
1400 }
1401
1402 /*
1403 * For each consecutive pair of measures, compute time of closest point
1404 * approach and actual distance between points at that time
1405 */
1406 for (i = 1; i < nmvals; ++i)
1407 {
1408 double t0 = mvals[i - 1];
1409 double t1 = mvals[i];
1410#if POSTGIS_DEBUG_LEVEL >= 1
1411 double t;
1412#endif
1413 POINT4D p0, p1, q0, q1;
1414 int seg;
1415 double dist2;
1416
1417 // lwnotice("T %g-%g", t0, t1);
1418
1419 seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0);
1420 if (-1 == seg)
1421 continue; /* possible, if GBOX is approximated */
1422 // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z);
1423
1424 seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg);
1425 if (-1 == seg)
1426 continue; /* possible, if GBOX is approximated */
1427 // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z);
1428
1429 seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0);
1430 if (-1 == seg)
1431 continue; /* possible, if GBOX is approximated */
1432 // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z);
1433
1434 seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg);
1435 if (-1 == seg)
1436 continue; /* possible, if GBOX is approximated */
1437 // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z);
1438
1439#if POSTGIS_DEBUG_LEVEL >= 1
1440 t =
1441#endif
1442 segments_tcpa(&p0, &p1, &q0, &q1, t0, t1);
1443
1444 /*
1445 lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g",
1446 p0.x, p0.y, p0.z,
1447 q0.x, q0.y, q0.z, t);
1448 */
1449
1450 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);
1451 if (dist2 <= maxdist2)
1452 {
1453 LWDEBUGF(1, "Within distance %g at time %g, breaking", sqrt(dist2), t);
1454 within = LW_TRUE;
1455 break;
1456 }
1457 }
1458
1459 /*
1460 * Release memory
1461 */
1462
1463 lwfree(mvals);
1464
1465 return within;
1466}
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:286
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition lwgeom.c:689
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:108
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:326
#define LW_FALSE
Definition liblwgeom.h:108
#define COLLECTIONTYPE
Definition liblwgeom.h:122
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition lwgeom.c:909
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:379
void lwmpoint_free(LWMPOINT *mpt)
Definition lwmpoint.c:72
double lwpoint_get_m(const LWPOINT *point)
Definition lwpoint.c:107
#define LW_FAILURE
Definition liblwgeom.h:110
#define MULTILINETYPE
Definition liblwgeom.h:120
#define LINETYPE
Definition liblwgeom.h:117
#define LW_SUCCESS
Definition liblwgeom.h:111
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:119
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:1311
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:916
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:116
#define FLAGS_GET_Z(flags)
Definition liblwgeom.h:179
void * lwalloc(size_t size)
Definition lwutil.c:227
#define TINTYPE
Definition liblwgeom.h:130
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition lwline.c:42
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:121
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
void lwfree(void *mem)
Definition lwutil.c:242
#define FLAGS_NDIMS(flags)
Definition liblwgeom.h:193
#define POLYGONTYPE
Definition liblwgeom.h:118
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:321
#define __attribute__(x)
Definition liblwgeom.h:242
#define POLYHEDRALSURFACETYPE
Definition liblwgeom.h:128
void lwcollection_release(LWCOLLECTION *lwcollection)
#define FLAGS_GET_M(flags)
Definition liblwgeom.h:180
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:281
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:737
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:161
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:129
void lwpoly_free(LWPOLY *poly)
Definition lwpoly.c:175
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:107
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:187
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:923
#define FLAGS_SET_Z(flags, value)
Definition liblwgeom.h:186
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition lwgeom_api.c:376
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:291
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)
#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
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
Datum within(PG_FUNCTION_ARGS)
double mmax
Definition liblwgeom.h:347
double mmin
Definition liblwgeom.h:346
lwflags_t flags
Definition liblwgeom.h:563
uint32_t ngeoms
Definition liblwgeom.h:566
uint8_t type
Definition liblwgeom.h:564
GBOX * bbox
Definition liblwgeom.h:560
LWGEOM ** geoms
Definition liblwgeom.h:561
int32_t srid
Definition liblwgeom.h:562
uint8_t type
Definition liblwgeom.h:448
int32_t srid
Definition liblwgeom.h:446
lwflags_t flags
Definition liblwgeom.h:471
GBOX * bbox
Definition liblwgeom.h:468
POINTARRAY * points
Definition liblwgeom.h:469
uint8_t type
Definition liblwgeom.h:472
int32_t srid
Definition liblwgeom.h:470
LWLINE ** geoms
Definition liblwgeom.h:533
uint32_t ngeoms
Definition liblwgeom.h:538
int32_t srid
Definition liblwgeom.h:520
GBOX * bbox
Definition liblwgeom.h:518
uint32_t ngeoms
Definition liblwgeom.h:524
LWPOINT ** geoms
Definition liblwgeom.h:519
uint8_t type
Definition liblwgeom.h:460
int32_t srid
Definition liblwgeom.h:458
POINTARRAY ** rings
Definition liblwgeom.h:505
uint32_t nrings
Definition liblwgeom.h:510
lwflags_t flags
Definition liblwgeom.h:507
int32_t srid
Definition liblwgeom.h:506
int32_t srid
Definition liblwgeom.h:482
lwflags_t flags
Definition liblwgeom.h:483
POINTARRAY * points
Definition liblwgeom.h:481
double z
Definition liblwgeom.h:382
double x
Definition liblwgeom.h:382
double y
Definition liblwgeom.h:382
double m
Definition liblwgeom.h:400
double x
Definition liblwgeom.h:400
double z
Definition liblwgeom.h:400
double y
Definition liblwgeom.h:400
lwflags_t flags
Definition liblwgeom.h:417
uint32_t npoints
Definition liblwgeom.h:413