/src/postgis/liblwgeom/lwline.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) 2012 Sandro Santilli <strk@kbt.io> |
22 | | * Copyright (C) 2001-2006 Refractions Research Inc. |
23 | | * |
24 | | **********************************************************************/ |
25 | | |
26 | | |
27 | | /* basic LWLINE functions */ |
28 | | |
29 | | #include <stdio.h> |
30 | | #include <stdlib.h> |
31 | | #include <string.h> |
32 | | #include "liblwgeom_internal.h" |
33 | | #include "lwgeom_log.h" |
34 | | |
35 | | |
36 | | |
37 | | /* |
38 | | * Construct a new LWLINE. points will *NOT* be copied |
39 | | * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) |
40 | | */ |
41 | | LWLINE * |
42 | | lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points) |
43 | 527 | { |
44 | 527 | LWLINE *result = (LWLINE *)lwalloc(sizeof(LWLINE)); |
45 | 527 | result->type = LINETYPE; |
46 | 527 | result->flags = points->flags; |
47 | 527 | FLAGS_SET_BBOX(result->flags, bbox?1:0); |
48 | 527 | result->srid = srid; |
49 | 527 | result->points = points; |
50 | 527 | result->bbox = bbox; |
51 | 527 | return result; |
52 | 527 | } |
53 | | |
54 | | LWLINE * |
55 | | lwline_construct_empty(int32_t srid, char hasz, char hasm) |
56 | 42.3k | { |
57 | 42.3k | LWLINE *result = lwalloc(sizeof(LWLINE)); |
58 | 42.3k | result->type = LINETYPE; |
59 | 42.3k | result->flags = lwflags(hasz,hasm,0); |
60 | 42.3k | result->srid = srid; |
61 | 42.3k | result->points = ptarray_construct_empty(hasz, hasm, 1); |
62 | 42.3k | result->bbox = NULL; |
63 | 42.3k | return result; |
64 | 42.3k | } |
65 | | |
66 | | |
67 | | void lwline_free (LWLINE *line) |
68 | 4.16k | { |
69 | 4.16k | if ( ! line ) return; |
70 | | |
71 | 4.16k | if ( line->bbox ) |
72 | 0 | lwfree(line->bbox); |
73 | 4.16k | if ( line->points ) |
74 | 4.16k | ptarray_free(line->points); |
75 | 4.16k | lwfree(line); |
76 | 4.16k | } |
77 | | |
78 | | |
79 | | void printLWLINE(LWLINE *line) |
80 | 0 | { |
81 | 0 | lwnotice("LWLINE {"); |
82 | 0 | lwnotice(" ndims = %i", (int)FLAGS_NDIMS(line->flags)); |
83 | 0 | lwnotice(" srid = %i", (int)line->srid); |
84 | 0 | printPA(line->points); |
85 | 0 | lwnotice("}"); |
86 | 0 | } |
87 | | |
88 | | /* @brief Clone LWLINE object. Serialized point lists are not copied. |
89 | | * |
90 | | * @see ptarray_clone |
91 | | */ |
92 | | LWLINE * |
93 | | lwline_clone(const LWLINE *g) |
94 | 0 | { |
95 | 0 | LWLINE *ret = lwalloc(sizeof(LWLINE)); |
96 | |
|
97 | 0 | LWDEBUGF(2, "lwline_clone called with %p", g); |
98 | |
|
99 | 0 | memcpy(ret, g, sizeof(LWLINE)); |
100 | |
|
101 | 0 | ret->points = ptarray_clone(g->points); |
102 | |
|
103 | 0 | if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); |
104 | 0 | return ret; |
105 | 0 | } |
106 | | |
107 | | /* Deep clone LWLINE object. POINTARRAY *is* copied. */ |
108 | | LWLINE * |
109 | | lwline_clone_deep(const LWLINE *g) |
110 | 0 | { |
111 | 0 | LWLINE *ret = lwalloc(sizeof(LWLINE)); |
112 | |
|
113 | 0 | LWDEBUGF(2, "lwline_clone_deep called with %p", g); |
114 | 0 | memcpy(ret, g, sizeof(LWLINE)); |
115 | |
|
116 | 0 | if ( g->bbox ) ret->bbox = gbox_copy(g->bbox); |
117 | 0 | if ( g->points ) ret->points = ptarray_clone_deep(g->points); |
118 | 0 | FLAGS_SET_READONLY(ret->flags,0); |
119 | |
|
120 | 0 | return ret; |
121 | 0 | } |
122 | | |
123 | | |
124 | | void |
125 | | lwline_release(LWLINE *lwline) |
126 | 0 | { |
127 | 0 | lwgeom_release(lwline_as_lwgeom(lwline)); |
128 | 0 | } |
129 | | |
130 | | |
131 | | LWLINE * |
132 | | lwline_segmentize2d(const LWLINE *line, double dist) |
133 | 0 | { |
134 | 0 | POINTARRAY *segmentized = ptarray_segmentize2d(line->points, dist); |
135 | 0 | if ( ! segmentized ) return NULL; |
136 | 0 | return lwline_construct(line->srid, NULL, segmentized); |
137 | 0 | } |
138 | | |
139 | | /* check coordinate equality */ |
140 | | char |
141 | | lwline_same(const LWLINE *l1, const LWLINE *l2) |
142 | 0 | { |
143 | 0 | return ptarray_same(l1->points, l2->points); |
144 | 0 | } |
145 | | |
146 | | /* |
147 | | * Construct a LWLINE from an array of point and line geometries |
148 | | * LWLINE dimensions are large enough to host all input dimensions. |
149 | | */ |
150 | | LWLINE * |
151 | | lwline_from_lwgeom_array(int32_t srid, uint32_t ngeoms, LWGEOM **geoms) |
152 | 0 | { |
153 | 0 | uint32_t i; |
154 | 0 | int hasz = LW_FALSE; |
155 | 0 | int hasm = LW_FALSE; |
156 | 0 | POINTARRAY *pa; |
157 | 0 | LWLINE *line; |
158 | 0 | POINT4D pt; |
159 | 0 | LWPOINTITERATOR* it; |
160 | | |
161 | | /* |
162 | | * Find output dimensions, check integrity |
163 | | */ |
164 | 0 | for (i=0; i<ngeoms; i++) |
165 | 0 | { |
166 | 0 | if ( FLAGS_GET_Z(geoms[i]->flags) ) hasz = LW_TRUE; |
167 | 0 | if ( FLAGS_GET_M(geoms[i]->flags) ) hasm = LW_TRUE; |
168 | 0 | if ( hasz && hasm ) break; /* Nothing more to learn! */ |
169 | 0 | } |
170 | | |
171 | | /* |
172 | | * ngeoms should be a guess about how many points we have in input. |
173 | | * It's an underestimate for lines and multipoints */ |
174 | 0 | pa = ptarray_construct_empty(hasz, hasm, ngeoms); |
175 | |
|
176 | 0 | for ( i=0; i < ngeoms; i++ ) |
177 | 0 | { |
178 | 0 | LWGEOM *g = geoms[i]; |
179 | |
|
180 | 0 | if ( lwgeom_is_empty(g) ) continue; |
181 | | |
182 | 0 | if ( g->type == POINTTYPE ) |
183 | 0 | { |
184 | 0 | lwpoint_getPoint4d_p((LWPOINT*)g, &pt); |
185 | 0 | ptarray_append_point(pa, &pt, LW_TRUE); |
186 | 0 | } |
187 | 0 | else if ( g->type == LINETYPE ) |
188 | 0 | { |
189 | | /* |
190 | | * Append the new line points, de-duplicating against the previous points. |
191 | | * Duplicated points internal to the linestring are untouched. |
192 | | */ |
193 | 0 | ptarray_append_ptarray(pa, ((LWLINE*)g)->points, -1); |
194 | 0 | } |
195 | 0 | else if ( g->type == MULTILINETYPE ) |
196 | 0 | { |
197 | 0 | LWMLINE *mline = lwgeom_as_lwmline(g); |
198 | 0 | for ( uint32_t j = 0; j < mline->ngeoms; j++ ) |
199 | 0 | { |
200 | 0 | LWLINE *line = mline->geoms[j]; |
201 | 0 | if (lwline_is_empty(line)) continue; |
202 | 0 | ptarray_append_ptarray(pa, line->points, -1); |
203 | 0 | } |
204 | 0 | } |
205 | 0 | else if ( g->type == MULTIPOINTTYPE ) |
206 | 0 | { |
207 | 0 | it = lwpointiterator_create(g); |
208 | 0 | while(lwpointiterator_next(it, &pt)) |
209 | 0 | { |
210 | 0 | ptarray_append_point(pa, &pt, LW_TRUE); |
211 | 0 | } |
212 | 0 | lwpointiterator_destroy(it); |
213 | 0 | } |
214 | 0 | else |
215 | 0 | { |
216 | 0 | ptarray_free(pa); |
217 | 0 | lwerror("lwline_from_ptarray: invalid input type: %s", lwtype_name(g->type)); |
218 | 0 | return NULL; |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | 0 | if ( pa->npoints > 0 ) |
223 | 0 | line = lwline_construct(srid, NULL, pa); |
224 | 0 | else { |
225 | | /* Is this really any different from the above ? */ |
226 | 0 | ptarray_free(pa); |
227 | 0 | line = lwline_construct_empty(srid, hasz, hasm); |
228 | 0 | } |
229 | |
|
230 | 0 | return line; |
231 | 0 | } |
232 | | |
233 | | /* |
234 | | * Construct a LWLINE from an array of LWPOINTs |
235 | | * LWLINE dimensions are large enough to host all input dimensions. |
236 | | */ |
237 | | LWLINE * |
238 | | lwline_from_ptarray(int32_t srid, uint32_t npoints, LWPOINT **points) |
239 | 0 | { |
240 | 0 | uint32_t i; |
241 | 0 | int hasz = LW_FALSE; |
242 | 0 | int hasm = LW_FALSE; |
243 | 0 | POINTARRAY *pa; |
244 | 0 | LWLINE *line; |
245 | 0 | POINT4D pt; |
246 | | |
247 | | /* |
248 | | * Find output dimensions, check integrity |
249 | | */ |
250 | 0 | for (i=0; i<npoints; i++) |
251 | 0 | { |
252 | 0 | if ( points[i]->type != POINTTYPE ) |
253 | 0 | { |
254 | 0 | lwerror("lwline_from_ptarray: invalid input type: %s", lwtype_name(points[i]->type)); |
255 | 0 | return NULL; |
256 | 0 | } |
257 | 0 | if ( FLAGS_GET_Z(points[i]->flags) ) hasz = LW_TRUE; |
258 | 0 | if ( FLAGS_GET_M(points[i]->flags) ) hasm = LW_TRUE; |
259 | 0 | if ( hasz && hasm ) break; /* Nothing more to learn! */ |
260 | 0 | } |
261 | | |
262 | 0 | pa = ptarray_construct_empty(hasz, hasm, npoints); |
263 | |
|
264 | 0 | for ( i=0; i < npoints; i++ ) |
265 | 0 | { |
266 | 0 | if ( ! lwpoint_is_empty(points[i]) ) |
267 | 0 | { |
268 | 0 | lwpoint_getPoint4d_p(points[i], &pt); |
269 | 0 | ptarray_append_point(pa, &pt, LW_TRUE); |
270 | 0 | } |
271 | 0 | } |
272 | |
|
273 | 0 | if ( pa->npoints > 0 ) |
274 | 0 | line = lwline_construct(srid, NULL, pa); |
275 | 0 | else |
276 | 0 | line = lwline_construct_empty(srid, hasz, hasm); |
277 | |
|
278 | 0 | return line; |
279 | 0 | } |
280 | | |
281 | | /* |
282 | | * Construct a LWLINE from a LWMPOINT |
283 | | */ |
284 | | LWLINE * |
285 | | lwline_from_lwmpoint(int32_t srid, const LWMPOINT *mpoint) |
286 | 0 | { |
287 | 0 | uint32_t i; |
288 | 0 | POINTARRAY *pa = NULL; |
289 | 0 | LWGEOM *lwgeom = (LWGEOM*)mpoint; |
290 | 0 | POINT4D pt; |
291 | |
|
292 | 0 | char hasz = lwgeom_has_z(lwgeom); |
293 | 0 | char hasm = lwgeom_has_m(lwgeom); |
294 | 0 | uint32_t npoints = mpoint->ngeoms; |
295 | |
|
296 | 0 | if ( lwgeom_is_empty(lwgeom) ) |
297 | 0 | { |
298 | 0 | return lwline_construct_empty(srid, hasz, hasm); |
299 | 0 | } |
300 | | |
301 | 0 | pa = ptarray_construct(hasz, hasm, npoints); |
302 | |
|
303 | 0 | for (i=0; i < npoints; i++) |
304 | 0 | { |
305 | 0 | getPoint4d_p(mpoint->geoms[i]->point, 0, &pt); |
306 | 0 | ptarray_set_point4d(pa, i, &pt); |
307 | 0 | } |
308 | |
|
309 | 0 | LWDEBUGF(3, "lwline_from_lwmpoint: constructed pointarray for %d points", mpoint->ngeoms); |
310 | |
|
311 | 0 | return lwline_construct(srid, NULL, pa); |
312 | 0 | } |
313 | | |
314 | | /** |
315 | | * Returns freshly allocated #LWPOINT that corresponds to the index where. |
316 | | * Returns NULL if the geometry is empty or the index invalid. |
317 | | */ |
318 | | LWPOINT* |
319 | | lwline_get_lwpoint(const LWLINE *line, uint32_t where) |
320 | 0 | { |
321 | 0 | POINT4D pt; |
322 | 0 | LWPOINT *lwpoint; |
323 | 0 | POINTARRAY *pa; |
324 | |
|
325 | 0 | if ( lwline_is_empty(line) || where >= line->points->npoints ) |
326 | 0 | return NULL; |
327 | | |
328 | 0 | pa = ptarray_construct_empty(FLAGS_GET_Z(line->flags), FLAGS_GET_M(line->flags), 1); |
329 | 0 | pt = getPoint4d(line->points, where); |
330 | 0 | ptarray_append_point(pa, &pt, LW_TRUE); |
331 | 0 | lwpoint = lwpoint_construct(line->srid, NULL, pa); |
332 | 0 | return lwpoint; |
333 | 0 | } |
334 | | |
335 | | |
336 | | int |
337 | | lwline_add_lwpoint(LWLINE *line, LWPOINT *point, uint32_t where) |
338 | 0 | { |
339 | 0 | POINT4D pt; |
340 | 0 | getPoint4d_p(point->point, 0, &pt); |
341 | |
|
342 | 0 | if ( ptarray_insert_point(line->points, &pt, where) != LW_SUCCESS ) |
343 | 0 | return LW_FAILURE; |
344 | | |
345 | | /* Update the bounding box */ |
346 | 0 | if ( line->bbox ) |
347 | 0 | { |
348 | 0 | lwgeom_refresh_bbox((LWGEOM*)line); |
349 | 0 | } |
350 | |
|
351 | 0 | return LW_SUCCESS; |
352 | 0 | } |
353 | | |
354 | | |
355 | | |
356 | | LWLINE * |
357 | | lwline_removepoint(LWLINE *line, uint32_t index) |
358 | 0 | { |
359 | 0 | POINTARRAY *newpa; |
360 | 0 | LWLINE *ret; |
361 | |
|
362 | 0 | newpa = ptarray_removePoint(line->points, index); |
363 | |
|
364 | 0 | ret = lwline_construct(line->srid, NULL, newpa); |
365 | 0 | lwgeom_add_bbox((LWGEOM *) ret); |
366 | |
|
367 | 0 | return ret; |
368 | 0 | } |
369 | | |
370 | | /* |
371 | | * Note: input will be changed, make sure you have permissions for this. |
372 | | */ |
373 | | void |
374 | | lwline_setPoint4d(LWLINE *line, uint32_t index, POINT4D *newpoint) |
375 | 0 | { |
376 | 0 | ptarray_set_point4d(line->points, index, newpoint); |
377 | | /* Update the box, if there is one to update */ |
378 | 0 | if ( line->bbox ) |
379 | 0 | { |
380 | 0 | lwgeom_refresh_bbox((LWGEOM*)line); |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | | /** |
385 | | * Re-write the measure coordinate (or add one, if it isn't already there) interpolating |
386 | | * the measure between the supplied start and end values. |
387 | | */ |
388 | | LWLINE* |
389 | | lwline_measured_from_lwline(const LWLINE *lwline, double m_start, double m_end) |
390 | 0 | { |
391 | 0 | int i = 0; |
392 | 0 | int hasm = 0, hasz = 0; |
393 | 0 | int npoints = 0; |
394 | 0 | double length = 0.0; |
395 | 0 | double length_so_far = 0.0; |
396 | 0 | double m_range = m_end - m_start; |
397 | 0 | double m; |
398 | 0 | POINTARRAY *pa = NULL; |
399 | 0 | POINT3DZ p1, p2; |
400 | |
|
401 | 0 | if ( lwline->type != LINETYPE ) |
402 | 0 | { |
403 | 0 | lwerror("lwline_construct_from_lwline: only line types supported"); |
404 | 0 | return NULL; |
405 | 0 | } |
406 | | |
407 | 0 | hasz = FLAGS_GET_Z(lwline->flags); |
408 | 0 | hasm = 1; |
409 | | |
410 | | /* Null points or npoints == 0 will result in empty return geometry */ |
411 | 0 | if ( lwline->points ) |
412 | 0 | { |
413 | 0 | npoints = lwline->points->npoints; |
414 | 0 | length = ptarray_length_2d(lwline->points); |
415 | 0 | getPoint3dz_p(lwline->points, 0, &p1); |
416 | 0 | } |
417 | |
|
418 | 0 | pa = ptarray_construct(hasz, hasm, npoints); |
419 | |
|
420 | 0 | for ( i = 0; i < npoints; i++ ) |
421 | 0 | { |
422 | 0 | POINT4D q; |
423 | 0 | POINT2D a, b; |
424 | 0 | getPoint3dz_p(lwline->points, i, &p2); |
425 | 0 | a.x = p1.x; |
426 | 0 | a.y = p1.y; |
427 | 0 | b.x = p2.x; |
428 | 0 | b.y = p2.y; |
429 | 0 | length_so_far += distance2d_pt_pt(&a, &b); |
430 | 0 | if ( length > 0.0 ) |
431 | 0 | m = m_start + m_range * length_so_far / length; |
432 | | /* #3172, support (valid) zero-length inputs */ |
433 | 0 | else if ( length == 0.0 && npoints > 1 ) |
434 | 0 | m = m_start + m_range * i / (npoints-1); |
435 | 0 | else |
436 | 0 | m = 0.0; |
437 | 0 | q.x = p2.x; |
438 | 0 | q.y = p2.y; |
439 | 0 | q.z = p2.z; |
440 | 0 | q.m = m; |
441 | 0 | ptarray_set_point4d(pa, i, &q); |
442 | 0 | p1 = p2; |
443 | 0 | } |
444 | |
|
445 | 0 | return lwline_construct(lwline->srid, NULL, pa); |
446 | 0 | } |
447 | | |
448 | | LWGEOM* |
449 | | lwline_remove_repeated_points(const LWLINE *lwline, double tolerance) |
450 | 0 | { |
451 | 0 | return lwgeom_remove_repeated_points((LWGEOM*)lwline, tolerance); |
452 | 0 | } |
453 | | |
454 | | int |
455 | | lwline_is_closed(const LWLINE *line) |
456 | 0 | { |
457 | 0 | if (FLAGS_GET_Z(line->flags)) |
458 | 0 | return ptarray_is_closed_3d(line->points); |
459 | | |
460 | 0 | return ptarray_is_closed_2d(line->points); |
461 | 0 | } |
462 | | |
463 | | int |
464 | | lwline_is_trajectory(const LWLINE *line) |
465 | 0 | { |
466 | 0 | if (!FLAGS_GET_M(line->flags)) |
467 | 0 | { |
468 | 0 | lwnotice("Line does not have M dimension"); |
469 | 0 | return LW_FALSE; |
470 | 0 | } |
471 | | |
472 | 0 | uint32_t n = line->points->npoints; |
473 | |
|
474 | 0 | if (n < 2) |
475 | 0 | return LW_TRUE; /* empty or single-point are "good" */ |
476 | | |
477 | 0 | double m = -1 * FLT_MAX; |
478 | 0 | for (uint32_t i = 0; i < n; ++i) |
479 | 0 | { |
480 | 0 | POINT3DM p; |
481 | 0 | if (!getPoint3dm_p(line->points, i, &p)) |
482 | 0 | return LW_FALSE; |
483 | 0 | if (p.m <= m) |
484 | 0 | { |
485 | 0 | lwnotice( |
486 | 0 | "Measure of vertex %d (%g) not bigger than measure of vertex %d (%g)", i, p.m, i - 1, m); |
487 | 0 | return LW_FALSE; |
488 | 0 | } |
489 | 0 | m = p.m; |
490 | 0 | } |
491 | | |
492 | 0 | return LW_TRUE; |
493 | 0 | } |
494 | | |
495 | | LWLINE* |
496 | | lwline_force_dims(const LWLINE *line, int hasz, int hasm, double zval, double mval) |
497 | 0 | { |
498 | 0 | POINTARRAY *pdims = NULL; |
499 | 0 | LWLINE *lineout; |
500 | | |
501 | | /* Return 2D empty */ |
502 | 0 | if( lwline_is_empty(line) ) |
503 | 0 | { |
504 | 0 | lineout = lwline_construct_empty(line->srid, hasz, hasm); |
505 | 0 | } |
506 | 0 | else |
507 | 0 | { |
508 | 0 | pdims = ptarray_force_dims(line->points, hasz, hasm, zval, mval); |
509 | 0 | lineout = lwline_construct(line->srid, NULL, pdims); |
510 | 0 | } |
511 | 0 | lineout->type = line->type; |
512 | 0 | return lineout; |
513 | 0 | } |
514 | | |
515 | | uint32_t lwline_count_vertices(const LWLINE *line) |
516 | 0 | { |
517 | 0 | assert(line); |
518 | 0 | if ( ! line->points ) |
519 | 0 | return 0; |
520 | 0 | return line->points->npoints; |
521 | 0 | } |
522 | | |
523 | | double lwline_length(const LWLINE *line) |
524 | 0 | { |
525 | 0 | if ( lwline_is_empty(line) ) |
526 | 0 | return 0.0; |
527 | 0 | return ptarray_length(line->points); |
528 | 0 | } |
529 | | |
530 | | double lwline_length_2d(const LWLINE *line) |
531 | 0 | { |
532 | 0 | if ( lwline_is_empty(line) ) |
533 | 0 | return 0.0; |
534 | 0 | return ptarray_length_2d(line->points); |
535 | 0 | } |
536 | | |
537 | | |
538 | 0 | POINTARRAY* lwline_interpolate_points(const LWLINE *line, double length_fraction, char repeat) { |
539 | 0 | POINT4D pt; |
540 | 0 | uint32_t i; |
541 | 0 | uint32_t points_to_interpolate; |
542 | 0 | uint32_t points_found = 0; |
543 | 0 | double length; |
544 | 0 | double length_fraction_increment = length_fraction; |
545 | 0 | double length_fraction_consumed = 0; |
546 | 0 | char has_z = (char) lwgeom_has_z(lwline_as_lwgeom(line)); |
547 | 0 | char has_m = (char) lwgeom_has_m(lwline_as_lwgeom(line)); |
548 | 0 | const POINTARRAY* ipa = line->points; |
549 | 0 | POINTARRAY* opa; |
550 | | |
551 | | /* Empty.InterpolatePoint == Point Empty */ |
552 | 0 | if ( lwline_is_empty(line) ) |
553 | 0 | { |
554 | 0 | return ptarray_construct_empty(has_z, has_m, 0); |
555 | 0 | } |
556 | | |
557 | | /* If distance is one of the two extremes, return the point on that |
558 | | * end rather than doing any computations |
559 | | */ |
560 | 0 | if ( length_fraction == 0.0 || length_fraction == 1.0 ) |
561 | 0 | { |
562 | 0 | if ( length_fraction == 0.0 ) |
563 | 0 | getPoint4d_p(ipa, 0, &pt); |
564 | 0 | else |
565 | 0 | getPoint4d_p(ipa, ipa->npoints-1, &pt); |
566 | |
|
567 | 0 | opa = ptarray_construct(has_z, has_m, 1); |
568 | 0 | ptarray_set_point4d(opa, 0, &pt); |
569 | |
|
570 | 0 | return opa; |
571 | 0 | } |
572 | | |
573 | | /* Interpolate points along the line */ |
574 | 0 | length = ptarray_length_2d(ipa); |
575 | 0 | points_to_interpolate = repeat ? (uint32_t) floor(1 / length_fraction) : 1; |
576 | 0 | opa = ptarray_construct(has_z, has_m, points_to_interpolate); |
577 | |
|
578 | 0 | const POINT2D* p1 = getPoint2d_cp(ipa, 0); |
579 | 0 | for ( i = 0; i < ipa->npoints - 1 && points_found < points_to_interpolate; i++ ) |
580 | 0 | { |
581 | 0 | const POINT2D* p2 = getPoint2d_cp(ipa, i+1); |
582 | 0 | double segment_length_frac = distance2d_pt_pt(p1, p2) / length; |
583 | | |
584 | | /* If our target distance is before the total length we've seen |
585 | | * so far. create a new point some distance down the current |
586 | | * segment. |
587 | | */ |
588 | 0 | while ( length_fraction < length_fraction_consumed + segment_length_frac && points_found < points_to_interpolate ) |
589 | 0 | { |
590 | 0 | POINT4D p1_4d = getPoint4d(ipa, i); |
591 | 0 | POINT4D p2_4d = getPoint4d(ipa, i+1); |
592 | |
|
593 | 0 | double segment_fraction = (length_fraction - length_fraction_consumed) / segment_length_frac; |
594 | 0 | interpolate_point4d(&p1_4d, &p2_4d, &pt, segment_fraction); |
595 | 0 | ptarray_set_point4d(opa, points_found++, &pt); |
596 | 0 | length_fraction += length_fraction_increment; |
597 | 0 | } |
598 | |
|
599 | 0 | length_fraction_consumed += segment_length_frac; |
600 | |
|
601 | 0 | p1 = p2; |
602 | 0 | } |
603 | | |
604 | | /* Return the last point on the line. This shouldn't happen, but |
605 | | * could if there's some floating point rounding errors. */ |
606 | 0 | if (points_found < points_to_interpolate) { |
607 | 0 | getPoint4d_p(ipa, ipa->npoints - 1, &pt); |
608 | 0 | ptarray_set_point4d(opa, points_found, &pt); |
609 | 0 | } |
610 | |
|
611 | 0 | return opa; |
612 | 0 | } |
613 | | |
614 | | extern LWPOINT * |
615 | | lwline_interpolate_point_3d(const LWLINE *line, double distance) |
616 | 0 | { |
617 | 0 | double length, slength, tlength; |
618 | 0 | POINTARRAY *ipa; |
619 | 0 | POINT4D pt; |
620 | 0 | int nsegs, i; |
621 | 0 | LWGEOM *geom = lwline_as_lwgeom(line); |
622 | 0 | int has_z = lwgeom_has_z(geom); |
623 | 0 | int has_m = lwgeom_has_m(geom); |
624 | 0 | ipa = line->points; |
625 | | |
626 | | /* Empty.InterpolatePoint == Point Empty */ |
627 | 0 | if (lwline_is_empty(line)) |
628 | 0 | { |
629 | 0 | return lwpoint_construct_empty(line->srid, has_z, has_m); |
630 | 0 | } |
631 | | |
632 | | /* If distance is one of the two extremes, return the point on that |
633 | | * end rather than doing any expensive computations |
634 | | */ |
635 | 0 | if (distance == 0.0 || distance == 1.0) |
636 | 0 | { |
637 | 0 | if (distance == 0.0) |
638 | 0 | getPoint4d_p(ipa, 0, &pt); |
639 | 0 | else |
640 | 0 | getPoint4d_p(ipa, ipa->npoints - 1, &pt); |
641 | |
|
642 | 0 | return lwpoint_make(line->srid, has_z, has_m, &pt); |
643 | 0 | } |
644 | | |
645 | | /* Interpolate a point on the line */ |
646 | 0 | nsegs = ipa->npoints - 1; |
647 | 0 | length = ptarray_length(ipa); |
648 | 0 | tlength = 0; |
649 | 0 | for (i = 0; i < nsegs; i++) |
650 | 0 | { |
651 | 0 | POINT4D p1, p2; |
652 | 0 | POINT4D *p1ptr = &p1, *p2ptr = &p2; /* don't break |
653 | | * strict-aliasing rules |
654 | | */ |
655 | |
|
656 | 0 | getPoint4d_p(ipa, i, &p1); |
657 | 0 | getPoint4d_p(ipa, i + 1, &p2); |
658 | | |
659 | | /* Find the relative length of this segment */ |
660 | 0 | slength = distance3d_pt_pt((POINT3D *)p1ptr, (POINT3D *)p2ptr) / length; |
661 | | |
662 | | /* If our target distance is before the total length we've seen |
663 | | * so far. create a new point some distance down the current |
664 | | * segment. |
665 | | */ |
666 | 0 | if (distance < tlength + slength) |
667 | 0 | { |
668 | 0 | double dseg = (distance - tlength) / slength; |
669 | 0 | interpolate_point4d(&p1, &p2, &pt, dseg); |
670 | 0 | return lwpoint_make(line->srid, has_z, has_m, &pt); |
671 | 0 | } |
672 | 0 | tlength += slength; |
673 | 0 | } |
674 | | |
675 | | /* Return the last point on the line. This shouldn't happen, but |
676 | | * could if there's some floating point rounding errors. */ |
677 | 0 | getPoint4d_p(ipa, ipa->npoints - 1, &pt); |
678 | 0 | return lwpoint_make(line->srid, has_z, has_m, &pt); |
679 | 0 | } |
680 | | |
681 | | extern LWLINE * |
682 | | lwline_extend(const LWLINE *line, double distance_forward, double distance_backward) |
683 | 0 | { |
684 | 0 | POINTARRAY *pa, *opa; |
685 | 0 | POINT4D p00, p01, p10, p11; |
686 | 0 | POINT4D p_start, p_end; |
687 | 0 | uint32_t i; |
688 | 0 | bool forward = false, backward = false; |
689 | |
|
690 | 0 | if (distance_forward < 0 || distance_backward < 0) |
691 | 0 | lwerror("%s: distances must be non-negative", __func__); |
692 | |
|
693 | 0 | if (!line || lwline_is_empty(line) || lwline_count_vertices(line) < 2) |
694 | 0 | { |
695 | 0 | lwerror("%s: line must have at least two points", __func__); |
696 | 0 | } |
697 | |
|
698 | 0 | pa = line->points; |
699 | 0 | if (distance_backward > 0.0) |
700 | 0 | { |
701 | 0 | i = 0; |
702 | | /* Get two distinct points at start of pointarray */ |
703 | 0 | getPoint4d_p(pa, i++, &p00); |
704 | 0 | getPoint4d_p(pa, i, &p01); |
705 | 0 | while(p4d_same(&p00, &p01)) |
706 | 0 | { |
707 | 0 | if (i == pa->npoints - 1) |
708 | 0 | { |
709 | 0 | lwerror("%s: line must have at least two distinct points", __func__); |
710 | 0 | } |
711 | 0 | i++; |
712 | 0 | getPoint4d_p(pa, i, &p01); |
713 | 0 | } |
714 | 0 | project_pt_pt(&p01, &p00, distance_backward, &p_start); |
715 | 0 | backward = true; |
716 | 0 | } |
717 | |
|
718 | 0 | if (distance_forward > 0.0) |
719 | 0 | { |
720 | 0 | i = pa->npoints - 1; |
721 | | /* Get two distinct points at end of pointarray */ |
722 | 0 | getPoint4d_p(pa, i--, &p10); |
723 | 0 | getPoint4d_p(pa, i, &p11); |
724 | 0 | while(p4d_same(&p10, &p11)) |
725 | 0 | { |
726 | 0 | if (i == 0) |
727 | 0 | { |
728 | 0 | lwerror("%s: line must have at least two distinct points", __func__); |
729 | 0 | } |
730 | 0 | i--; |
731 | 0 | getPoint4d_p(pa, i, &p11); |
732 | 0 | } |
733 | 0 | project_pt_pt(&p11, &p10, distance_forward, &p_end); |
734 | 0 | forward = true; |
735 | 0 | } |
736 | |
|
737 | 0 | opa = ptarray_construct_empty(ptarray_has_z(pa), ptarray_has_m(pa), pa->npoints + 2); |
738 | |
|
739 | 0 | if (backward) |
740 | 0 | { |
741 | 0 | ptarray_append_point(opa, &p_start, true); |
742 | 0 | } |
743 | 0 | ptarray_append_ptarray(opa, pa, -1.0); |
744 | 0 | if (forward) |
745 | 0 | { |
746 | 0 | ptarray_append_point(opa, &p_end, true); |
747 | 0 | } |
748 | | return lwline_construct(line->srid, NULL, opa); |
749 | 0 | } |