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