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