Coverage Report

Created: 2026-03-31 06:40

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