PostGIS  3.0.6dev-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->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 
759 static inline LWCOLLECTION *
760 lwtriangle_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);
776  ptarray_append_point(tpa, &p, LW_TRUE);
777  getPoint4d_p(pa, i + 1, &p);
778  ptarray_append_point(tpa, &p, LW_TRUE);
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 
791 static inline LWCOLLECTION *
792 lwcollection_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 
828 LWCOLLECTION *
829 lwgeom_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 
916 LWCOLLECTION *
917 lwgeom_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 
925 double
926 lwgeom_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  */
980 static double
981 segments_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 
1064 static int
1065 ptarray_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 
1078 static int
1079 compare_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 */
1092 static int
1093 uniq(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  */
1122 static int
1123 ptarray_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 
1143 double
1144 lwgeom_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 
1310 int
1311 lwgeom_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
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
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:161
POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n)
Definition: lwgeom_api.c:108
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:321
#define LW_FALSE
Definition: liblwgeom.h:108
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:291
#define COLLECTIONTYPE
Definition: liblwgeom.h:122
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:909
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
LWGEOM * lwmline_as_lwgeom(const LWMLINE *obj)
Definition: lwgeom.c:281
LWGEOM * lwgeom_offsetcurve(const LWGEOM *geom, double size, int quadsegs, int joinStyle, double mitreLimit)
#define LW_SUCCESS
Definition: liblwgeom.h:111
LWGEOM * lwmpoint_as_lwgeom(const LWMPOINT *obj)
Definition: lwgeom.c:286
#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:1303
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:916
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:116
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:179
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:130
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:121
double distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2)
Definition: measures3d.c:1032
void lwfree(void *mem)
Definition: lwutil.c:242
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:326
#define FLAGS_NDIMS(flags)
Definition: liblwgeom.h:193
#define POLYGONTYPE
Definition: liblwgeom.h:118
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:242
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:128
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:180
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:737
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:129
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:107
#define FLAGS_SET_M(flags, value)
Definition: liblwgeom.h:187
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
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:376
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: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 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: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