Coverage Report

Created: 2026-08-13 07:11

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