PostGIS  3.4.0dev-r@@SVN_REVISION@@
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 
30 static int
31 segment_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 
77 static POINTARRAY *
78 ptarray_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 
109 static LWMPOINT *
110 lwline_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 
148 static LWMPOINT *
149 lwmline_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 
183 static LWMPOINT *
184 lwpoint_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 
196 static LWMPOINT *
197 lwmpoint_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 
218 LWGEOM *
219 lwgeom_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 
254 inline double
255 lwpoint_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 
282 inline void
283 lwpoint_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 
315 inline 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 
373 static inline LWCOLLECTION *
374 lwpoint_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);
394  lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(lwp));
395  }
396 
397  return lwgeom_out;
398 }
399 
403 static inline LWCOLLECTION *
404 lwmpoint_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]);
429  lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(lwp));
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 
440 static inline POINTARRAY *
441 ptarray_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)
466  ptarray_append_point(opa, &p1, LW_FALSE);
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  {
477  ptarray_append_point(opa, &p2, LW_FALSE);
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);
487  ptarray_append_point(opa, &p2, LW_FALSE);
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);
517  ptarray_append_point(opa, &p2, LW_FALSE);
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);
528  ptarray_append_point(opa, &p1, LW_FALSE);
529  }
530  lwfree(t);
531 
532  return opa;
533 }
534 
539 static inline LWCOLLECTION *
540 lwline_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 
726 static inline LWCOLLECTION *
727 lwpoly_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)
740  return NULL;
741 
742  if (pa->npoints >= 4)
743  lwpoly_add_ring(poly_res, pa);
744  else
745  {
746  ptarray_free(pa);
747  if (i == 0)
748  break;
749  }
750  }
751  if (poly_res->nrings > 0)
752  lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, (LWGEOM *)poly_res);
753  else
754  lwpoly_free(poly_res);
755 
756  return lwgeom_out;
757 }
758 
762 static inline LWCOLLECTION *
763 lwtriangle_clip_to_ordinate_range(const LWTRIANGLE *tri, char ordinate, double from, double to)
764 {
765  assert(tri);
766  char hasz = FLAGS_GET_Z(tri->flags), hasm = FLAGS_GET_M(tri->flags);
767  LWCOLLECTION *lwgeom_out = lwcollection_construct_empty(TINTYPE, tri->srid, hasz, hasm);
768  POINTARRAY *pa = ptarray_clamp_to_ordinate_range(tri->points, ordinate, from, to, LW_TRUE);
769 
770  if (!pa)
771  return NULL;
772 
773  if (pa->npoints >= 4)
774  {
775  POINT4D first = getPoint4d(pa, 0);
776  for (uint32_t i = 1; i < pa->npoints - 2; i++)
777  {
778  POINT4D p;
779  POINTARRAY *tpa = ptarray_construct_empty(hasz, hasm, 4);
780  ptarray_append_point(tpa, &first, LW_TRUE);
781  getPoint4d_p(pa, i, &p);
782  ptarray_append_point(tpa, &p, LW_TRUE);
783  getPoint4d_p(pa, i + 1, &p);
784  ptarray_append_point(tpa, &p, LW_TRUE);
785  ptarray_append_point(tpa, &first, LW_TRUE);
786  LWTRIANGLE *otri = lwtriangle_construct(tri->srid, NULL, tpa);
787  lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, (LWGEOM *)otri);
788  }
789  }
790  ptarray_free(pa);
791  return lwgeom_out;
792 }
793 
797 static inline LWCOLLECTION *
798 lwcollection_clip_to_ordinate_range(const LWCOLLECTION *icol, char ordinate, double from, double to)
799 {
800  LWCOLLECTION *lwgeom_out;
801 
802  assert(icol);
803  if (icol->ngeoms == 1)
804  lwgeom_out = lwgeom_clip_to_ordinate_range(icol->geoms[0], ordinate, from, to, 0);
805  else
806  {
807  LWCOLLECTION *col;
808  char hasz = lwgeom_has_z(lwcollection_as_lwgeom(icol));
809  char hasm = lwgeom_has_m(lwcollection_as_lwgeom(icol));
810  uint32_t i;
811  lwgeom_out = lwcollection_construct_empty(icol->type, icol->srid, hasz, hasm);
812  FLAGS_SET_Z(lwgeom_out->flags, hasz);
813  FLAGS_SET_M(lwgeom_out->flags, hasm);
814  for (i = 0; i < icol->ngeoms; i++)
815  {
816  col = lwgeom_clip_to_ordinate_range(icol->geoms[i], ordinate, from, to, 0);
817  if (col)
818  {
819  if (col->type != icol->type)
820  lwgeom_out->type = COLLECTIONTYPE;
821  lwgeom_out = lwcollection_concat_in_place(lwgeom_out, col);
822  lwfree(col->geoms);
824  }
825  }
826  }
827 
828  if (icol->bbox)
829  lwgeom_refresh_bbox((LWGEOM *)lwgeom_out);
830 
831  return lwgeom_out;
832 }
833 
834 LWCOLLECTION *
835 lwgeom_clip_to_ordinate_range(const LWGEOM *lwin, char ordinate, double from, double to, double offset)
836 {
837  LWCOLLECTION *out_col;
838  LWCOLLECTION *out_offset;
839  uint32_t i;
840 
841  /* Ensure 'from' is less than 'to'. */
842  if (to < from)
843  {
844  double t = from;
845  from = to;
846  to = t;
847  }
848 
849  if (!lwin)
850  lwerror("lwgeom_clip_to_ordinate_range: null input geometry!");
851 
852  switch (lwin->type)
853  {
854  case LINETYPE:
855  out_col = lwline_clip_to_ordinate_range((LWLINE *)lwin, ordinate, from, to);
856  break;
857  case MULTIPOINTTYPE:
858  out_col = lwmpoint_clip_to_ordinate_range((LWMPOINT *)lwin, ordinate, from, to);
859  break;
860  case POINTTYPE:
861  out_col = lwpoint_clip_to_ordinate_range((LWPOINT *)lwin, ordinate, from, to);
862  break;
863  case POLYGONTYPE:
864  out_col = lwpoly_clip_to_ordinate_range((LWPOLY *)lwin, ordinate, from, to);
865  break;
866  case TRIANGLETYPE:
867  out_col = lwtriangle_clip_to_ordinate_range((LWTRIANGLE *)lwin, ordinate, from, to);
868  break;
869  case TINTYPE:
870  case MULTILINETYPE:
871  case MULTIPOLYGONTYPE:
872  case COLLECTIONTYPE:
874  out_col = lwcollection_clip_to_ordinate_range((LWCOLLECTION *)lwin, ordinate, from, to);
875  break;
876  default:
877  lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type));
878  return NULL;
879  }
880 
881  /* Stop if result is NULL */
882  if (!out_col)
883  lwerror("lwgeom_clip_to_ordinate_range clipping routine returned NULL");
884 
885  /* Return if we aren't going to offset the result */
886  if (FP_IS_ZERO(offset) || lwgeom_is_empty(lwcollection_as_lwgeom(out_col)))
887  return out_col;
888 
889  /* Construct a collection to hold our outputs. */
890  /* Things get ugly: GEOS offset drops Z's and M's so we have to drop ours */
891  out_offset = lwcollection_construct_empty(MULTILINETYPE, lwin->srid, 0, 0);
892 
893  /* Try and offset the linear portions of the return value */
894  for (i = 0; i < out_col->ngeoms; i++)
895  {
896  int type = out_col->geoms[i]->type;
897  if (type == POINTTYPE)
898  {
899  lwnotice("lwgeom_clip_to_ordinate_range cannot offset a clipped point");
900  continue;
901  }
902  else if (type == LINETYPE)
903  {
904  /* lwgeom_offsetcurve(line, offset, quadsegs, joinstyle (round), mitrelimit) */
905  LWGEOM *lwoff = lwgeom_offsetcurve(out_col->geoms[i], offset, 8, 1, 5.0);
906  if (!lwoff)
907  {
908  lwerror("lwgeom_offsetcurve returned null");
909  }
910  lwcollection_add_lwgeom(out_offset, lwoff);
911  }
912  else
913  {
914  lwerror("lwgeom_clip_to_ordinate_range found an unexpected type (%s) in the offset routine",
915  lwtype_name(type));
916  }
917  }
918 
919  return out_offset;
920 }
921 
922 LWCOLLECTION *
923 lwgeom_locate_between(const LWGEOM *lwin, double from, double to, double offset)
924 {
925  if (!lwgeom_has_m(lwin))
926  lwerror("Input geometry does not have a measure dimension");
927 
928  return lwgeom_clip_to_ordinate_range(lwin, 'M', from, to, offset);
929 }
930 
931 double
932 lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt)
933 {
934  POINT4D p, p_proj;
935  double ret = 0.0;
936 
937  if (!lwin)
938  lwerror("lwgeom_interpolate_point: null input geometry!");
939 
940  if (!lwgeom_has_m(lwin))
941  lwerror("Input geometry does not have a measure dimension");
942 
943  if (lwgeom_is_empty(lwin) || lwpoint_is_empty(lwpt))
944  lwerror("Input geometry is empty");
945 
946  switch (lwin->type)
947  {
948  case LINETYPE:
949  {
950  LWLINE *lwline = lwgeom_as_lwline(lwin);
951  lwpoint_getPoint4d_p(lwpt, &p);
952  ret = ptarray_locate_point(lwline->points, &p, NULL, &p_proj);
953  ret = p_proj.m;
954  break;
955  }
956  default:
957  lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type));
958  }
959  return ret;
960 }
961 
962 /*
963  * Time of closest point of approach
964  *
965  * Given two vectors (p1-p2 and q1-q2) and
966  * a time range (t1-t2) return the time in which
967  * a point p is closest to a point q on their
968  * respective vectors, and the actual points
969  *
970  * Here we use algorithm from softsurfer.com
971  * that can be found here
972  * http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
973  *
974  * @param p0 start of first segment, will be set to actual
975  * closest point of approach on segment.
976  * @param p1 end of first segment
977  * @param q0 start of second segment, will be set to actual
978  * closest point of approach on segment.
979  * @param q1 end of second segment
980  * @param t0 start of travel time
981  * @param t1 end of travel time
982  *
983  * @return time of closest point of approach
984  *
985  */
986 static double
987 segments_tcpa(POINT4D *p0, const POINT4D *p1, POINT4D *q0, const POINT4D *q1, double t0, double t1)
988 {
989  POINT3DZ pv; /* velocity of p, aka u */
990  POINT3DZ qv; /* velocity of q, aka v */
991  POINT3DZ dv; /* velocity difference */
992  POINT3DZ w0; /* vector between first points */
993 
994  /*
995  lwnotice("FROM %g,%g,%g,%g -- %g,%g,%g,%g",
996  p0->x, p0->y, p0->z, p0->m,
997  p1->x, p1->y, p1->z, p1->m);
998  lwnotice(" TO %g,%g,%g,%g -- %g,%g,%g,%g",
999  q0->x, q0->y, q0->z, q0->m,
1000  q1->x, q1->y, q1->z, q1->m);
1001  */
1002 
1003  /* PV aka U */
1004  pv.x = (p1->x - p0->x);
1005  pv.y = (p1->y - p0->y);
1006  pv.z = (p1->z - p0->z);
1007  /*lwnotice("PV: %g, %g, %g", pv.x, pv.y, pv.z);*/
1008 
1009  /* QV aka V */
1010  qv.x = (q1->x - q0->x);
1011  qv.y = (q1->y - q0->y);
1012  qv.z = (q1->z - q0->z);
1013  /*lwnotice("QV: %g, %g, %g", qv.x, qv.y, qv.z);*/
1014 
1015  dv.x = pv.x - qv.x;
1016  dv.y = pv.y - qv.y;
1017  dv.z = pv.z - qv.z;
1018  /*lwnotice("DV: %g, %g, %g", dv.x, dv.y, dv.z);*/
1019 
1020  double dv2 = DOT(dv, dv);
1021  /*lwnotice("DOT: %g", dv2);*/
1022 
1023  if (dv2 == 0.0)
1024  {
1025  /* Distance is the same at any time, we pick the earliest */
1026  return t0;
1027  }
1028 
1029  /* Distance at any given time, with t0 */
1030  w0.x = (p0->x - q0->x);
1031  w0.y = (p0->y - q0->y);
1032  w0.z = (p0->z - q0->z);
1033 
1034  /*lwnotice("W0: %g, %g, %g", w0.x, w0.y, w0.z);*/
1035 
1036  /* Check that at distance dt w0 is distance */
1037 
1038  /* This is the fraction of measure difference */
1039  double t = -DOT(w0, dv) / dv2;
1040  /*lwnotice("CLOSEST TIME (fraction): %g", t);*/
1041 
1042  if (t > 1.0)
1043  {
1044  /* Getting closer as we move to the end */
1045  /*lwnotice("Converging");*/
1046  t = 1;
1047  }
1048  else if (t < 0.0)
1049  {
1050  /*lwnotice("Diverging");*/
1051  t = 0;
1052  }
1053 
1054  /* Interpolate the actual points now */
1055 
1056  p0->x += pv.x * t;
1057  p0->y += pv.y * t;
1058  p0->z += pv.z * t;
1059 
1060  q0->x += qv.x * t;
1061  q0->y += qv.y * t;
1062  q0->z += qv.z * t;
1063 
1064  t = t0 + (t1 - t0) * t;
1065  /*lwnotice("CLOSEST TIME (real): %g", t);*/
1066 
1067  return t;
1068 }
1069 
1070 static int
1071 ptarray_collect_mvals(const POINTARRAY *pa, double tmin, double tmax, double *mvals)
1072 {
1073  POINT4D pbuf;
1074  uint32_t i, n = 0;
1075  for (i = 0; i < pa->npoints; ++i)
1076  {
1077  getPoint4d_p(pa, i, &pbuf); /* could be optimized */
1078  if (pbuf.m >= tmin && pbuf.m <= tmax)
1079  mvals[n++] = pbuf.m;
1080  }
1081  return n;
1082 }
1083 
1084 static int
1085 compare_double(const void *pa, const void *pb)
1086 {
1087  double a = *((double *)pa);
1088  double b = *((double *)pb);
1089  if (a < b)
1090  return -1;
1091  else if (a > b)
1092  return 1;
1093  else
1094  return 0;
1095 }
1096 
1097 /* Return number of elements in unique array */
1098 static int
1099 uniq(double *vals, int nvals)
1100 {
1101  int i, last = 0;
1102  for (i = 1; i < nvals; ++i)
1103  {
1104  // lwnotice("(I%d):%g", i, vals[i]);
1105  if (vals[i] != vals[last])
1106  {
1107  vals[++last] = vals[i];
1108  // lwnotice("(O%d):%g", last, vals[last]);
1109  }
1110  }
1111  return last + 1;
1112 }
1113 
1114 /*
1115  * Find point at a given measure
1116  *
1117  * The function assumes measures are linear so that always a single point
1118  * is returned for a single measure.
1119  *
1120  * @param pa the point array to perform search on
1121  * @param m the measure to search for
1122  * @param p the point to write result into
1123  * @param from the segment number to start from
1124  *
1125  * @return the segment number the point was found into
1126  * or -1 if given measure was out of the known range.
1127  */
1128 static int
1129 ptarray_locate_along_linear(const POINTARRAY *pa, double m, POINT4D *p, uint32_t from)
1130 {
1131  uint32_t i = from;
1132  POINT4D p1, p2;
1133 
1134  /* Walk through each segment in the point array */
1135  getPoint4d_p(pa, i, &p1);
1136  for (i = from + 1; i < pa->npoints; i++)
1137  {
1138  getPoint4d_p(pa, i, &p2);
1139 
1140  if (segment_locate_along(&p1, &p2, m, 0, p) == LW_TRUE)
1141  return i - 1; /* found */
1142 
1143  p1 = p2;
1144  }
1145 
1146  return -1; /* not found */
1147 }
1148 
1149 double
1150 lwgeom_tcpa(const LWGEOM *g1, const LWGEOM *g2, double *mindist)
1151 {
1152  LWLINE *l1, *l2;
1153  int i;
1154  GBOX gbox1, gbox2;
1155  double tmin, tmax;
1156  double *mvals;
1157  int nmvals = 0;
1158  double mintime;
1159  double mindist2 = FLT_MAX; /* minimum distance, squared */
1160 
1161  if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2))
1162  {
1163  lwerror("Both input geometries must have a measure dimension");
1164  return -1;
1165  }
1166 
1167  l1 = lwgeom_as_lwline(g1);
1168  l2 = lwgeom_as_lwline(g2);
1169 
1170  if (!l1 || !l2)
1171  {
1172  lwerror("Both input geometries must be linestrings");
1173  return -1;
1174  }
1175 
1176  if (l1->points->npoints < 2 || l2->points->npoints < 2)
1177  {
1178  lwerror("Both input lines must have at least 2 points");
1179  return -1;
1180  }
1181 
1182  /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */
1183  /* because we cannot afford the float rounding inaccuracy when */
1184  /* we compare the ranges for overlap below */
1185  lwgeom_calculate_gbox(g1, &gbox1);
1186  lwgeom_calculate_gbox(g2, &gbox2);
1187 
1188  /*
1189  * Find overlapping M range
1190  * WARNING: may be larger than the real one
1191  */
1192 
1193  tmin = FP_MAX(gbox1.mmin, gbox2.mmin);
1194  tmax = FP_MIN(gbox1.mmax, gbox2.mmax);
1195 
1196  if (tmax < tmin)
1197  {
1198  LWDEBUG(1, "Inputs never exist at the same time");
1199  return -2;
1200  }
1201 
1202  // lwnotice("Min:%g, Max:%g", tmin, tmax);
1203 
1204  /*
1205  * Collect M values in common time range from inputs
1206  */
1207 
1208  mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints));
1209 
1210  /* TODO: also clip the lines ? */
1211  nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals);
1212  nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals);
1213 
1214  /* Sort values in ascending order */
1215  qsort(mvals, nmvals, sizeof(double), compare_double);
1216 
1217  /* Remove duplicated values */
1218  nmvals = uniq(mvals, nmvals);
1219 
1220  if (nmvals < 2)
1221  {
1222  {
1223  /* there's a single time, must be that one... */
1224  double t0 = mvals[0];
1225  POINT4D p0, p1;
1226  LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0);
1227  if (mindist)
1228  {
1229  if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0))
1230  {
1231  lwfree(mvals);
1232  lwerror("Could not find point with M=%g on first geom", t0);
1233  return -1;
1234  }
1235  if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0))
1236  {
1237  lwfree(mvals);
1238  lwerror("Could not find point with M=%g on second geom", t0);
1239  return -1;
1240  }
1241  *mindist = distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1);
1242  }
1243  lwfree(mvals);
1244  return t0;
1245  }
1246  }
1247 
1248  /*
1249  * For each consecutive pair of measures, compute time of closest point
1250  * approach and actual distance between points at that time
1251  */
1252  mintime = tmin;
1253  for (i = 1; i < nmvals; ++i)
1254  {
1255  double t0 = mvals[i - 1];
1256  double t1 = mvals[i];
1257  double t;
1258  POINT4D p0, p1, q0, q1;
1259  int seg;
1260  double dist2;
1261 
1262  // lwnotice("T %g-%g", t0, t1);
1263 
1264  seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0);
1265  if (-1 == seg)
1266  continue; /* possible, if GBOX is approximated */
1267  // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z);
1268 
1269  seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg);
1270  if (-1 == seg)
1271  continue; /* possible, if GBOX is approximated */
1272  // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z);
1273 
1274  seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0);
1275  if (-1 == seg)
1276  continue; /* possible, if GBOX is approximated */
1277  // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z);
1278 
1279  seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg);
1280  if (-1 == seg)
1281  continue; /* possible, if GBOX is approximated */
1282  // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z);
1283 
1284  t = segments_tcpa(&p0, &p1, &q0, &q1, t0, t1);
1285 
1286  /*
1287  lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g",
1288  p0.x, p0.y, p0.z,
1289  q0.x, q0.y, q0.z, t);
1290  */
1291 
1292  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);
1293  if (dist2 < mindist2)
1294  {
1295  mindist2 = dist2;
1296  mintime = t;
1297  // lwnotice("MINTIME: %g", mintime);
1298  }
1299  }
1300 
1301  /*
1302  * Release memory
1303  */
1304 
1305  lwfree(mvals);
1306 
1307  if (mindist)
1308  {
1309  *mindist = sqrt(mindist2);
1310  }
1311  /*lwnotice("MINDIST: %g", sqrt(mindist2));*/
1312 
1313  return mintime;
1314 }
1315 
1316 int
1317 lwgeom_cpa_within(const LWGEOM *g1, const LWGEOM *g2, double maxdist)
1318 {
1319  LWLINE *l1, *l2;
1320  int i;
1321  GBOX gbox1, gbox2;
1322  double tmin, tmax;
1323  double *mvals;
1324  int nmvals = 0;
1325  double maxdist2 = maxdist * maxdist;
1326  int within = LW_FALSE;
1327 
1328  if (!lwgeom_has_m(g1) || !lwgeom_has_m(g2))
1329  {
1330  lwerror("Both input geometries must have a measure dimension");
1331  return LW_FALSE;
1332  }
1333 
1334  l1 = lwgeom_as_lwline(g1);
1335  l2 = lwgeom_as_lwline(g2);
1336 
1337  if (!l1 || !l2)
1338  {
1339  lwerror("Both input geometries must be linestrings");
1340  return LW_FALSE;
1341  }
1342 
1343  if (l1->points->npoints < 2 || l2->points->npoints < 2)
1344  {
1345  /* TODO: return distance between these two points */
1346  lwerror("Both input lines must have at least 2 points");
1347  return LW_FALSE;
1348  }
1349 
1350  /* We use lwgeom_calculate_gbox() instead of lwgeom_get_gbox() */
1351  /* because we cannot afford the float rounding inaccuracy when */
1352  /* we compare the ranges for overlap below */
1353  lwgeom_calculate_gbox(g1, &gbox1);
1354  lwgeom_calculate_gbox(g2, &gbox2);
1355 
1356  /*
1357  * Find overlapping M range
1358  * WARNING: may be larger than the real one
1359  */
1360 
1361  tmin = FP_MAX(gbox1.mmin, gbox2.mmin);
1362  tmax = FP_MIN(gbox1.mmax, gbox2.mmax);
1363 
1364  if (tmax < tmin)
1365  {
1366  LWDEBUG(1, "Inputs never exist at the same time");
1367  return LW_FALSE;
1368  }
1369 
1370  /*
1371  * Collect M values in common time range from inputs
1372  */
1373 
1374  mvals = lwalloc(sizeof(double) * (l1->points->npoints + l2->points->npoints));
1375 
1376  /* TODO: also clip the lines ? */
1377  nmvals = ptarray_collect_mvals(l1->points, tmin, tmax, mvals);
1378  nmvals += ptarray_collect_mvals(l2->points, tmin, tmax, mvals + nmvals);
1379 
1380  /* Sort values in ascending order */
1381  qsort(mvals, nmvals, sizeof(double), compare_double);
1382 
1383  /* Remove duplicated values */
1384  nmvals = uniq(mvals, nmvals);
1385 
1386  if (nmvals < 2)
1387  {
1388  /* there's a single time, must be that one... */
1389  double t0 = mvals[0];
1390  POINT4D p0, p1;
1391  LWDEBUGF(1, "Inputs only exist both at a single time (%g)", t0);
1392  if (-1 == ptarray_locate_along_linear(l1->points, t0, &p0, 0))
1393  {
1394  lwnotice("Could not find point with M=%g on first geom", t0);
1395  return LW_FALSE;
1396  }
1397  if (-1 == ptarray_locate_along_linear(l2->points, t0, &p1, 0))
1398  {
1399  lwnotice("Could not find point with M=%g on second geom", t0);
1400  return LW_FALSE;
1401  }
1402  if (distance3d_pt_pt((POINT3D *)&p0, (POINT3D *)&p1) <= maxdist)
1403  within = LW_TRUE;
1404  lwfree(mvals);
1405  return within;
1406  }
1407 
1408  /*
1409  * For each consecutive pair of measures, compute time of closest point
1410  * approach and actual distance between points at that time
1411  */
1412  for (i = 1; i < nmvals; ++i)
1413  {
1414  double t0 = mvals[i - 1];
1415  double t1 = mvals[i];
1416 #if POSTGIS_DEBUG_LEVEL >= 1
1417  double t;
1418 #endif
1419  POINT4D p0, p1, q0, q1;
1420  int seg;
1421  double dist2;
1422 
1423  // lwnotice("T %g-%g", t0, t1);
1424 
1425  seg = ptarray_locate_along_linear(l1->points, t0, &p0, 0);
1426  if (-1 == seg)
1427  continue; /* possible, if GBOX is approximated */
1428  // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z);
1429 
1430  seg = ptarray_locate_along_linear(l1->points, t1, &p1, seg);
1431  if (-1 == seg)
1432  continue; /* possible, if GBOX is approximated */
1433  // lwnotice("Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z);
1434 
1435  seg = ptarray_locate_along_linear(l2->points, t0, &q0, 0);
1436  if (-1 == seg)
1437  continue; /* possible, if GBOX is approximated */
1438  // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z);
1439 
1440  seg = ptarray_locate_along_linear(l2->points, t1, &q1, seg);
1441  if (-1 == seg)
1442  continue; /* possible, if GBOX is approximated */
1443  // lwnotice("Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z);
1444 
1445 #if POSTGIS_DEBUG_LEVEL >= 1
1446  t =
1447 #endif
1448  segments_tcpa(&p0, &p1, &q0, &q1, t0, t1);
1449 
1450  /*
1451  lwnotice("Closest points: %g,%g,%g and %g,%g,%g at time %g",
1452  p0.x, p0.y, p0.z,
1453  q0.x, q0.y, q0.z, t);
1454  */
1455 
1456  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);
1457  if (dist2 <= maxdist2)
1458  {
1459  LWDEBUGF(1, "Within distance %g at time %g, breaking", sqrt(dist2), t);
1460  within = LW_TRUE;
1461  break;
1462  }
1463  }
1464 
1465  /*
1466  * Release memory
1467  */
1468 
1469  lwfree(mvals);
1470 
1471  return within;
1472 }
char * r
Definition: cu_in_wkt.c:24
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition: lwgeom.c:707
int lwpoint_getPoint4d_p(const LWPOINT *point, POINT4D *out)
Definition: lwpoint.c:57
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n)
Definition: lwgeom_api.c:108
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
#define LW_FALSE
Definition: liblwgeom.h:94
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:309
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:927
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:96
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define LINETYPE
Definition: liblwgeom.h:103
LWGEOM * lwmline_as_lwgeom(const LWMLINE *obj)
Definition: lwgeom.c:299
LWGEOM * lwgeom_offsetcurve(const LWGEOM *geom, double size, int quadsegs, int joinStyle, double mitreLimit)
#define LW_SUCCESS
Definition: liblwgeom.h:97
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition: lwgeom.c:304
#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:1386
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
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:165
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition: lwmpoint.c:45
#define TINTYPE
Definition: liblwgeom.h:116
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
Definition: measures3d.c:1029
void lwfree(void *mem)
Definition: lwutil.c:242
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
#define FLAGS_NDIMS(flags)
Definition: liblwgeom.h:179
#define POLYGONTYPE
Definition: liblwgeom.h:104
LWMPOINT * lwmpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwmpoint.c:39
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition: lwpoint.c:129
#define __attribute__(x)
Definition: liblwgeom.h:228
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:114
void lwcollection_release(LWCOLLECTION *lwcollection)
Definition: lwcollection.c:36
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:59
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
Definition: lwcollection.c:92
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#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
void ptarray_free(POINTARRAY *pa)
Definition: ptarray.c:319
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
Calculate bounding box of a geometry, automatically taking into account whether it is cartesian or ge...
Definition: lwgeom.c:755
LWCOLLECTION * lwcollection_concat_in_place(LWCOLLECTION *col1, const LWCOLLECTION *col2)
Appends all geometries from col2 to col1 in place.
Definition: lwcollection.c:241
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
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:188
void * lwalloc(size_t size)
Definition: lwutil.c:227
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 lwpoly_free(LWPOLY *poly)
Definition: lwpoly.c:175
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
#define FLAGS_SET_M(flags, value)
Definition: liblwgeom.h:173
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
#define FLAGS_SET_Z(flags, value)
Definition: liblwgeom.h:172
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoly.c:161
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:369
LWMPOINT * lwmpoint_construct(int32_t srid, const POINTARRAY *pa)
Definition: lwmpoint.c:52
void lwline_free(LWLINE *line)
Definition: lwline.c:67
LWTRIANGLE * lwtriangle_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwtriangle.c:40
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:203
void lwpoint_set_ordinate(POINT4D *p, char ordinate, double value)
Given a point, ordinate number and value, set that ordinate on the point.
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.
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.
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 * lwmline_locate_along(const LWMLINE *lwmline, double m, double offset)
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.
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)
static LWMPOINT * lwpoint_locate_along(const LWPOINT *lwpoint, double m, __attribute__((__unused__)) double offset)
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 POINTARRAY * ptarray_clamp_to_ordinate_range(const POINTARRAY *ipa, char ordinate, double from, double to, uint8_t is_closed)
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 double segments_tcpa(POINT4D *p0, const POINT4D *p1, POINT4D *q0, const POINT4D *q1, double t0, double t1)
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,...
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...
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.
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 int ptarray_locate_along_linear(const POINTARRAY *pa, double m, POINT4D *p, uint32_t from)
static int uniq(double *vals, int nvals)
static POINTARRAY * ptarray_locate_along(const POINTARRAY *pa, double m, double offset)
static LWMPOINT * lwmpoint_locate_along(const LWMPOINT *lwin, double m, __attribute__((__unused__)) double offset)
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 * 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 LWMPOINT * lwline_locate_along(const LWLINE *lwline, double m, double offset)
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
int value
Definition: genraster.py:62
type
Definition: ovdump.py:42
Datum within(PG_FUNCTION_ARGS)
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