Coverage Report

Created: 2026-08-31 06:50

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