PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
ptarray.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) 2012-2021 Sandro Santilli <strk@kbt.io>
22 * Copyright (C) 2001-2006 Refractions Research Inc.
23 *
24 **********************************************************************/
25
26
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30
31#include "../postgis_config.h"
32/*#define POSTGIS_DEBUG_LEVEL 4*/
33#include "liblwgeom_internal.h"
34#include "lwgeom_log.h"
35
36int
38{
39 if ( ! pa ) return LW_FALSE;
40 return FLAGS_GET_Z(pa->flags);
41}
42
43int
45{
46 if ( ! pa ) return LW_FALSE;
47 return FLAGS_GET_M(pa->flags);
48}
49
51ptarray_construct(char hasz, char hasm, uint32_t npoints)
52{
53 POINTARRAY *pa = ptarray_construct_empty(hasz, hasm, npoints);
54 pa->npoints = npoints;
55 return pa;
56}
57
59ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
60{
61 POINTARRAY *pa = lwalloc(sizeof(POINTARRAY));
62 pa->serialized_pointlist = NULL;
63
64 /* Set our dimensionality info on the bitmap */
65 pa->flags = lwflags(hasz, hasm, 0);
66
67 /* We will be allocating a bit of room */
68 pa->npoints = 0;
69 pa->maxpoints = maxpoints;
70
71 /* Allocate the coordinate array */
72 if ( maxpoints > 0 )
73 pa->serialized_pointlist = lwalloc(maxpoints * ptarray_point_size(pa));
74 else
75 pa->serialized_pointlist = NULL;
76
77 return pa;
78}
79
80/*
81* Add a point into a pointarray. Only adds as many dimensions as the
82* pointarray supports.
83*/
84int
85ptarray_insert_point(POINTARRAY *pa, const POINT4D *p, uint32_t where)
86{
87 if (!pa || !p)
88 return LW_FAILURE;
89 size_t point_size = ptarray_point_size(pa);
90 LWDEBUGF(5,"pa = %p; p = %p; where = %d", pa, p, where);
91 LWDEBUGF(5,"pa->npoints = %d; pa->maxpoints = %d", pa->npoints, pa->maxpoints);
92
93 if ( FLAGS_GET_READONLY(pa->flags) )
94 {
95 lwerror("ptarray_insert_point: called on read-only point array");
96 return LW_FAILURE;
97 }
98
99 /* Error on invalid offset value */
100 if ( where > pa->npoints )
101 {
102 lwerror("ptarray_insert_point: offset out of range (%d)", where);
103 return LW_FAILURE;
104 }
105
106 /* If we have no storage, let's allocate some */
107 if( pa->maxpoints == 0 || ! pa->serialized_pointlist )
108 {
109 pa->maxpoints = 32;
110 pa->npoints = 0;
112 }
113
114 /* Error out if we have a bad situation */
115 if ( pa->npoints > pa->maxpoints )
116 {
117 lwerror("npoints (%d) is greater than maxpoints (%d)", pa->npoints, pa->maxpoints);
118 return LW_FAILURE;
119 }
120
121 /* Check if we have enough storage, add more if necessary */
122 if( pa->npoints == pa->maxpoints )
123 {
124 pa->maxpoints *= 2;
126 }
127
128 /* Make space to insert the new point */
129 if( where < pa->npoints )
130 {
131 size_t copy_size = point_size * (pa->npoints - where);
132 memmove(getPoint_internal(pa, where+1), getPoint_internal(pa, where), copy_size);
133 LWDEBUGF(5,"copying %zu bytes to start vertex %d from start vertex %d", copy_size, where+1, where);
134 }
135
136 /* We have one more point */
137 ++pa->npoints;
138
139 /* Copy the new point into the gap */
140 ptarray_set_point4d(pa, where, p);
141 LWDEBUGF(5,"copying new point to start vertex %zu", point_size);
142
143 return LW_SUCCESS;
144}
145
146int
147ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int repeated_points)
148{
149 /* Check for pathology */
150 if( ! pa || ! pt )
151 {
152 lwerror("ptarray_append_point: null input");
153 return LW_FAILURE;
154 }
155
156 /* Check for duplicate end point */
157 if ( repeated_points == LW_FALSE && pa->npoints > 0 )
158 {
159 POINT4D tmp;
160 getPoint4d_p(pa, pa->npoints-1, &tmp);
161 LWDEBUGF(4,"checking for duplicate end point (pt = POINT(%g %g) pa->npoints-q = POINT(%g %g))",pt->x,pt->y,tmp.x,tmp.y);
162
163 /* Return LW_SUCCESS and do nothing else if previous point in list is equal to this one */
164 if ( (pt->x == tmp.x) && (pt->y == tmp.y) &&
165 (FLAGS_GET_Z(pa->flags) ? pt->z == tmp.z : 1) &&
166 (FLAGS_GET_M(pa->flags) ? pt->m == tmp.m : 1) )
167 {
168 return LW_SUCCESS;
169 }
170 }
171
172 /* Append is just a special case of insert */
173 return ptarray_insert_point(pa, pt, pa->npoints);
174}
175
176int
177ptarray_append_ptarray(POINTARRAY *pa1, POINTARRAY *pa2, double gap_tolerance)
178{
179 unsigned int poff = 0;
180 unsigned int npoints;
181 unsigned int ncap;
182 unsigned int ptsize;
183
184 /* Check for pathology */
185 if( ! pa1 || ! pa2 )
186 {
187 lwerror("%s: null input", __func__);
188 return LW_FAILURE;
189 }
190
191 npoints = pa2->npoints;
192
193 if ( ! npoints ) return LW_SUCCESS; /* nothing more to do */
194
195 if( FLAGS_GET_READONLY(pa1->flags) )
196 {
197 lwerror("%s: target pointarray is read-only", __func__);
198 return LW_FAILURE;
199 }
200
201 if( FLAGS_GET_ZM(pa1->flags) != FLAGS_GET_ZM(pa2->flags) )
202 {
203 lwerror("%s: appending mixed dimensionality is not allowed", __func__);
204 return LW_FAILURE;
205 }
206
207 ptsize = ptarray_point_size(pa1);
208
209 /* Check for duplicate end point */
210 if ( pa1->npoints )
211 {
212 const POINT2D *tmp1, *tmp2;
213 tmp1 = getPoint2d_cp(pa1, pa1->npoints-1);
214 tmp2 = getPoint2d_cp(pa2, 0);
215
216 /* If the end point and start point are the same, then don't copy start point */
217 if (p2d_same(tmp1, tmp2)) {
218 poff = 1;
219 --npoints;
220 }
221 else if ((gap_tolerance == 0) ||
222 (gap_tolerance > 0 && distance2d_pt_pt(tmp1, tmp2) > gap_tolerance) )
223 {
224 lwerror("Second line start point too far from first line end point");
225 return LW_FAILURE;
226 }
227 }
228
229 /* Check if we need extra space */
230 ncap = pa1->npoints + npoints;
231 if ( pa1->maxpoints < ncap )
232 {
233 if ( pa1->maxpoints )
234 {
235 pa1->maxpoints = ncap > pa1->maxpoints*2 ?
236 ncap : pa1->maxpoints*2;
237 pa1->serialized_pointlist = lwrealloc(pa1->serialized_pointlist, (size_t)ptsize * pa1->maxpoints);
238 }
239 else
240 {
241 pa1->maxpoints = ncap;
242 pa1->serialized_pointlist = lwalloc((size_t)ptsize * pa1->maxpoints);
243 }
244 }
245
246 memcpy(getPoint_internal(pa1, pa1->npoints),
247 getPoint_internal(pa2, poff), (size_t)ptsize * npoints);
248
249 pa1->npoints = ncap;
250
251 return LW_SUCCESS;
252}
253
254/*
255* Add a point into a pointarray. Only adds as many dimensions as the
256* pointarray supports.
257*/
258int
260{
261 /* Check for pathology */
262 if( ! pa )
263 {
264 lwerror("ptarray_remove_point: null input");
265 return LW_FAILURE;
266 }
267
268 /* Error on invalid offset value */
269 if ( where >= pa->npoints )
270 {
271 lwerror("ptarray_remove_point: offset out of range (%d)", where);
272 return LW_FAILURE;
273 }
274
275 /* If the point is any but the last, we need to copy the data back one point */
276 if (where < pa->npoints - 1)
277 memmove(getPoint_internal(pa, where),
278 getPoint_internal(pa, where + 1),
279 ptarray_point_size(pa) * (pa->npoints - where - 1));
280
281 /* We have one less point */
282 pa->npoints--;
283
284 return LW_SUCCESS;
285}
286
291POINTARRAY* ptarray_construct_reference_data(char hasz, char hasm, uint32_t npoints, uint8_t *ptlist)
292{
293 POINTARRAY *pa = lwalloc(sizeof(POINTARRAY));
294 LWDEBUGF(5, "hasz = %d, hasm = %d, npoints = %d, ptlist = %p", hasz, hasm, npoints, ptlist);
295 pa->flags = lwflags(hasz, hasm, 0);
296 FLAGS_SET_READONLY(pa->flags, 1); /* We don't own this memory, so we can't alter or free it. */
297 pa->npoints = npoints;
298 pa->maxpoints = npoints;
299 pa->serialized_pointlist = ptlist;
300 return pa;
301}
302
303
305ptarray_construct_copy_data(char hasz, char hasm, uint32_t npoints, const uint8_t *ptlist)
306{
307 POINTARRAY *pa = lwalloc(sizeof(POINTARRAY));
308
309 pa->flags = lwflags(hasz, hasm, 0);
310 pa->npoints = npoints;
311 pa->maxpoints = npoints;
312
313 if ( npoints > 0 )
314 {
316 memcpy(pa->serialized_pointlist, ptlist, ptarray_point_size(pa) * npoints);
317 }
318 else
319 {
320 pa->serialized_pointlist = NULL;
321 }
322
323 return pa;
324}
325
326void
328{
329 if (pa)
330 {
333 lwfree(pa);
334 }
335}
336
337
338void
340{
341 if (!pa->npoints)
342 return;
343 uint32_t i;
344 uint32_t last = pa->npoints - 1;
345 uint32_t mid = pa->npoints / 2;
346
347 double *d = (double*)(pa->serialized_pointlist);
348 int j;
349 int ndims = FLAGS_NDIMS(pa->flags);
350 for (i = 0; i < mid; i++)
351 {
352 for (j = 0; j < ndims; j++)
353 {
354 double buf;
355 buf = d[i*ndims+j];
356 d[i*ndims+j] = d[(last-i)*ndims+j];
357 d[(last-i)*ndims+j] = buf;
358 }
359 }
360 return;
361}
362
363
369{
370 uint32_t i;
371 double d;
372 POINT4D p;
373
374 for (i=0 ; i < pa->npoints ; i++)
375 {
376 getPoint4d_p(pa, i, &p);
377 d = p.y;
378 p.y = p.x;
379 p.x = d;
380 ptarray_set_point4d(pa, i, &p);
381 }
382
383 return pa;
384}
385
386void
388{
389 uint32_t i;
390 double d, *dp1, *dp2;
391 POINT4D p;
392
393 dp1 = ((double*)&p)+(unsigned)o1;
394 dp2 = ((double*)&p)+(unsigned)o2;
395 for (i=0 ; i < pa->npoints ; i++)
396 {
397 getPoint4d_p(pa, i, &p);
398 d = *dp2;
399 *dp2 = *dp1;
400 *dp1 = d;
401 ptarray_set_point4d(pa, i, &p);
402 }
403}
404
413ptarray_segmentize2d(const POINTARRAY *ipa, double dist)
414{
415 double segdist;
416 POINT4D p1, p2;
417 POINT4D pbuf;
418 POINTARRAY *opa;
419 uint32_t i, j, nseg;
420 int hasz = FLAGS_GET_Z(ipa->flags);
421 int hasm = FLAGS_GET_M(ipa->flags);
422
423 pbuf.x = pbuf.y = pbuf.z = pbuf.m = 0;
424
425 /* Initial storage */
426 opa = ptarray_construct_empty(hasz, hasm, ipa->npoints);
427
428 /* Add first point */
429 getPoint4d_p(ipa, 0, &p1);
431
432 /* Loop on all other input points */
433 for (i = 1; i < ipa->npoints; i++)
434 {
435 /*
436 * We use these pointers to avoid
437 * "strict-aliasing rules break" warning raised
438 * by gcc (3.3 and up).
439 *
440 * It looks that casting a variable address (also
441 * referred to as "type-punned pointer")
442 * breaks those "strict" rules.
443 */
444 POINT4D *p1ptr=&p1, *p2ptr=&p2;
445 double segments;
446
447 getPoint4d_p(ipa, i, &p2);
448
449 segdist = distance2d_pt_pt((POINT2D *)p1ptr, (POINT2D *)p2ptr);
450 /* Split input segment into shorter even chunks */
451 segments = ceil(segdist / dist);
452
453 /* Uses INT32_MAX instead of UINT32_MAX to be safe that it fits */
454 if (segments >= INT32_MAX)
455 {
456 lwnotice("%s:%d - %s: Too many segments required (%e)",
457 __FILE__, __LINE__,__func__, segments);
458 ptarray_free(opa);
459 return NULL;
460 }
461 nseg = segments;
462
463 for (j = 1; j < nseg; j++)
464 {
465 pbuf.x = p1.x + (p2.x - p1.x) * j / nseg;
466 pbuf.y = p1.y + (p2.y - p1.y) * j / nseg;
467 if (hasz)
468 pbuf.z = p1.z + (p2.z - p1.z) * j / nseg;
469 if (hasm)
470 pbuf.m = p1.m + (p2.m - p1.m) * j / nseg;
471 ptarray_append_point(opa, &pbuf, LW_FALSE);
472 LW_ON_INTERRUPT(ptarray_free(opa); return NULL);
473 }
474
475 ptarray_append_point(opa, &p2, (ipa->npoints == 2) ? LW_TRUE : LW_FALSE);
476 p1 = p2;
477 LW_ON_INTERRUPT(ptarray_free(opa); return NULL);
478 }
479
480 return opa;
481}
482
483char
484ptarray_same(const POINTARRAY *pa1, const POINTARRAY *pa2)
485{
486 uint32_t i;
487 size_t ptsize;
488
489 if ( FLAGS_GET_ZM(pa1->flags) != FLAGS_GET_ZM(pa2->flags) ) return LW_FALSE;
490 LWDEBUG(5,"dimensions are the same");
491
492 if ( pa1->npoints != pa2->npoints ) return LW_FALSE;
493 LWDEBUG(5,"npoints are the same");
494
495 ptsize = ptarray_point_size(pa1);
496 LWDEBUGF(5, "ptsize = %zu", ptsize);
497
498 for (i=0; i<pa1->npoints; i++)
499 {
500 if ( memcmp(getPoint_internal(pa1, i), getPoint_internal(pa2, i), ptsize) )
501 return LW_FALSE;
502 LWDEBUGF(5,"point #%d is the same",i);
503 }
504
505 return LW_TRUE;
506}
507
508char
509ptarray_same2d(const POINTARRAY *pa1, const POINTARRAY *pa2)
510{
511 uint32_t i;
512
513 if ( FLAGS_GET_ZM(pa1->flags) != FLAGS_GET_ZM(pa2->flags) ) return LW_FALSE;
514 LWDEBUG(5,"dimensions are the same");
515
516 if ( pa1->npoints != pa2->npoints ) return LW_FALSE;
517 LWDEBUG(5,"npoints are the same");
518
519 for (i=0; i<pa1->npoints; i++)
520 {
521 if ( memcmp(getPoint_internal(pa1, i), getPoint_internal(pa2, i), sizeof(POINT2D)) )
522 return LW_FALSE;
523 LWDEBUGF(5,"point #%d is the same",i);
524 }
525
526 return LW_TRUE;
527}
528
530ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where)
531{
532 POINTARRAY *ret;
533 POINT4D pbuf;
534 size_t ptsize = ptarray_point_size(pa);
535
536 LWDEBUGF(3, "pa %p p %p size %zu where %u",
537 pa, p, pdims, where);
538
539 if ( pdims < 2 || pdims > 4 )
540 {
541 lwerror("ptarray_addPoint: point dimension out of range (%zu)",
542 pdims);
543 return NULL;
544 }
545
546 if ( where > pa->npoints )
547 {
548 lwerror("ptarray_addPoint: offset out of range (%d)",
549 where);
550 return NULL;
551 }
552
553 LWDEBUG(3, "called with a point");
554
555 pbuf.x = pbuf.y = pbuf.z = pbuf.m = 0.0;
556 memcpy((uint8_t *)&pbuf, p, pdims*sizeof(double));
557
558 LWDEBUG(3, "initialized point buffer");
559
561 FLAGS_GET_M(pa->flags), pa->npoints+1);
562
563
564 if ( where )
565 {
566 memcpy(getPoint_internal(ret, 0), getPoint_internal(pa, 0), ptsize*where);
567 }
568
569 memcpy(getPoint_internal(ret, where), (uint8_t *)&pbuf, ptsize);
570
571 if ( where+1 != ret->npoints )
572 {
573 memcpy(getPoint_internal(ret, where+1),
574 getPoint_internal(pa, where),
575 ptsize*(pa->npoints-where));
576 }
577
578 return ret;
579}
580
582ptarray_removePoint(POINTARRAY *pa, uint32_t which)
583{
584 POINTARRAY *ret;
585 size_t ptsize = ptarray_point_size(pa);
586
587 LWDEBUGF(3, "pa %p which %u", pa, which);
588
589 assert(which <= pa->npoints-1);
590 assert(pa->npoints >= 3);
591
593 FLAGS_GET_M(pa->flags), pa->npoints-1);
594
595 /* copy initial part */
596 if ( which )
597 {
598 memcpy(getPoint_internal(ret, 0), getPoint_internal(pa, 0), ptsize*which);
599 }
600
601 /* copy final part */
602 if ( which < pa->npoints-1 )
603 {
604 memcpy(getPoint_internal(ret, which), getPoint_internal(pa, which+1),
605 ptsize*(pa->npoints-which-1));
606 }
607
608 return ret;
609}
610
613{
614 POINTARRAY *pa;
615 size_t ptsize = ptarray_point_size(pa1);
616
617 if (FLAGS_GET_ZM(pa1->flags) != FLAGS_GET_ZM(pa2->flags))
618 lwerror("ptarray_cat: Mixed dimension");
619
621 FLAGS_GET_M(pa1->flags),
622 pa1->npoints + pa2->npoints);
623
624 memcpy( getPoint_internal(pa, 0),
625 getPoint_internal(pa1, 0),
626 ptsize*(pa1->npoints));
627
628 memcpy( getPoint_internal(pa, pa1->npoints),
629 getPoint_internal(pa2, 0),
630 ptsize*(pa2->npoints));
631
632 ptarray_free(pa1);
633 ptarray_free(pa2);
634
635 return pa;
636}
637
638
644{
645 POINTARRAY *out = lwalloc(sizeof(POINTARRAY));
646
647 LWDEBUG(3, "ptarray_clone_deep called.");
648
649 out->flags = in->flags;
650 out->npoints = in->npoints;
651 out->maxpoints = in->npoints;
652
653 FLAGS_SET_READONLY(out->flags, 0);
654
655 if (!in->npoints)
656 {
657 // Avoid calling lwalloc of 0 bytes
658 out->serialized_pointlist = NULL;
659 }
660 else
661 {
662 size_t size = in->npoints * ptarray_point_size(in);
663 out->serialized_pointlist = lwalloc(size);
664 memcpy(out->serialized_pointlist, in->serialized_pointlist, size);
665 }
666
667 return out;
668}
669
675{
676 POINTARRAY *out = lwalloc(sizeof(POINTARRAY));
677
678 LWDEBUG(3, "ptarray_clone called.");
679
680 out->flags = in->flags;
681 out->npoints = in->npoints;
682 out->maxpoints = in->maxpoints;
683
684 FLAGS_SET_READONLY(out->flags, 1);
685
687
688 return out;
689}
690
695int
697{
698 if (!in)
699 {
700 lwerror("ptarray_is_closed: called with null point array");
701 return 0;
702 }
703 if (in->npoints <= 1 ) return in->npoints; /* single-point are closed, empty not closed */
704
705 return 0 == memcmp(getPoint_internal(in, 0), getPoint_internal(in, in->npoints-1), ptarray_point_size(in));
706}
707
708
709int
711{
712 if (!in)
713 {
714 lwerror("ptarray_is_closed_2d: called with null point array");
715 return 0;
716 }
717 if (in->npoints <= 1 ) return in->npoints; /* single-point are closed, empty not closed */
718
719 return 0 == memcmp(getPoint_internal(in, 0), getPoint_internal(in, in->npoints-1), sizeof(POINT2D) );
720}
721
722int
724{
725 if (!in)
726 {
727 lwerror("ptarray_is_closed_3d: called with null point array");
728 return 0;
729 }
730 if (in->npoints <= 1 ) return in->npoints; /* single-point are closed, empty not closed */
731
732 return 0 == memcmp(getPoint_internal(in, 0), getPoint_internal(in, in->npoints-1), sizeof(POINT3D) );
733}
734
735int
737{
738 if ( FLAGS_GET_Z(in->flags) )
739 return ptarray_is_closed_3d(in);
740 else
741 return ptarray_is_closed_2d(in);
742}
743
754int
756{
757 const POINT2D *seg1, *seg2;
758 int wn = 0;
759
760 seg1 = getPoint2d_cp(pa, 0);
761 seg2 = getPoint2d_cp(pa, pa->npoints-1);
762 if (!p2d_same(seg1, seg2))
763 lwerror("ptarray_contains_point called on unclosed ring");
764
765 for (uint32_t i = 1; i < pa->npoints; i++)
766 {
767 double side, ymin, ymax;
768
769 seg2 = getPoint2d_cp(pa, i);
770
771 /* Zero length segments are ignored. */
772 if (p2d_same(seg1, seg2))
773 {
774 seg1 = seg2;
775 continue;
776 }
777
778 ymin = FP_MIN(seg1->y, seg2->y);
779 ymax = FP_MAX(seg1->y, seg2->y);
780
781 /* Only test segments in our vertical range */
782 if (pt->y > ymax || pt->y < ymin)
783 {
784 seg1 = seg2;
785 continue;
786 }
787
788 side = lw_segment_side(seg1, seg2, pt);
789
790 /*
791 * A point on the boundary of a ring is not contained.
792 * WAS: if (fabs(side) < 1e-12), see #852
793 */
794 if ((side == 0) && lw_pt_in_seg(pt, seg1, seg2))
795 {
796 return LW_BOUNDARY;
797 }
798
799 /*
800 * If the point is to the left of the line, and it's rising,
801 * then the line is to the right of the point and
802 * circling counter-clockwise, so increment.
803 */
804 if ((side < 0) && (seg1->y <= pt->y) && (pt->y < seg2->y))
805 {
806 wn++;
807 }
808
809 /*
810 * If the point is to the right of the line, and it's falling,
811 * then the line is to the right of the point and circling
812 * clockwise, so decrement.
813 */
814 else if ( (side > 0) && (seg2->y <= pt->y) && (pt->y < seg1->y) )
815 {
816 wn--;
817 }
818
819 seg1 = seg2;
820 }
821
822 /* wn == 0 => Outside, wn != 0 => Inside */
823 return wn == 0 ? LW_OUTSIDE : LW_INSIDE;
824}
825
826int
827ptarray_raycast_intersections(const POINTARRAY *pa, const POINT2D *p, int *on_boundary)
828{
829 // A valid linestring must have at least 2 point
830 if (pa->npoints < 2)
831 lwerror("%s called on invalid linestring", __func__);
832
833 int intersections = 0;
834 double px = p->x;
835 double py = p->y;
836
837 // Iterate through each edge of the polygon
838 for (uint32_t i = 0; i < pa->npoints-1; ++i)
839 {
840 const POINT2D* p1 = getPoint2d_cp(pa, i);
841 const POINT2D* p2 = getPoint2d_cp(pa, i+1);
842
843 /* Skip zero-length edges */
844 if (p2d_same(p1, p2))
845 continue;
846
847 /* --- Step 1: Check if the point is ON the boundary edge --- */
848 if (lw_pt_on_segment(p1, p2, p))
849 {
850 *on_boundary = LW_TRUE;
851 return 0;
852 }
853
854 /* --- Step 2: Perform the Ray Casting intersection test --- */
855
856 /*
857 * Check if the horizontal ray from p intersects the edge (p1, p2).
858 * This is the core condition for handling vertices correctly:
859 * - One vertex must be strictly above the ray (py < vertex.y)
860 * - The other must be on or below the ray (py >= vertex.y)
861 */
862 if (((p1->y <= py) && (py < p2->y)) || ((p2->y <= py) && (py < p1->y)))
863 {
864 /*
865 * Calculate the x-coordinate where the edge intersects the ray's horizontal line.
866 * Formula: x = x1 + (y - y1) * (x2 - x1) / (y2 - y1)
867 */
868 double x_intersection = p1->x + (py - p1->y) * (p2->x - p1->x) / (p2->y - p1->y);
869
870 /*
871 * If the intersection point is to the right of our test point,
872 * it's a valid "crossing".
873 */
874 if (x_intersection > px)
875 {
876 intersections++;
877 }
878 }
879 }
880
881 return intersections;
882}
883
884
902static int
903circle_raycast_intersections(const POINT2D *center, double radius, double ray, POINT2D *i0, POINT2D *i1)
904{
905 // Calculate the vertical distance from the circle's center to the horizontal line.
906 double dy = ray - center->y;
907
908 // If the absolute vertical distance is greater than the radius, there are no intersections.
909 if (fabs(dy) > radius)
910 return 0;
911
912 // Use the Pythagorean theorem to find the horizontal distance (dx) from the
913 // center's x-coordinate to the intersection points.
914 // dx^2 + dy^2 = radius^2 => dx^2 = radius^2 - dy^2
915 double dx_squared = radius * radius - dy * dy;
916
917 // Case 1: One intersection (tangent)
918 // This occurs when the line just touches the top or bottom of the circle.
919 // dx_squared will be zero. We check against a small epsilon for floating-point safety.
920 if (FP_EQUALS(dx_squared, 0.0))
921 {
922 i0->x = center->x;
923 i0->y = ray;
924 return 1;
925 }
926
927 // Case 2: Two intersections
928 // The line cuts through the circle.
929 double dx = sqrt(dx_squared);
930
931 // The first intersection point has the smaller x-value.
932 i0->x = center->x - dx;
933 i0->y = ray;
934
935 // The second intersection point has the larger x-value.
936 i1->x = center->x + dx;
937 i1->y = ray;
938
939 return 2;
940}
941
942
943int
944ptarrayarc_raycast_intersections(const POINTARRAY *pa, const POINT2D *p, int *on_boundary)
945{
946 int intersections = 0;
947 double px = p->x;
948 double py = p->y;
949
950 assert(on_boundary);
951
952 // A valid circular arc must have at least 3 vertices (circle).
953 if (pa->npoints < 3)
954 lwerror("%s called on invalid circularstring", __func__);
955
956 if (pa->npoints % 2 == 0)
957 lwerror("%s called with even number of points", __func__);
958
959 // Iterate through each arc of the circularstring
960 for (uint32_t i = 1; i < pa->npoints-1; i +=2)
961 {
962 const POINT2D* p0 = getPoint2d_cp(pa, i-1);
963 const POINT2D* p1 = getPoint2d_cp(pa, i);
964 const POINT2D* p2 = getPoint2d_cp(pa, i+1);
965 POINT2D center = {0,0};
966 double radius, d;
967 GBOX gbox;
968
969 // Skip zero-length arc
970 if (lw_arc_is_pt(p0, p1, p2))
971 continue;
972
973 // --- Step 1: Check if the point is ON the boundary edge ---
974 if (p2d_same(p0, p) || p2d_same(p1, p) || p2d_same(p2, p))
975 {
976 *on_boundary = LW_TRUE;
977 return 0;
978 }
979
980 // Calculate some important pieces
981 radius = lw_arc_center(p0, p1, p2, &center);
982
983 d = distance2d_pt_pt(p, &center);
984 if (FP_EQUALS(d, radius) && lw_pt_in_arc(p, p0, p1, p2))
985 {
986 *on_boundary = LW_TRUE;
987 return 0;
988 }
989
990 // --- Step 2: Perform the Ray Casting intersection test ---
991
992 // Only process arcs that our ray crosses
993 lw_arc_calculate_gbox_cartesian_2d(p0, p1, p2, &gbox);
994 if ((gbox.ymin <= py) && (py < gbox.ymax))
995 {
996 // Point of intersection on the circle that defines the arc
997 POINT2D i0, i1;
998
999 // How many points of intersection are there
1000 int iCount = circle_raycast_intersections(&center, radius, py, &i0, &i1);
1001
1002 // Nothing to see here
1003 if (iCount == 0)
1004 continue;
1005
1006 // Cannot think of a case where a grazing is not a
1007 // no-op
1008 if (iCount == 1)
1009 continue;
1010
1011 // So we must have 2 intersections
1012 // Only increment the counter for intersections to the right
1013 // of the test point
1014 if (i0.x > px && lw_pt_in_arc(&i0, p0, p1, p2))
1015 intersections++;
1016
1017 if (i1.x > px && lw_pt_in_arc(&i1, p0, p1, p2))
1018 intersections++;
1019 }
1020 }
1021
1022 return intersections;
1023}
1024
1025/*
1026 * The following is based on the "Fast Winding Number Inclusion of a Point
1027 * in a Polygon" algorithm by Dan Sunday.
1028 * http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm#Winding%20Number
1029 */
1030int
1031ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number)
1032{
1033 int wn = 0;
1034 uint32_t i;
1035 double side;
1036 const POINT2D *seg1, *seg2;
1037 double ymin, ymax;
1038
1039 seg1 = getPoint2d_cp(pa, 0);
1040 seg2 = getPoint2d_cp(pa, pa->npoints-1);
1041 if ( check_closed && ! p2d_same(seg1, seg2) )
1042 lwerror("ptarray_contains_point called on unclosed ring");
1043
1044 for ( i=1; i < pa->npoints; i++ )
1045 {
1046 seg2 = getPoint2d_cp(pa, i);
1047
1048 /* Zero length segments are ignored. */
1049 if ( seg1->x == seg2->x && seg1->y == seg2->y )
1050 {
1051 seg1 = seg2;
1052 continue;
1053 }
1054
1055 ymin = FP_MIN(seg1->y, seg2->y);
1056 ymax = FP_MAX(seg1->y, seg2->y);
1057
1058 /* Only test segments in our vertical range */
1059 if ( pt->y > ymax || pt->y < ymin )
1060 {
1061 seg1 = seg2;
1062 continue;
1063 }
1064
1065 side = lw_segment_side(seg1, seg2, pt);
1066
1067 /*
1068 * A point on the boundary of a ring is not contained.
1069 * WAS: if (fabs(side) < 1e-12), see #852
1070 */
1071 if ( (side == 0) && lw_pt_in_seg(pt, seg1, seg2) )
1072 {
1073 return LW_BOUNDARY;
1074 }
1075
1076 /*
1077 * If the point is to the left of the line, and it's rising,
1078 * then the line is to the right of the point and
1079 * circling counter-clockwise, so increment.
1080 */
1081 if ( (side < 0) && (seg1->y <= pt->y) && (pt->y < seg2->y) )
1082 {
1083 wn++;
1084 }
1085
1086 /*
1087 * If the point is to the right of the line, and it's falling,
1088 * then the line is to the right of the point and circling
1089 * clockwise, so decrement.
1090 */
1091 else if ( (side > 0) && (seg2->y <= pt->y) && (pt->y < seg1->y) )
1092 {
1093 wn--;
1094 }
1095
1096 seg1 = seg2;
1097 }
1098
1099 /* Sent out the winding number for calls that are building on this as a primitive */
1100 if ( winding_number )
1101 *winding_number = wn;
1102
1103 /* Outside */
1104 if (wn == 0)
1105 {
1106 return LW_OUTSIDE;
1107 }
1108
1109 /* Inside */
1110 return LW_INSIDE;
1111}
1112
1122int
1124{
1125 int on_boundary = LW_FALSE;
1126 int intersections;
1127 if (!ptarray_is_closed_2d(pa))
1128 lwerror("%s called on unclosed ring", __func__);
1129
1130 intersections = ptarrayarc_raycast_intersections(pa, pt, &on_boundary);
1131 if (on_boundary)
1132 return LW_BOUNDARY;
1133 else
1134 return (intersections % 2) ? LW_INSIDE : LW_OUTSIDE;
1135}
1136
1142double
1144{
1145 const POINT2D *P1;
1146 const POINT2D *P2;
1147 const POINT2D *P3;
1148 double sum = 0.0;
1149 double x0, x, y1, y2;
1150 uint32_t i;
1151
1152 if (! pa || pa->npoints < 3 )
1153 return 0.0;
1154
1155 P1 = getPoint2d_cp(pa, 0);
1156 P2 = getPoint2d_cp(pa, 1);
1157 x0 = P1->x;
1158 for ( i = 2; i < pa->npoints; i++ )
1159 {
1160 P3 = getPoint2d_cp(pa, i);
1161 x = P2->x - x0;
1162 y1 = P3->y;
1163 y2 = P1->y;
1164 sum += x * (y2-y1);
1165
1166 /* Move forwards! */
1167 P1 = P2;
1168 P2 = P3;
1169 }
1170 return sum / 2.0;
1171}
1172
1173int
1174ptarray_has_orientation(const POINTARRAY *pa, int orientation)
1175{
1176 if (ptarray_signed_area(pa) > 0)
1177 return orientation == LW_CLOCKWISE;
1178 else
1179 return orientation == LW_COUNTERCLOCKWISE;
1180}
1181
1183{
1185}
1186
1188ptarray_force_dims(const POINTARRAY *pa, int hasz, int hasm, double zval, double mval)
1189{
1190 /* TODO handle zero-length point arrays */
1191 uint32_t i;
1192 int in_hasz = FLAGS_GET_Z(pa->flags);
1193 int in_hasm = FLAGS_GET_M(pa->flags);
1194 POINT4D pt;
1195 POINTARRAY *pa_out = ptarray_construct_empty(hasz, hasm, pa->npoints);
1196
1197 for( i = 0; i < pa->npoints; i++ )
1198 {
1199 getPoint4d_p(pa, i, &pt);
1200 if( hasz && ! in_hasz )
1201 pt.z = zval;
1202 if( hasm && ! in_hasm )
1203 pt.m = mval;
1204 ptarray_append_point(pa_out, &pt, LW_TRUE);
1205 }
1206
1207 return pa_out;
1208}
1209
1210POINTARRAY *
1211ptarray_substring(POINTARRAY *ipa, double from, double to, double tolerance)
1212{
1213 POINTARRAY *dpa;
1214 POINT4D pt;
1215 POINT4D p1, p2;
1216 POINT4D *p1ptr=&p1; /* don't break strict-aliasing rule */
1217 POINT4D *p2ptr=&p2;
1218 int nsegs, i;
1219 double length, slength, tlength;
1220 int state = 0; /* 0=before, 1=inside */
1221
1222 /*
1223 * Create a dynamic pointarray with an initial capacity
1224 * equal to full copy of input points
1225 */
1227
1228 /* Compute total line length */
1229 length = ptarray_length_2d(ipa);
1230
1231
1232 LWDEBUGF(3, "Total length: %g", length);
1233
1234
1235 /* Get 'from' and 'to' lengths */
1236 from = length*from;
1237 to = length*to;
1238
1239
1240 LWDEBUGF(3, "From/To: %g/%g", from, to);
1241
1242
1243 tlength = 0;
1244 getPoint4d_p(ipa, 0, &p1);
1245 nsegs = ipa->npoints - 1;
1246 for ( i = 0; i < nsegs; i++ )
1247 {
1248 double dseg;
1249
1250 getPoint4d_p(ipa, i+1, &p2);
1251
1252
1253 LWDEBUGF(3 ,"Segment %d: (%g,%g,%g,%g)-(%g,%g,%g,%g)",
1254 i, p1.x, p1.y, p1.z, p1.m, p2.x, p2.y, p2.z, p2.m);
1255
1256
1257 /* Find the length of this segment */
1258 slength = distance2d_pt_pt((POINT2D *)p1ptr, (POINT2D *)p2ptr);
1259
1260 /*
1261 * We are before requested start.
1262 */
1263 if ( state == 0 ) /* before */
1264 {
1265
1266 LWDEBUG(3, " Before start");
1267
1268 if ( fabs ( from - ( tlength + slength ) ) <= tolerance )
1269 {
1270
1271 LWDEBUG(3, " Second point is our start");
1272
1273 /*
1274 * Second point is our start
1275 */
1276 ptarray_append_point(dpa, &p2, LW_FALSE);
1277 state=1; /* we're inside now */
1278 goto END;
1279 }
1280
1281 else if ( fabs(from - tlength) <= tolerance )
1282 {
1283
1284 LWDEBUG(3, " First point is our start");
1285
1286 /*
1287 * First point is our start
1288 */
1289 ptarray_append_point(dpa, &p1, LW_FALSE);
1290
1291 /*
1292 * We're inside now, but will check
1293 * 'to' point as well
1294 */
1295 state=1;
1296 }
1297
1298 /*
1299 * Didn't reach the 'from' point,
1300 * nothing to do
1301 */
1302 else if ( from > tlength + slength ) goto END;
1303
1304 else /* tlength < from < tlength+slength */
1305 {
1306
1307 LWDEBUG(3, " Seg contains first point");
1308
1309 /*
1310 * Our start is between first and
1311 * second point
1312 */
1313 dseg = (from - tlength) / slength;
1314
1315 interpolate_point4d(&p1, &p2, &pt, dseg);
1316
1317 ptarray_append_point(dpa, &pt, LW_FALSE);
1318
1319 /*
1320 * We're inside now, but will check
1321 * 'to' point as well
1322 */
1323 state=1;
1324 }
1325 }
1326
1327 if ( state == 1 ) /* inside */
1328 {
1329
1330 LWDEBUG(3, " Inside");
1331
1332 /*
1333 * 'to' point is our second point.
1334 */
1335 if ( fabs(to - ( tlength + slength ) ) <= tolerance )
1336 {
1337
1338 LWDEBUG(3, " Second point is our end");
1339
1340 ptarray_append_point(dpa, &p2, LW_FALSE);
1341 break; /* substring complete */
1342 }
1343
1344 /*
1345 * 'to' point is our first point.
1346 * (should only happen if 'to' is 0)
1347 */
1348 else if ( fabs(to - tlength) <= tolerance )
1349 {
1350
1351 LWDEBUG(3, " First point is our end");
1352
1353 ptarray_append_point(dpa, &p1, LW_FALSE);
1354
1355 break; /* substring complete */
1356 }
1357
1358 /*
1359 * Didn't reach the 'end' point,
1360 * just copy second point
1361 */
1362 else if ( to > tlength + slength )
1363 {
1364 ptarray_append_point(dpa, &p2, LW_FALSE);
1365 goto END;
1366 }
1367
1368 /*
1369 * 'to' point falls on this segment
1370 * Interpolate and break.
1371 */
1372 else if ( to < tlength + slength )
1373 {
1374
1375 LWDEBUG(3, " Seg contains our end");
1376
1377 dseg = (to - tlength) / slength;
1378 interpolate_point4d(&p1, &p2, &pt, dseg);
1379
1380 ptarray_append_point(dpa, &pt, LW_FALSE);
1381
1382 break;
1383 }
1384
1385 else
1386 {
1387 LWDEBUG(3, "Unhandled case");
1388 }
1389 }
1390
1391
1392END:
1393
1394 tlength += slength;
1395 memcpy(&p1, &p2, sizeof(POINT4D));
1396 }
1397
1398 LWDEBUGF(3, "Out of loop, ptarray has %d points", dpa->npoints);
1399
1400 return dpa;
1401}
1402
1403/*
1404 * Write into the *ret argument coordinates of the closes point on
1405 * the given segment to the reference input point.
1406 */
1407void
1408closest_point_on_segment(const POINT4D *p, const POINT4D *A, const POINT4D *B, POINT4D *ret)
1409{
1410 double r;
1411
1412 if ( FP_EQUALS(A->x, B->x) && FP_EQUALS(A->y, B->y) )
1413 {
1414 *ret = *A;
1415 return;
1416 }
1417
1418 /*
1419 * We use comp.graphics.algorithms Frequently Asked Questions method
1420 *
1421 * (1) AC dot AB
1422 * r = ----------
1423 * ||AB||^2
1424 * r has the following meaning:
1425 * r=0 P = A
1426 * r=1 P = B
1427 * r<0 P is on the backward extension of AB
1428 * r>1 P is on the forward extension of AB
1429 * 0<r<1 P is interior to AB
1430 *
1431 */
1432 r = ( (p->x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) );
1433
1434 if (r<=0)
1435 {
1436 *ret = *A;
1437 return;
1438 }
1439 if (r>=1)
1440 {
1441 *ret = *B;
1442 return;
1443 }
1444
1445 ret->x = A->x + ( (B->x - A->x) * r );
1446 ret->y = A->y + ( (B->y - A->y) * r );
1447 ret->z = A->z + ( (B->z - A->z) * r );
1448 ret->m = A->m + ( (B->m - A->m) * r );
1449}
1450
1451int
1452ptarray_closest_segment_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
1453{
1454 const POINT2D *start = getPoint2d_cp(pa, 0), *end = NULL;
1455 uint32_t t, seg=0;
1456 double mindist=DBL_MAX;
1457
1458 /* Loop through pointarray looking for nearest segment */
1459 for (t=1; t<pa->npoints; t++)
1460 {
1461 double dist_sqr;
1462 end = getPoint2d_cp(pa, t);
1463 dist_sqr = distance2d_sqr_pt_seg(qp, start, end);
1464
1465 if (dist_sqr < mindist)
1466 {
1467 mindist = dist_sqr;
1468 seg=t-1;
1469 if ( mindist == 0 )
1470 {
1471 LWDEBUG(3, "Breaking on mindist=0");
1472 break;
1473 }
1474 }
1475
1476 start = end;
1477 }
1478
1479 if ( dist ) *dist = sqrt(mindist);
1480 return seg;
1481}
1482
1483
1484int
1485ptarray_closest_vertex_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
1486{
1487 uint32_t t, pn=0;
1488 const POINT2D *p;
1489 double mindist = DBL_MAX;
1490
1491 /* Loop through pointarray looking for nearest segment */
1492 for (t=0; t<pa->npoints; t++)
1493 {
1494 double dist_sqr;
1495 p = getPoint2d_cp(pa, t);
1496 dist_sqr = distance2d_sqr_pt_pt(p, qp);
1497
1498 if (dist_sqr < mindist)
1499 {
1500 mindist = dist_sqr;
1501 pn = t;
1502 if ( mindist == 0 )
1503 {
1504 LWDEBUG(3, "Breaking on mindist=0");
1505 break;
1506 }
1507 }
1508 }
1509 if ( dist ) *dist = sqrt(mindist);
1510 return pn;
1511}
1512
1513/*
1514 * Given a point, returns the location of closest point on pointarray
1515 * and, optionally, it's actual distance from the point array.
1516 */
1517double
1518ptarray_locate_point(const POINTARRAY *pa, const POINT4D *p4d, double *mindistout, POINT4D *proj4d)
1519{
1520 double mindist=DBL_MAX;
1521 double tlen, plen;
1522 uint32_t t, seg=0;
1523 POINT4D start4d, end4d, projtmp;
1524 POINT2D proj, p;
1525 const POINT2D *start = NULL, *end = NULL;
1526
1527 /* Initialize our 2D copy of the input parameter */
1528 p.x = p4d->x;
1529 p.y = p4d->y;
1530
1531 if ( ! proj4d ) proj4d = &projtmp;
1532
1533 /* Check for special cases (length 0 and 1) */
1534 if ( pa->npoints <= 1 )
1535 {
1536 if ( pa->npoints == 1 )
1537 {
1538 getPoint4d_p(pa, 0, proj4d);
1539 if ( mindistout )
1540 *mindistout = distance2d_pt_pt(&p, getPoint2d_cp(pa, 0));
1541 }
1542 return 0.0;
1543 }
1544
1545 start = getPoint2d_cp(pa, 0);
1546 /* Loop through pointarray looking for nearest segment */
1547 for (t=1; t<pa->npoints; t++)
1548 {
1549 double dist_sqr;
1550 end = getPoint2d_cp(pa, t);
1551 dist_sqr = distance2d_sqr_pt_seg(&p, start, end);
1552
1553 if (dist_sqr < mindist)
1554 {
1555 mindist = dist_sqr;
1556 seg=t-1;
1557 if ( mindist == 0 )
1558 {
1559 LWDEBUG(3, "Breaking on mindist=0");
1560 break;
1561 }
1562 }
1563
1564 start = end;
1565 }
1566 mindist = sqrt(mindist);
1567
1568 if ( mindistout ) *mindistout = mindist;
1569
1570 LWDEBUGF(3, "Closest segment: %d", seg);
1571 LWDEBUGF(3, "mindist: %g", mindist);
1572
1573 /*
1574 * We need to project the
1575 * point on the closest segment.
1576 */
1577 getPoint4d_p(pa, seg, &start4d);
1578 getPoint4d_p(pa, seg+1, &end4d);
1579 closest_point_on_segment(p4d, &start4d, &end4d, proj4d);
1580
1581 /* Copy 4D values into 2D holder */
1582 proj.x = proj4d->x;
1583 proj.y = proj4d->y;
1584
1585 LWDEBUGF(3, "Closest segment:%d, npoints:%d", seg, pa->npoints);
1586
1587 /* For robustness, force 1 when closest point == endpoint */
1588 if ( (seg >= (pa->npoints-2)) && p2d_same(&proj, end) )
1589 {
1590 return 1.0;
1591 }
1592
1593 LWDEBUGF(3, "Closest point on segment: %g,%g", proj.x, proj.y);
1594
1595 tlen = ptarray_length_2d(pa);
1596
1597 LWDEBUGF(3, "tlen %g", tlen);
1598
1599 /* Location of any point on a zero-length line is 0 */
1600 /* See http://trac.osgeo.org/postgis/ticket/1772#comment:2 */
1601 if ( tlen == 0 ) return 0;
1602
1603 plen=0;
1604 start = getPoint2d_cp(pa, 0);
1605 for (t=0; t<seg; t++, start=end)
1606 {
1607 end = getPoint2d_cp(pa, t+1);
1608 plen += distance2d_pt_pt(start, end);
1609
1610 LWDEBUGF(4, "Segment %d made plen %g", t, plen);
1611 }
1612
1613 plen+=distance2d_pt_pt(&proj, start);
1614
1615 LWDEBUGF(3, "plen %g, tlen %g", plen, tlen);
1616
1617 /* Floating point arithmetic is not reliable, make sure we return values [0,1] */
1618 double result = plen / tlen;
1619 if ( result < 0.0 ) {
1620 result = 0.0;
1621 } else if ( result > 1.0 ) {
1622 result = 1.0;
1623 }
1624 return result;
1625}
1626
1636void
1638{
1639 uint32_t i;
1640 double x;
1641
1642 for (i=0; i<pa->npoints; i++)
1643 {
1644 memcpy(&x, getPoint_internal(pa, i), sizeof(double));
1645 if ( x < 0 ) x+= 360;
1646 else if ( x > 180 ) x -= 360;
1647 memcpy(getPoint_internal(pa, i), &x, sizeof(double));
1648 }
1649}
1650
1651
1652/*
1653 * Returns a POINTARRAY with consecutive equal points
1654 * removed. Equality test on all dimensions of input.
1655 *
1656 * Always returns a newly allocated object.
1657 */
1658static POINTARRAY *
1659ptarray_remove_repeated_points_minpoints(const POINTARRAY *in, double tolerance, int minpoints)
1660{
1661 POINTARRAY *out = ptarray_clone_deep(in);
1662 ptarray_remove_repeated_points_in_place(out, tolerance, minpoints);
1663 return out;
1664}
1665
1666POINTARRAY *
1667ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance)
1668{
1669 return ptarray_remove_repeated_points_minpoints(in, tolerance, 2);
1670}
1671
1672
1673void
1674ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, uint32_t min_points)
1675{
1676 uint32_t i;
1677 double tolsq = tolerance * tolerance;
1678 const POINT2D *last = NULL;
1679 const POINT2D *pt;
1680 uint32_t n_points = pa->npoints;
1681 uint32_t n_points_out = 1;
1682 size_t pt_size = ptarray_point_size(pa);
1683
1684 double dsq = FLT_MAX;
1685
1686 /* No-op on short inputs */
1687 if ( n_points <= min_points ) return;
1688
1689 last = getPoint2d_cp(pa, 0);
1690 void *p_to = ((char *)last) + pt_size;
1691 for (i = 1; i < n_points; i++)
1692 {
1693 int last_point = (i == n_points - 1);
1694
1695 /* Look straight into the abyss */
1696 pt = getPoint2d_cp(pa, i);
1697
1698 /* Don't drop points if we are running short of points */
1699 if (n_points + n_points_out > min_points + i)
1700 {
1701 if (tolerance > 0.0)
1702 {
1703 /* Only drop points that are within our tolerance */
1704 dsq = distance2d_sqr_pt_pt(last, pt);
1705 /* Allow any point but the last one to be dropped */
1706 if (!last_point && dsq <= tolsq)
1707 {
1708 continue;
1709 }
1710 }
1711 else
1712 {
1713 /* At tolerance zero, only skip exact dupes */
1714 if (memcmp((char*)pt, (char*)last, pt_size) == 0)
1715 continue;
1716 }
1717
1718 /* Got to last point, and it's not very different from */
1719 /* the point that preceded it. We want to keep the last */
1720 /* point, not the second-to-last one, so we pull our write */
1721 /* index back one value */
1722 if (last_point && n_points_out > 1 && tolerance > 0.0 && dsq <= tolsq)
1723 {
1724 n_points_out--;
1725 p_to = (char*)p_to - pt_size;
1726 }
1727 }
1728
1729 /* Compact all remaining values to front of array */
1730 memcpy(p_to, pt, pt_size);
1731 n_points_out++;
1732 p_to = (char*)p_to + pt_size;
1733 last = pt;
1734 }
1735 /* Adjust array length */
1736 pa->npoints = n_points_out;
1737 return;
1738}
1739
1740/* Out of the points in pa [itfist .. itlast], finds the one that's farthest away from
1741 * the segment determined by pts[itfist] and pts[itlast].
1742 * Returns itfirst if no point was found further away than max_distance_sqr
1743 */
1744static uint32_t
1745ptarray_dp_findsplit_in_place(const POINTARRAY *pts, uint32_t it_first, uint32_t it_last, double max_distance_sqr)
1746{
1747 uint32_t split = it_first;
1748 if ((it_first - it_last) < 2)
1749 return it_first;
1750
1751 const POINT2D *A = getPoint2d_cp(pts, it_first);
1752 const POINT2D *B = getPoint2d_cp(pts, it_last);
1753
1754 if (distance2d_sqr_pt_pt(A, B) < DBL_EPSILON)
1755 {
1756 /* If p1 == p2, we can just calculate the distance from each point to A */
1757 for (uint32_t itk = it_first + 1; itk < it_last; itk++)
1758 {
1759 const POINT2D *pk = getPoint2d_cp(pts, itk);
1760 double distance_sqr = distance2d_sqr_pt_pt(pk, A);
1761 if (distance_sqr > max_distance_sqr)
1762 {
1763 split = itk;
1764 max_distance_sqr = distance_sqr;
1765 }
1766 }
1767 return split;
1768 }
1769
1770 /* This is based on distance2d_sqr_pt_seg, but heavily inlined here to avoid recalculations */
1771 double ba_x = (B->x - A->x);
1772 double ba_y = (B->y - A->y);
1773 double ab_length_sqr = (ba_x * ba_x + ba_y * ba_y);
1774 /* To avoid the division by ab_length_sqr in the 3rd path, we normalize here
1775 * and multiply in the first two paths [(dot_ac_ab < 0) and (> ab_length_sqr)] */
1776 max_distance_sqr *= ab_length_sqr;
1777 for (uint32_t itk = it_first + 1; itk < it_last; itk++)
1778 {
1779 const POINT2D *C = getPoint2d_cp(pts, itk);
1780 double distance_sqr;
1781 double ca_x = (C->x - A->x);
1782 double ca_y = (C->y - A->y);
1783 double dot_ac_ab = (ca_x * ba_x + ca_y * ba_y);
1784
1785 if (dot_ac_ab <= 0.0)
1786 {
1787 distance_sqr = distance2d_sqr_pt_pt(C, A) * ab_length_sqr;
1788 }
1789 else if (dot_ac_ab >= ab_length_sqr)
1790 {
1791 distance_sqr = distance2d_sqr_pt_pt(C, B) * ab_length_sqr;
1792 }
1793 else
1794 {
1795 double s_numerator = ca_x * ba_y - ca_y * ba_x;
1796 distance_sqr = s_numerator * s_numerator; /* Missing division by ab_length_sqr on purpose */
1797 }
1798
1799 if (distance_sqr > max_distance_sqr)
1800 {
1801 split = itk;
1802 max_distance_sqr = distance_sqr;
1803 }
1804 }
1805 return split;
1806}
1807
1808/* O(N) simplification for tolerance = 0 */
1809static void
1811{
1812 uint32_t kept_it = 0;
1813 uint32_t last_it = pa->npoints - 1;
1814 const POINT2D *kept_pt = getPoint2d_cp(pa, 0);
1815 const size_t pt_size = ptarray_point_size(pa);
1816
1817 for (uint32_t i = 1; i < last_it; i++)
1818 {
1819 const POINT2D *curr_pt = getPoint2d_cp(pa, i);
1820 const POINT2D *next_pt = getPoint2d_cp(pa, i + 1);
1821
1822 double ba_x = next_pt->x - kept_pt->x;
1823 double ba_y = next_pt->y - kept_pt->y;
1824 double ab_length_sqr = ba_x * ba_x + ba_y * ba_y;
1825
1826 double ca_x = curr_pt->x - kept_pt->x;
1827 double ca_y = curr_pt->y - kept_pt->y;
1828 double dot_ac_ab = ca_x * ba_x + ca_y * ba_y;
1829 double s_numerator = ca_x * ba_y - ca_y * ba_x;
1830
1831 if (p2d_same(kept_pt, next_pt) ||
1832 dot_ac_ab < 0.0 ||
1833 dot_ac_ab > ab_length_sqr ||
1834 s_numerator != 0)
1835
1836 {
1837 kept_it++;
1838 kept_pt = curr_pt;
1839 if (kept_it != i)
1840 memcpy(pa->serialized_pointlist + pt_size * kept_it,
1841 pa->serialized_pointlist + pt_size * i,
1842 pt_size);
1843 }
1844 }
1845
1846 /* Append last point */
1847 kept_it++;
1848 if (kept_it != last_it)
1849 memcpy(pa->serialized_pointlist + pt_size * kept_it,
1850 pa->serialized_pointlist + pt_size * last_it,
1851 pt_size);
1852 pa->npoints = kept_it + 1;
1853}
1854
1855void
1856ptarray_simplify_in_place(POINTARRAY *pa, double tolerance, uint32_t minpts)
1857{
1858 /* Do not try to simplify really short things */
1859 if (pa->npoints < 3 || pa->npoints <= minpts)
1860 return;
1861
1862 if (tolerance == 0 && minpts <= 2)
1863 {
1865 return;
1866 }
1867
1868 /* We use this array to keep track of the points we are keeping, so
1869 * we store just TRUE / FALSE in their position */
1870 uint8_t *kept_points = lwalloc(sizeof(uint8_t) * pa->npoints);
1871 memset(kept_points, LW_FALSE, sizeof(uint8_t) * pa->npoints);
1872 kept_points[0] = LW_TRUE;
1873 kept_points[pa->npoints - 1] = LW_TRUE;
1874 uint32_t keptn = 2;
1875
1876 /* We use this array as a stack to store the iterators that we are going to need
1877 * in the following steps.
1878 * This is ~10% faster than iterating over @kept_points looking for them
1879 */
1880 uint32_t *iterator_stack = lwalloc(sizeof(uint32_t) * pa->npoints);
1881 iterator_stack[0] = 0;
1882 uint32_t iterator_stack_size = 1;
1883
1884 uint32_t it_first = 0;
1885 uint32_t it_last = pa->npoints - 1;
1886
1887 const double tolerance_sqr = tolerance * tolerance;
1888 /* For the first @minpts points we ignore the tolerance */
1889 double it_tol = keptn >= minpts ? tolerance_sqr : -1.0;
1890
1891 while (iterator_stack_size)
1892 {
1893 uint32_t split = ptarray_dp_findsplit_in_place(pa, it_first, it_last, it_tol);
1894 if (split == it_first)
1895 {
1896 it_first = it_last;
1897 it_last = iterator_stack[--iterator_stack_size];
1898 }
1899 else
1900 {
1901 kept_points[split] = LW_TRUE;
1902 keptn++;
1903
1904 iterator_stack[iterator_stack_size++] = it_last;
1905 it_last = split;
1906 it_tol = keptn >= minpts ? tolerance_sqr : -1.0;
1907 }
1908 }
1909
1910 const size_t pt_size = ptarray_point_size(pa);
1911 /* The first point is already in place, so we don't need to copy it */
1912 size_t kept_it = 1;
1913 if (keptn == 2)
1914 {
1915 /* If there are 2 points remaining, it has to be first and last as
1916 * we added those at the start */
1917 memcpy(pa->serialized_pointlist + pt_size * kept_it,
1918 pa->serialized_pointlist + pt_size * (pa->npoints - 1),
1919 pt_size);
1920 }
1921 else if (pa->npoints != keptn) /* We don't need to move any points if we are keeping them all */
1922 {
1923 for (uint32_t i = 1; i < pa->npoints; i++)
1924 {
1925 if (kept_points[i])
1926 {
1927 memcpy(pa->serialized_pointlist + pt_size * kept_it,
1928 pa->serialized_pointlist + pt_size * i,
1929 pt_size);
1930 kept_it++;
1931 }
1932 }
1933 }
1934 pa->npoints = keptn;
1935
1936 lwfree(kept_points);
1937 lwfree(iterator_stack);
1938}
1939
1940/************************************************************************/
1941
1947double
1949{
1950 double dist = 0.0;
1951 uint32_t i;
1952 const POINT2D *a1;
1953 const POINT2D *a2;
1954 const POINT2D *a3;
1955
1956 if ( pts->npoints % 2 != 1 )
1957 lwerror("arc point array with even number of points");
1958
1959 a1 = getPoint2d_cp(pts, 0);
1960
1961 for ( i=2; i < pts->npoints; i += 2 )
1962 {
1963 a2 = getPoint2d_cp(pts, i-1);
1964 a3 = getPoint2d_cp(pts, i);
1965 dist += lw_arc_length(a1, a2, a3);
1966 a1 = a3;
1967 }
1968 return dist;
1969}
1970
1974double
1976{
1977 double dist = 0.0;
1978 uint32_t i;
1979 const POINT2D *frm;
1980 const POINT2D *to;
1981
1982 if ( pts->npoints < 2 ) return 0.0;
1983
1984 frm = getPoint2d_cp(pts, 0);
1985
1986 for ( i=1; i < pts->npoints; i++ )
1987 {
1988 to = getPoint2d_cp(pts, i);
1989
1990 dist += sqrt( ((frm->x - to->x)*(frm->x - to->x)) +
1991 ((frm->y - to->y)*(frm->y - to->y)) );
1992
1993 frm = to;
1994 }
1995 return dist;
1996}
1997
2002double
2004{
2005 double dist = 0.0;
2006 uint32_t i;
2007 POINT3DZ frm;
2008 POINT3DZ to;
2009
2010 if ( pts->npoints < 2 ) return 0.0;
2011
2012 /* compute 2d length if 3d is not available */
2013 if ( ! FLAGS_GET_Z(pts->flags) ) return ptarray_length_2d(pts);
2014
2015 getPoint3dz_p(pts, 0, &frm);
2016 for ( i=1; i < pts->npoints; i++ )
2017 {
2018 getPoint3dz_p(pts, i, &to);
2019 dist += sqrt( ((frm.x - to.x)*(frm.x - to.x)) +
2020 ((frm.y - to.y)*(frm.y - to.y)) +
2021 ((frm.z - to.z)*(frm.z - to.z)) );
2022 frm = to;
2023 }
2024 return dist;
2025}
2026
2027
2028
2032void
2034{
2035 if (FLAGS_GET_Z(pa->flags))
2036 {
2037 for (uint32_t i = 0; i < pa->npoints; i++)
2038 {
2039 POINT4D *p4d = (POINT4D *)(getPoint_internal(pa, i));
2040 double x = p4d->x;
2041 double y = p4d->y;
2042 double z = p4d->z;
2043 p4d->x = a->afac * x + a->bfac * y + a->cfac * z + a->xoff;
2044 p4d->y = a->dfac * x + a->efac * y + a->ffac * z + a->yoff;
2045 p4d->z = a->gfac * x + a->hfac * y + a->ifac * z + a->zoff;
2046 }
2047 }
2048 else
2049 {
2050 for (uint32_t i = 0; i < pa->npoints; i++)
2051 {
2052 POINT2D *pt = (POINT2D *)(getPoint_internal(pa, i));
2053 double x = pt->x;
2054 double y = pt->y;
2055 pt->x = a->afac * x + a->bfac * y + a->xoff;
2056 pt->y = a->dfac * x + a->efac * y + a->yoff;
2057 }
2058 }
2059}
2060
2065#if 0
2066static int gluInvertMatrix(const double *m, double *invOut)
2067{
2068 double inv[16], det;
2069 int i;
2070
2071 inv[0] = m[5] * m[10] * m[15] -
2072 m[5] * m[11] * m[14] -
2073 m[9] * m[6] * m[15] +
2074 m[9] * m[7] * m[14] +
2075 m[13] * m[6] * m[11] -
2076 m[13] * m[7] * m[10];
2077
2078 inv[4] = -m[4] * m[10] * m[15] +
2079 m[4] * m[11] * m[14] +
2080 m[8] * m[6] * m[15] -
2081 m[8] * m[7] * m[14] -
2082 m[12] * m[6] * m[11] +
2083 m[12] * m[7] * m[10];
2084
2085 inv[8] = m[4] * m[9] * m[15] -
2086 m[4] * m[11] * m[13] -
2087 m[8] * m[5] * m[15] +
2088 m[8] * m[7] * m[13] +
2089 m[12] * m[5] * m[11] -
2090 m[12] * m[7] * m[9];
2091
2092 inv[12] = -m[4] * m[9] * m[14] +
2093 m[4] * m[10] * m[13] +
2094 m[8] * m[5] * m[14] -
2095 m[8] * m[6] * m[13] -
2096 m[12] * m[5] * m[10] +
2097 m[12] * m[6] * m[9];
2098
2099 inv[1] = -m[1] * m[10] * m[15] +
2100 m[1] * m[11] * m[14] +
2101 m[9] * m[2] * m[15] -
2102 m[9] * m[3] * m[14] -
2103 m[13] * m[2] * m[11] +
2104 m[13] * m[3] * m[10];
2105
2106 inv[5] = m[0] * m[10] * m[15] -
2107 m[0] * m[11] * m[14] -
2108 m[8] * m[2] * m[15] +
2109 m[8] * m[3] * m[14] +
2110 m[12] * m[2] * m[11] -
2111 m[12] * m[3] * m[10];
2112
2113 inv[9] = -m[0] * m[9] * m[15] +
2114 m[0] * m[11] * m[13] +
2115 m[8] * m[1] * m[15] -
2116 m[8] * m[3] * m[13] -
2117 m[12] * m[1] * m[11] +
2118 m[12] * m[3] * m[9];
2119
2120 inv[13] = m[0] * m[9] * m[14] -
2121 m[0] * m[10] * m[13] -
2122 m[8] * m[1] * m[14] +
2123 m[8] * m[2] * m[13] +
2124 m[12] * m[1] * m[10] -
2125 m[12] * m[2] * m[9];
2126
2127 inv[2] = m[1] * m[6] * m[15] -
2128 m[1] * m[7] * m[14] -
2129 m[5] * m[2] * m[15] +
2130 m[5] * m[3] * m[14] +
2131 m[13] * m[2] * m[7] -
2132 m[13] * m[3] * m[6];
2133
2134 inv[6] = -m[0] * m[6] * m[15] +
2135 m[0] * m[7] * m[14] +
2136 m[4] * m[2] * m[15] -
2137 m[4] * m[3] * m[14] -
2138 m[12] * m[2] * m[7] +
2139 m[12] * m[3] * m[6];
2140
2141 inv[10] = m[0] * m[5] * m[15] -
2142 m[0] * m[7] * m[13] -
2143 m[4] * m[1] * m[15] +
2144 m[4] * m[3] * m[13] +
2145 m[12] * m[1] * m[7] -
2146 m[12] * m[3] * m[5];
2147
2148 inv[14] = -m[0] * m[5] * m[14] +
2149 m[0] * m[6] * m[13] +
2150 m[4] * m[1] * m[14] -
2151 m[4] * m[2] * m[13] -
2152 m[12] * m[1] * m[6] +
2153 m[12] * m[2] * m[5];
2154
2155 inv[3] = -m[1] * m[6] * m[11] +
2156 m[1] * m[7] * m[10] +
2157 m[5] * m[2] * m[11] -
2158 m[5] * m[3] * m[10] -
2159 m[9] * m[2] * m[7] +
2160 m[9] * m[3] * m[6];
2161
2162 inv[7] = m[0] * m[6] * m[11] -
2163 m[0] * m[7] * m[10] -
2164 m[4] * m[2] * m[11] +
2165 m[4] * m[3] * m[10] +
2166 m[8] * m[2] * m[7] -
2167 m[8] * m[3] * m[6];
2168
2169 inv[11] = -m[0] * m[5] * m[11] +
2170 m[0] * m[7] * m[9] +
2171 m[4] * m[1] * m[11] -
2172 m[4] * m[3] * m[9] -
2173 m[8] * m[1] * m[7] +
2174 m[8] * m[3] * m[5];
2175
2176 inv[15] = m[0] * m[5] * m[10] -
2177 m[0] * m[6] * m[9] -
2178 m[4] * m[1] * m[10] +
2179 m[4] * m[2] * m[9] +
2180 m[8] * m[1] * m[6] -
2181 m[8] * m[2] * m[5];
2182
2183 det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
2184
2185 if (det == 0)
2186 return LW_FALSE;
2187
2188 det = 1.0 / det;
2189
2190 for (i = 0; i < 16; i++)
2191 invOut[i] = inv[i] * det;
2192
2193 return LW_TRUE;
2194}
2195#endif
2196
2200void
2202{
2203 uint32_t i;
2204 POINT4D p4d;
2205 LWDEBUG(3, "ptarray_scale start");
2206 for (i=0; i<pa->npoints; i++)
2207 {
2208 getPoint4d_p(pa, i, &p4d);
2209 p4d.x *= fact->x;
2210 p4d.y *= fact->y;
2211 p4d.z *= fact->z;
2212 p4d.m *= fact->m;
2213 ptarray_set_point4d(pa, i, &p4d);
2214 }
2215 LWDEBUG(3, "ptarray_scale end");
2216}
2217
2218int
2220{
2221 return getPoint4d_p(pa, 0, pt);
2222}
2223
2224
2225/*
2226 * Stick an array of points to the given gridspec.
2227 * Return "gridded" points in *outpts and their number in *outptsn.
2228 *
2229 * Two consecutive points falling on the same grid cell are collapsed
2230 * into one single point.
2231 *
2232 */
2233void
2235{
2236 uint32_t j = 0;
2237 POINT4D *p, *p_out = NULL;
2238 double x, y, z = 0, m = 0;
2239 uint32_t ndims = FLAGS_NDIMS(pa->flags);
2240 uint32_t has_z = FLAGS_GET_Z(pa->flags);
2241 uint32_t has_m = FLAGS_GET_M(pa->flags);
2242
2243 for (uint32_t i = 0; i < pa->npoints; i++)
2244 {
2245 /* Look straight into the abyss */
2246 p = (POINT4D *)(getPoint_internal(pa, i));
2247 x = p->x;
2248 y = p->y;
2249 if (ndims > 2)
2250 z = p->z;
2251 if (ndims > 3)
2252 m = p->m;
2253
2254 /*
2255 * See https://github.com/libgeos/geos/pull/956
2256 * We use scale for rounding when gridsize is < 1 and
2257 * gridsize for rounding when scale < 1.
2258 */
2259 if (grid->xsize > 0) {
2260 if (grid->xsize < 1)
2261 x = rint((x - grid->ipx) * grid->xscale) / grid->xscale + grid->ipx;
2262 else
2263 x = rint((x - grid->ipx) / grid->xsize) * grid->xsize + grid->ipx;
2264 }
2265
2266 if (grid->ysize > 0) {
2267 if (grid->ysize < 1)
2268 y = rint((y - grid->ipy) * grid->yscale) / grid->yscale + grid->ipy;
2269 else
2270 y = rint((y - grid->ipy) / grid->ysize) * grid->ysize + grid->ipy;
2271 }
2272
2273 /* Read and round this point */
2274 /* Z is always in third position */
2275 if (has_z && grid->zsize > 0)
2276 z = rint((z - grid->ipz) / grid->zsize) * grid->zsize + grid->ipz;
2277
2278 /* M might be in 3rd or 4th position */
2279 if (has_m && grid->msize > 0)
2280 {
2281 /* In POINT ZM, M is in 4th position, in POINT M, M is in 3rd position which is Z in POINT4D */
2282 if (has_z)
2283 m = rint((m - grid->ipm) / grid->msize) * grid->msize + grid->ipm;
2284 else
2285 z = rint((z - grid->ipm) / grid->msize) * grid->msize + grid->ipm;
2286 }
2287
2288 /* Skip duplicates */
2289 if (p_out &&
2290 p_out->x == x &&
2291 p_out->y == y &&
2292 (ndims > 2 ? p_out->z == z : 1) &&
2293 (ndims > 3 ? p_out->m == m : 1))
2294 {
2295 continue;
2296 }
2297
2298 /* Write rounded values into the next available point */
2299 p_out = (POINT4D *)(getPoint_internal(pa, j++));
2300 p_out->x = x;
2301 p_out->y = y;
2302 if (ndims > 2)
2303 p_out->z = z;
2304 if (ndims > 3)
2305 p_out->m = m;
2306 }
2307
2308 /* Update output ptarray length */
2309 pa->npoints = j;
2310 return;
2311}
2312
2313int
2315{
2316 const POINT2D *pt;
2317 int n = 0;
2318 uint32_t i;
2319 for ( i = 0; i < pa->npoints; i++ )
2320 {
2321 pt = getPoint2d_cp(pa, i);
2322 if ( gbox_contains_point2d(gbox, pt) )
2323 n++;
2324 }
2325 return n;
2326}
2327
2328
2329/*
2330 * Reorder the vertices of a closed pointarray so that the
2331 * given point is the first/last one.
2332 *
2333 * Error out if pointarray is not closed or it does not
2334 * contain the given point.
2335 */
2336int
2338{
2339 POINTARRAY *tmp;
2340 int found;
2341 uint32_t it;
2342 int ptsize;
2343
2344 if ( ! ptarray_is_closed_2d(pa) )
2345 {
2346 lwerror("ptarray_scroll_in_place: input POINTARRAY is not closed");
2347 return LW_FAILURE;
2348 }
2349
2350 ptsize = ptarray_point_size(pa);
2351
2352 /* Find the point in the array */
2353 found = 0;
2354 for ( it = 0; it < pa->npoints; ++it )
2355 {
2356 if ( ! memcmp(getPoint_internal(pa, it), pt, ptsize) )
2357 {
2358 found = 1;
2359 break;
2360 }
2361 }
2362
2363 if ( ! found )
2364 {
2365 lwerror("ptarray_scroll_in_place: input POINTARRAY does not contain the given point");
2366 return LW_FAILURE;
2367 }
2368
2369 if ( 0 == it )
2370 {
2371 /* Point is already the start/end point, just clone the input */
2372 return LW_SUCCESS;
2373 }
2374
2375 /* TODO: reduce allocations */
2377
2378 memset(getPoint_internal(tmp, 0), 0, (size_t)ptsize * pa->npoints);
2379 /* Copy the block from found point to last point into the output array */
2380 memcpy(
2381 getPoint_internal(tmp, 0),
2382 getPoint_internal(pa, it),
2383 (size_t)ptsize * ( pa->npoints - it )
2384 );
2385
2386 /* Copy the block from second point to the found point into the last portion of the
2387 * return */
2388 memcpy(
2389 getPoint_internal(tmp, pa->npoints - it),
2390 getPoint_internal(pa, 1),
2391 (size_t)ptsize * ( it )
2392 );
2393
2394 /* Copy the resulting pointarray back to source one */
2395 memcpy(
2396 getPoint_internal(pa, 0),
2397 getPoint_internal(tmp, 0),
2398 (size_t)ptsize * ( pa->npoints )
2399 );
2400
2401 ptarray_free(tmp);
2402
2403 return LW_SUCCESS;
2404}
char * r
Definition cu_in_wkt.c:24
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition cu_print.c:267
int gbox_contains_point2d(const GBOX *g, const POINT2D *p)
Definition gbox.c:362
int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox)
Definition gbox.c:465
#define LW_FALSE
Definition liblwgeom.h:94
void * lwrealloc(void *mem, size_t size)
Definition lwutil.c:242
double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
Definition measures.c:2344
#define LW_FAILURE
Definition liblwgeom.h:96
void interpolate_point4d(const POINT4D *A, const POINT4D *B, POINT4D *I, double F)
Find interpolation point I between point A and point B so that the len(AI) == len(AB)*F and I falls o...
Definition lwgeom_api.c:646
#define LW_SUCCESS
Definition liblwgeom.h:97
#define FLAGS_GET_READONLY(flags)
Definition liblwgeom.h:169
#define FLAGS_GET_Z(flags)
Definition liblwgeom.h:165
void * lwalloc(size_t size)
Definition lwutil.c:227
int getPoint3dz_p(const POINTARRAY *pa, uint32_t n, POINT3DZ *point)
Definition lwgeom_api.c:215
void lwfree(void *mem)
Definition lwutil.c:248
#define FLAGS_NDIMS(flags)
Definition liblwgeom.h:179
#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
double distance2d_sqr_pt_seg(const POINT2D *p, const POINT2D *A, const POINT2D *B)
Definition measures.c:2354
#define FLAGS_GET_ZM(flags)
Definition liblwgeom.h:180
#define FLAGS_SET_READONLY(flags, value)
Definition liblwgeom.h:176
lwflags_t lwflags(int hasz, int hasm, int geodetic)
Construct a new flags bitmask.
Definition lwutil.c:477
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:93
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition lwgeom_api.c:369
enum LWORD_T LWORD
Ordinate names.
#define LW_CLOCKWISE
Constants for orientation checking and forcing.
#define LW_INSIDE
Constants for point-in-polygon return values.
#define LW_BOUNDARY
#define LW_COUNTERCLOCKWISE
double lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
Returns the length of a circular arc segment.
#define LW_ON_INTERRUPT(x)
#define FP_MAX(A, B)
#define FP_MIN(A, B)
int lw_pt_on_segment(const POINT2D *p1, const POINT2D *p2, const POINT2D *p)
double lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result)
Determines the center of the circle defined by the three given points.
#define FP_EQUALS(A, B)
int lw_pt_in_seg(const POINT2D *P, const POINT2D *A1, const POINT2D *A2)
Returns true if P is between A1/A2.
#define LW_OUTSIDE
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition lwalgorithm.c:70
int lw_arc_is_pt(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
Returns true if arc A is actually a point (all vertices are the same) .
int lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
Returns true if P is on the same side of the plane partition defined by A1/A3 as A2 is.
Definition lwalgorithm.c:91
int p2d_same(const POINT2D *p1, const POINT2D *p2)
Definition lwalgorithm.c:57
static double det(double m00, double m01, double m10, double m11)
#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.
#define INT32_MAX
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition lwinline.h:75
static double distance2d_sqr_pt_pt(const POINT2D *p1, const POINT2D *p2)
Definition lwinline.h:33
static size_t ptarray_point_size(const POINTARRAY *pa)
Definition lwinline.h:56
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition lwinline.h:97
int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number)
Definition ptarray.c:1031
static uint32_t ptarray_dp_findsplit_in_place(const POINTARRAY *pts, uint32_t it_first, uint32_t it_last, double max_distance_sqr)
Definition ptarray.c:1745
int ptarray_remove_point(POINTARRAY *pa, uint32_t where)
Remove a point from an existing POINTARRAY.
Definition ptarray.c:259
void ptarray_longitude_shift(POINTARRAY *pa)
Longitude shift for a pointarray.
Definition ptarray.c:1637
int ptarray_closest_segment_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
Definition ptarray.c:1452
POINTARRAY * ptarray_clone_deep(const POINTARRAY *in)
Deep clone a pointarray (also clones serialized pointlist)
Definition ptarray.c:643
POINTARRAY * ptarray_clone(const POINTARRAY *in)
Clone a POINTARRAY object.
Definition ptarray.c:674
int ptarray_append_ptarray(POINTARRAY *pa1, POINTARRAY *pa2, double gap_tolerance)
Append a POINTARRAY, pa2 to the end of an existing POINTARRAY, pa1.
Definition ptarray.c:177
void ptarray_reverse_in_place(POINTARRAY *pa)
Definition ptarray.c:339
int ptarray_closest_vertex_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
Definition ptarray.c:1485
int ptarrayarc_contains_point(const POINTARRAY *pa, const POINT2D *pt)
For POINTARRAYs representing CIRCULARSTRINGS.
Definition ptarray.c:1123
int ptarray_is_closed_2d(const POINTARRAY *in)
Definition ptarray.c:710
POINTARRAY * ptarray_substring(POINTARRAY *ipa, double from, double to, double tolerance)
@d1 start location (distance from start / total distance) @d2 end location (distance from start / tot...
Definition ptarray.c:1211
POINTARRAY * ptarray_construct_reference_data(char hasz, char hasm, uint32_t npoints, uint8_t *ptlist)
Build a new POINTARRAY, but on top of someone else's ordinate array.
Definition ptarray.c:291
int ptarray_is_closed_z(const POINTARRAY *in)
Definition ptarray.c:736
int ptarray_raycast_intersections(const POINTARRAY *pa, const POINT2D *p, int *on_boundary)
Definition ptarray.c:827
double ptarray_length(const POINTARRAY *pts)
Find the 3d/2d length of the given POINTARRAY (depending on its dimensionality)
Definition ptarray.c:2003
double ptarray_signed_area(const POINTARRAY *pa)
Returns the area in cartesian units.
Definition ptarray.c:1143
static POINTARRAY * ptarray_remove_repeated_points_minpoints(const POINTARRAY *in, double tolerance, int minpoints)
Definition ptarray.c:1659
void closest_point_on_segment(const POINT4D *p, const POINT4D *A, const POINT4D *B, POINT4D *ret)
Definition ptarray.c:1408
int ptarray_startpoint(const POINTARRAY *pa, POINT4D *pt)
Definition ptarray.c:2219
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition ptarray.c:59
int ptarray_is_closed(const POINTARRAY *in)
Check for ring closure using whatever dimensionality is declared on the pointarray.
Definition ptarray.c:696
double ptarray_length_2d(const POINTARRAY *pts)
Find the 2d length of the given POINTARRAY (even if it's 3d)
Definition ptarray.c:1975
char ptarray_same(const POINTARRAY *pa1, const POINTARRAY *pa2)
Definition ptarray.c:484
int ptarray_is_closed_3d(const POINTARRAY *in)
Definition ptarray.c:723
void ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, uint32_t min_points)
Definition ptarray.c:1674
int ptarrayarc_raycast_intersections(const POINTARRAY *pa, const POINT2D *p, int *on_boundary)
Definition ptarray.c:944
int ptarray_scroll_in_place(POINTARRAY *pa, const POINT4D *pt)
Definition ptarray.c:2337
void ptarray_simplify_in_place(POINTARRAY *pa, double tolerance, uint32_t minpts)
Definition ptarray.c:1856
POINTARRAY * ptarray_flip_coordinates(POINTARRAY *pa)
Reverse X and Y axis on a given POINTARRAY.
Definition ptarray.c:368
int ptarray_insert_point(POINTARRAY *pa, const POINT4D *p, uint32_t where)
Insert a point into an existing POINTARRAY.
Definition ptarray.c:85
void ptarray_grid_in_place(POINTARRAY *pa, gridspec *grid)
Snap to grid.
Definition ptarray.c:2234
double ptarray_arc_length_2d(const POINTARRAY *pts)
Find the 2d length of the given POINTARRAY, using circular arc interpolation between each coordinate ...
Definition ptarray.c:1948
static int circle_raycast_intersections(const POINT2D *center, double radius, double ray, POINT2D *i0, POINT2D *i1)
Calculates the intersection points of a circle and a horizontal line.
Definition ptarray.c:903
POINTARRAY * ptarray_merge(POINTARRAY *pa1, POINTARRAY *pa2)
Merge two given POINTARRAY and returns a pointer on the new aggregate one.
Definition ptarray.c:612
void ptarray_affine(POINTARRAY *pa, const AFFINE *a)
Affine transform a pointarray.
Definition ptarray.c:2033
int ptarray_contains_point(const POINTARRAY *pa, const POINT2D *pt)
The following is based on the "Fast Winding Number Inclusion of a Point in a Polygon" algorithm by Da...
Definition ptarray.c:755
POINTARRAY * ptarray_segmentize2d(const POINTARRAY *ipa, double dist)
Returns a modified POINTARRAY so that no segment is longer than the given distance (computed using 2d...
Definition ptarray.c:413
POINTARRAY * ptarray_removePoint(POINTARRAY *pa, uint32_t which)
Remove a point from a pointarray.
Definition ptarray.c:582
int ptarray_has_orientation(const POINTARRAY *pa, int orientation)
Definition ptarray.c:1174
void ptarray_free(POINTARRAY *pa)
Definition ptarray.c:327
static void ptarray_simplify_in_place_tolerance0(POINTARRAY *pa)
Definition ptarray.c:1810
int ptarray_isccw(const POINTARRAY *pa)
Definition ptarray.c:1182
int ptarray_has_z(const POINTARRAY *pa)
Definition ptarray.c:37
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int repeated_points)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_FALSE,...
Definition ptarray.c:147
POINTARRAY * ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where)
Add a point in a pointarray.
Definition ptarray.c:530
void ptarray_scale(POINTARRAY *pa, const POINT4D *fact)
WARNING, make sure you send in only 16-member double arrays or obviously things will go pear-shaped f...
Definition ptarray.c:2201
void ptarray_swap_ordinates(POINTARRAY *pa, LWORD o1, LWORD o2)
Swap ordinate values o1 and o2 on a given POINTARRAY.
Definition ptarray.c:387
POINTARRAY * ptarray_construct_copy_data(char hasz, char hasm, uint32_t npoints, const uint8_t *ptlist)
Construct a new POINTARRAY, copying in the data from ptlist.
Definition ptarray.c:305
int ptarray_npoints_in_rect(const POINTARRAY *pa, const GBOX *gbox)
Definition ptarray.c:2314
char ptarray_same2d(const POINTARRAY *pa1, const POINTARRAY *pa2)
Definition ptarray.c:509
POINTARRAY * ptarray_force_dims(const POINTARRAY *pa, int hasz, int hasm, double zval, double mval)
Definition ptarray.c:1188
POINTARRAY * ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance)
Definition ptarray.c:1667
int ptarray_has_m(const POINTARRAY *pa)
Definition ptarray.c:44
double ptarray_locate_point(const POINTARRAY *pa, const POINT4D *p4d, double *mindistout, POINT4D *proj4d)
Definition ptarray.c:1518
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
double gfac
Definition liblwgeom.h:332
double zoff
Definition liblwgeom.h:332
double bfac
Definition liblwgeom.h:332
double ifac
Definition liblwgeom.h:332
double xoff
Definition liblwgeom.h:332
double dfac
Definition liblwgeom.h:332
double afac
Definition liblwgeom.h:332
double ffac
Definition liblwgeom.h:332
double cfac
Definition liblwgeom.h:332
double hfac
Definition liblwgeom.h:332
double efac
Definition liblwgeom.h:332
double yoff
Definition liblwgeom.h:332
double ymax
Definition liblwgeom.h:357
double ymin
Definition liblwgeom.h:356
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
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 maxpoints
Definition liblwgeom.h:428
uint32_t npoints
Definition liblwgeom.h:427
uint8_t * serialized_pointlist
Definition liblwgeom.h:434
double xscale
Definition liblwgeom.h:1409
double ipm
Definition liblwgeom.h:1404
double zsize
Definition liblwgeom.h:1407
double ysize
Definition liblwgeom.h:1406
double xsize
Definition liblwgeom.h:1405
double yscale
Definition liblwgeom.h:1410
double ipx
Definition liblwgeom.h:1401
double msize
Definition liblwgeom.h:1408
double ipy
Definition liblwgeom.h:1402
double ipz
Definition liblwgeom.h:1403
Snap-to-grid.
Definition liblwgeom.h:1400