Coverage Report

Created: 2026-07-16 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/measures.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 2001-2006 Refractions Research Inc.
22
 * Copyright 2010 Nicklas Avén
23
 * Copyright 2012 Paul Ramsey
24
 * Copyright 2019 Darafei Praliaskouski
25
 *
26
 **********************************************************************/
27
28
#include <string.h>
29
#include <stdlib.h>
30
31
#include "../postgis_config.h"
32
//#define POSTGIS_DEBUG_LEVEL 5
33
34
#include "measures.h"
35
#include "lwgeom_log.h"
36
37
/*------------------------------------------------------------------------------------------------------------
38
Initializing functions
39
The functions starting the distance-calculation processes
40
--------------------------------------------------------------------------------------------------------------*/
41
42
LWGEOM *
43
lwgeom_closest_line(const LWGEOM *lw1, const LWGEOM *lw2)
44
0
{
45
0
  return lw_dist2d_distanceline(lw1, lw2, lw1->srid, DIST_MIN);
46
0
}
47
48
LWGEOM *
49
lwgeom_furthest_line(const LWGEOM *lw1, const LWGEOM *lw2)
50
0
{
51
0
  return lw_dist2d_distanceline(lw1, lw2, lw1->srid, DIST_MAX);
52
0
}
53
54
LWGEOM *
55
lwgeom_closest_point(const LWGEOM *lw1, const LWGEOM *lw2)
56
0
{
57
0
  return lw_dist2d_distancepoint(lw1, lw2, lw1->srid, DIST_MIN);
58
0
}
59
60
LWGEOM *
61
lwgeom_furthest_point(const LWGEOM *lw1, const LWGEOM *lw2)
62
0
{
63
0
  return lw_dist2d_distancepoint(lw1, lw2, lw1->srid, DIST_MAX);
64
0
}
65
66
void
67
lw_dist2d_distpts_init(DISTPTS *dl, int mode)
68
0
{
69
0
  dl->twisted = -1;
70
0
  dl->p1.x = dl->p1.y = 0.0;
71
0
  dl->p2.x = dl->p2.y = 0.0;
72
0
  dl->mode = mode;
73
0
  dl->tolerance = 0.0;
74
0
  if (mode == DIST_MIN)
75
0
    dl->distance = FLT_MAX;
76
0
  else
77
0
    dl->distance = -1 * FLT_MAX;
78
0
}
79
80
static void
81
lw_dist2d_distpts_set(DISTPTS *dl, double distance, const POINT2D *p1, const POINT2D *p2)
82
0
{
83
0
  int update = (dl->mode == DIST_MIN) ? (distance < dl->distance) : (distance > dl->distance);
84
0
  if (update)
85
0
  {
86
0
    dl->distance = distance;
87
0
    dl->p1 = *p1;
88
0
    dl->p2 = *p2;
89
0
  }
90
0
}
91
92
/**
93
Function initializing shortestline and longestline calculations.
94
*/
95
LWGEOM *
96
lw_dist2d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
97
0
{
98
0
  double x1, x2, y1, y2;
99
100
0
  double initdistance = (mode == DIST_MIN ? FLT_MAX : -1.0);
101
0
  DISTPTS thedl;
102
0
  LWPOINT *lwpoints[2];
103
0
  LWGEOM *result;
104
105
0
  thedl.mode = mode;
106
0
  thedl.distance = initdistance;
107
0
  thedl.tolerance = 0.0;
108
109
0
  LWDEBUG(2, "lw_dist2d_distanceline is called");
110
111
0
  if (!lw_dist2d_comp(lw1, lw2, &thedl))
112
0
  {
113
    /*should never get here. all cases ought to be error handled earlier*/
114
0
    lwerror("Some unspecified error.");
115
0
    result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
116
0
  }
117
118
  /*if thedl.distance is unchanged there where only empty geometries input*/
119
0
  if (thedl.distance == initdistance)
120
0
  {
121
0
    LWDEBUG(3, "didn't find geometries to measure between, returning null");
122
0
    result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
123
0
  }
124
0
  else
125
0
  {
126
0
    x1 = thedl.p1.x;
127
0
    y1 = thedl.p1.y;
128
0
    x2 = thedl.p2.x;
129
0
    y2 = thedl.p2.y;
130
131
0
    lwpoints[0] = lwpoint_make2d(srid, x1, y1);
132
0
    lwpoints[1] = lwpoint_make2d(srid, x2, y2);
133
134
0
    result = (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints);
135
0
  }
136
0
  return result;
137
0
}
138
139
/**
140
Function initializing closestpoint calculations.
141
*/
142
LWGEOM *
143
lw_dist2d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode)
144
0
{
145
0
  double x, y;
146
0
  DISTPTS thedl;
147
0
  double initdistance = FLT_MAX;
148
0
  LWGEOM *result;
149
150
0
  thedl.mode = mode;
151
0
  thedl.distance = initdistance;
152
0
  thedl.tolerance = 0;
153
154
0
  LWDEBUG(2, "lw_dist2d_distancepoint is called");
155
156
0
  if (!lw_dist2d_comp(lw1, lw2, &thedl))
157
0
  {
158
    /*should never get here. all cases ought to be error handled earlier*/
159
0
    lwerror("Some unspecified error.");
160
0
    result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
161
0
  }
162
0
  if (thedl.distance == initdistance)
163
0
  {
164
0
    LWDEBUG(3, "didn't find geometries to measure between, returning null");
165
0
    result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0);
166
0
  }
167
0
  else
168
0
  {
169
0
    x = thedl.p1.x;
170
0
    y = thedl.p1.y;
171
0
    result = (LWGEOM *)lwpoint_make2d(srid, x, y);
172
0
  }
173
0
  return result;
174
0
}
175
176
/**
177
Function initializing max distance calculation
178
*/
179
double
180
lwgeom_maxdistance2d(const LWGEOM *lw1, const LWGEOM *lw2)
181
0
{
182
0
  LWDEBUG(2, "lwgeom_maxdistance2d is called");
183
184
0
  return lwgeom_maxdistance2d_tolerance(lw1, lw2, 0.0);
185
0
}
186
187
/**
188
Function handling max distance calculations and dfullywithin calculations.
189
The difference is just the tolerance.
190
*/
191
double
192
lwgeom_maxdistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
193
0
{
194
  /*double thedist;*/
195
0
  DISTPTS thedl;
196
0
  LWDEBUG(2, "lwgeom_maxdistance2d_tolerance is called");
197
0
  thedl.mode = DIST_MAX;
198
0
  thedl.distance = -1;
199
0
  thedl.tolerance = tolerance;
200
0
  if (lw_dist2d_comp(lw1, lw2, &thedl))
201
0
    return thedl.distance;
202
203
  /*should never get here. all cases ought to be error handled earlier*/
204
0
  lwerror("Some unspecified error.");
205
0
  return -1;
206
0
}
207
208
/**
209
  Function initializing min distance calculation
210
*/
211
double
212
lwgeom_mindistance2d(const LWGEOM *lw1, const LWGEOM *lw2)
213
0
{
214
0
  return lwgeom_mindistance2d_tolerance(lw1, lw2, 0.0);
215
0
}
216
217
/**
218
  Function handling min distance calculations and dwithin calculations.
219
  The difference is just the tolerance.
220
*/
221
double
222
lwgeom_mindistance2d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
223
0
{
224
0
  DISTPTS thedl;
225
0
  LWDEBUG(2, "lwgeom_mindistance2d_tolerance is called");
226
0
  thedl.mode = DIST_MIN;
227
0
  thedl.distance = FLT_MAX;
228
0
  thedl.tolerance = tolerance;
229
0
  if (lw_dist2d_comp(lw1, lw2, &thedl))
230
0
    return thedl.distance;
231
  /*should never get here. all cases ought to be error handled earlier*/
232
0
  lwerror("Some unspecified error.");
233
0
  return FLT_MAX;
234
0
}
235
236
/*------------------------------------------------------------------------------------------------------------
237
End of Initializing functions
238
--------------------------------------------------------------------------------------------------------------*/
239
240
/*------------------------------------------------------------------------------------------------------------
241
Preprocessing functions
242
Functions preparing geometries for distance-calculations
243
--------------------------------------------------------------------------------------------------------------*/
244
245
/**
246
  This function just deserializes geometries
247
  Bboxes is not checked here since it is the subgeometries
248
  bboxes we will use anyway.
249
*/
250
int
251
lw_dist2d_comp(const LWGEOM *lw1, const LWGEOM *lw2, DISTPTS *dl)
252
0
{
253
0
  return lw_dist2d_recursive(lw1, lw2, dl);
254
0
}
255
256
static int
257
lw_dist2d_is_collection(const LWGEOM *g)
258
0
{
259
  /* Differs from lwgeom_is_collection by not treating CURVEPOLYGON as collection */
260
0
  switch (g->type)
261
0
  {
262
0
  case TINTYPE:
263
0
  case MULTIPOINTTYPE:
264
0
  case MULTILINETYPE:
265
0
  case MULTIPOLYGONTYPE:
266
0
  case COLLECTIONTYPE:
267
0
  case MULTICURVETYPE:
268
0
  case MULTISURFACETYPE:
269
0
  case COMPOUNDTYPE:
270
0
  case POLYHEDRALSURFACETYPE:
271
0
    return LW_TRUE;
272
0
    break;
273
274
0
  default:
275
0
    return LW_FALSE;
276
0
  }
277
0
}
278
279
280
/* Check for overlapping bboxes */
281
static int
282
lw_dist2d_check_overlap(const LWGEOM *lwg1, const LWGEOM *lwg2)
283
0
{
284
0
  assert(lwg1 && lwg2 && lwg1->bbox && lwg2->bbox);
285
286
  /* Check if the geometries intersect. */
287
0
  if ((lwg1->bbox->xmax < lwg2->bbox->xmin || lwg1->bbox->xmin > lwg2->bbox->xmax ||
288
0
       lwg1->bbox->ymax < lwg2->bbox->ymin || lwg1->bbox->ymin > lwg2->bbox->ymax))
289
0
  {
290
0
    return LW_FALSE;
291
0
  }
292
0
  return LW_TRUE;
293
0
}
294
295
/**
296
This is a recursive function delivering every possible combination of subgeometries
297
*/
298
int
299
lw_dist2d_recursive(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS *dl)
300
0
{
301
0
  int i, j;
302
0
  int n1 = 1;
303
0
  int n2 = 1;
304
0
  LWGEOM *g1 = NULL;
305
0
  LWGEOM *g2 = NULL;
306
0
  LWCOLLECTION *c1 = NULL;
307
0
  LWCOLLECTION *c2 = NULL;
308
309
0
  LWDEBUGF(2, "lw_dist2d_comp is called with type1=%d, type2=%d", lwg1->type, lwg2->type);
310
311
0
  if (lw_dist2d_is_collection(lwg1))
312
0
  {
313
0
    LWDEBUG(3, "First geometry is collection");
314
0
    c1 = lwgeom_as_lwcollection(lwg1);
315
0
    n1 = c1->ngeoms;
316
0
  }
317
0
  if (lw_dist2d_is_collection(lwg2))
318
0
  {
319
0
    LWDEBUG(3, "Second geometry is collection");
320
0
    c2 = lwgeom_as_lwcollection(lwg2);
321
0
    n2 = c2->ngeoms;
322
0
  }
323
324
0
  for (i = 0; i < n1; i++)
325
0
  {
326
327
0
    if (lw_dist2d_is_collection(lwg1))
328
0
      g1 = c1->geoms[i];
329
0
    else
330
0
      g1 = (LWGEOM *)lwg1;
331
332
0
    if (!g1) continue;
333
334
0
    if (lwgeom_is_empty(g1))
335
0
      continue;
336
337
0
    if (lw_dist2d_is_collection(g1))
338
0
    {
339
0
      LWDEBUG(3, "Found collection inside first geometry collection, recursing");
340
0
      if (!lw_dist2d_recursive(g1, lwg2, dl))
341
0
        return LW_FALSE;
342
0
      continue;
343
0
    }
344
0
    for (j = 0; j < n2; j++)
345
0
    {
346
0
      if (lw_dist2d_is_collection(lwg2))
347
0
        g2 = c2->geoms[j];
348
0
      else
349
0
        g2 = (LWGEOM *)lwg2;
350
351
0
      if (!g2) continue;
352
353
0
      if (lw_dist2d_is_collection(g2))
354
0
      {
355
0
        LWDEBUG(3, "Found collection inside second geometry collection, recursing");
356
0
        if (!lw_dist2d_recursive(g1, g2, dl))
357
0
          return LW_FALSE;
358
0
        continue;
359
0
      }
360
361
0
      if (!g1->bbox)
362
0
        lwgeom_add_bbox(g1);
363
364
0
      if (!g2->bbox)
365
0
        lwgeom_add_bbox(g2);
366
367
      /* If one of geometries is empty, skip */
368
0
      if (lwgeom_is_empty(g1) || lwgeom_is_empty(g2))
369
0
        continue;
370
371
0
      if ((dl->mode != DIST_MAX) && (!lw_dist2d_check_overlap(g1, g2)) &&
372
0
          (g1->type == LINETYPE || g1->type == POLYGONTYPE || g1->type == TRIANGLETYPE) &&
373
0
          (g2->type == LINETYPE || g2->type == POLYGONTYPE || g2->type == TRIANGLETYPE))
374
0
      {
375
0
        if (!lw_dist2d_distribute_fast(g1, g2, dl))
376
0
          return LW_FALSE;
377
0
      }
378
0
      else
379
0
      {
380
0
        if (!lw_dist2d_distribute_bruteforce(g1, g2, dl))
381
0
          return LW_FALSE;
382
0
        LWDEBUGF(2, "Distance so far: %.15g (%.15g tolerated)", dl->distance, dl->tolerance);
383
0
        if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
384
0
          return LW_TRUE; /*just a check if the answer is already given*/
385
0
        LWDEBUG(2, "Not below tolerance yet");
386
0
      }
387
0
    }
388
0
  }
389
0
  return LW_TRUE;
390
0
}
391
392
int
393
lw_dist2d_distribute_bruteforce(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS *dl)
394
0
{
395
396
0
  int t1 = lwg1->type;
397
0
  int t2 = lwg2->type;
398
399
0
  switch (t1)
400
0
  {
401
0
  case POINTTYPE:
402
0
  {
403
0
    dl->twisted = 1;
404
0
    switch (t2)
405
0
    {
406
0
    case POINTTYPE:
407
0
      return lw_dist2d_point_point((LWPOINT *)lwg1, (LWPOINT *)lwg2, dl);
408
0
    case LINETYPE:
409
0
      return lw_dist2d_point_line((LWPOINT *)lwg1, (LWLINE *)lwg2, dl);
410
0
    case TRIANGLETYPE:
411
0
      return lw_dist2d_point_tri((LWPOINT *)lwg1, (LWTRIANGLE *)lwg2, dl);
412
0
    case POLYGONTYPE:
413
0
      return lw_dist2d_point_poly((LWPOINT *)lwg1, (LWPOLY *)lwg2, dl);
414
0
    case CIRCSTRINGTYPE:
415
0
      return lw_dist2d_point_circstring((LWPOINT *)lwg1, (LWCIRCSTRING *)lwg2, dl);
416
0
    case CURVEPOLYTYPE:
417
0
      return lw_dist2d_point_curvepoly((LWPOINT *)lwg1, (LWCURVEPOLY *)lwg2, dl);
418
0
    default:
419
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
420
0
      return LW_FALSE;
421
0
    }
422
0
  }
423
0
  case LINETYPE:
424
0
  {
425
0
    dl->twisted = 1;
426
0
    switch (t2)
427
0
    {
428
0
    case POINTTYPE:
429
0
      dl->twisted = -1;
430
0
      return lw_dist2d_point_line((LWPOINT *)lwg2, (LWLINE *)lwg1, dl);
431
0
    case LINETYPE:
432
0
      return lw_dist2d_line_line((LWLINE *)lwg1, (LWLINE *)lwg2, dl);
433
0
    case TRIANGLETYPE:
434
0
      return lw_dist2d_line_tri((LWLINE *)lwg1, (LWTRIANGLE *)lwg2, dl);
435
0
    case POLYGONTYPE:
436
0
      return lw_dist2d_line_poly((LWLINE *)lwg1, (LWPOLY *)lwg2, dl);
437
0
    case CIRCSTRINGTYPE:
438
0
      return lw_dist2d_line_circstring((LWLINE *)lwg1, (LWCIRCSTRING *)lwg2, dl);
439
0
    case CURVEPOLYTYPE:
440
0
      return lw_dist2d_line_curvepoly((LWLINE *)lwg1, (LWCURVEPOLY *)lwg2, dl);
441
0
    default:
442
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
443
0
      return LW_FALSE;
444
0
    }
445
0
  }
446
0
  case TRIANGLETYPE:
447
0
  {
448
0
    dl->twisted = 1;
449
0
    switch (t2)
450
0
    {
451
0
    case POINTTYPE:
452
0
      dl->twisted = -1;
453
0
      return lw_dist2d_point_tri((LWPOINT *)lwg2, (LWTRIANGLE *)lwg1, dl);
454
0
    case LINETYPE:
455
0
      dl->twisted = -1;
456
0
      return lw_dist2d_line_tri((LWLINE *)lwg2, (LWTRIANGLE *)lwg1, dl);
457
0
    case TRIANGLETYPE:
458
0
      return lw_dist2d_tri_tri((LWTRIANGLE *)lwg1, (LWTRIANGLE *)lwg2, dl);
459
0
    case POLYGONTYPE:
460
0
      return lw_dist2d_tri_poly((LWTRIANGLE *)lwg1, (LWPOLY *)lwg2, dl);
461
0
    case CIRCSTRINGTYPE:
462
0
      return lw_dist2d_tri_circstring((LWTRIANGLE *)lwg1, (LWCIRCSTRING *)lwg2, dl);
463
0
    case CURVEPOLYTYPE:
464
0
      return lw_dist2d_tri_curvepoly((LWTRIANGLE *)lwg1, (LWCURVEPOLY *)lwg2, dl);
465
0
    default:
466
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
467
0
      return LW_FALSE;
468
0
    }
469
0
  }
470
0
  case CIRCSTRINGTYPE:
471
0
  {
472
0
    dl->twisted = 1;
473
0
    switch (t2)
474
0
    {
475
0
    case POINTTYPE:
476
0
      dl->twisted = -1;
477
0
      return lw_dist2d_point_circstring((LWPOINT *)lwg2, (LWCIRCSTRING *)lwg1, dl);
478
0
    case LINETYPE:
479
0
      dl->twisted = -1;
480
0
      return lw_dist2d_line_circstring((LWLINE *)lwg2, (LWCIRCSTRING *)lwg1, dl);
481
0
    case TRIANGLETYPE:
482
0
      dl->twisted = -1;
483
0
      return lw_dist2d_tri_circstring((LWTRIANGLE *)lwg2, (LWCIRCSTRING *)lwg1, dl);
484
0
    case POLYGONTYPE:
485
0
      return lw_dist2d_circstring_poly((LWCIRCSTRING *)lwg1, (LWPOLY *)lwg2, dl);
486
0
    case CIRCSTRINGTYPE:
487
0
      return lw_dist2d_circstring_circstring((LWCIRCSTRING *)lwg1, (LWCIRCSTRING *)lwg2, dl);
488
0
    case CURVEPOLYTYPE:
489
0
      return lw_dist2d_circstring_curvepoly((LWCIRCSTRING *)lwg1, (LWCURVEPOLY *)lwg2, dl);
490
0
    default:
491
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
492
0
      return LW_FALSE;
493
0
    }
494
0
  }
495
0
  case POLYGONTYPE:
496
0
  {
497
0
    dl->twisted = -1;
498
0
    switch (t2)
499
0
    {
500
0
    case POINTTYPE:
501
0
      return lw_dist2d_point_poly((LWPOINT *)lwg2, (LWPOLY *)lwg1, dl);
502
0
    case LINETYPE:
503
0
      return lw_dist2d_line_poly((LWLINE *)lwg2, (LWPOLY *)lwg1, dl);
504
0
    case TRIANGLETYPE:
505
0
      return lw_dist2d_tri_poly((LWTRIANGLE *)lwg2, (LWPOLY *)lwg1, dl);
506
0
    case CIRCSTRINGTYPE:
507
0
      return lw_dist2d_circstring_poly((LWCIRCSTRING *)lwg2, (LWPOLY *)lwg1, dl);
508
0
    case POLYGONTYPE:
509
0
      dl->twisted = 1;
510
0
      return lw_dist2d_poly_poly((LWPOLY *)lwg1, (LWPOLY *)lwg2, dl);
511
0
    case CURVEPOLYTYPE:
512
0
      dl->twisted = 1;
513
0
      return lw_dist2d_poly_curvepoly((LWPOLY *)lwg1, (LWCURVEPOLY *)lwg2, dl);
514
0
    default:
515
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
516
0
      return LW_FALSE;
517
0
    }
518
0
  }
519
0
  case CURVEPOLYTYPE:
520
0
  {
521
0
    dl->twisted = -1;
522
0
    switch (t2)
523
0
    {
524
0
    case POINTTYPE:
525
0
      return lw_dist2d_point_curvepoly((LWPOINT *)lwg2, (LWCURVEPOLY *)lwg1, dl);
526
0
    case LINETYPE:
527
0
      return lw_dist2d_line_curvepoly((LWLINE *)lwg2, (LWCURVEPOLY *)lwg1, dl);
528
0
    case TRIANGLETYPE:
529
0
      return lw_dist2d_tri_curvepoly((LWTRIANGLE *)lwg2, (LWCURVEPOLY *)lwg1, dl);
530
0
    case POLYGONTYPE:
531
0
      return lw_dist2d_poly_curvepoly((LWPOLY *)lwg2, (LWCURVEPOLY *)lwg1, dl);
532
0
    case CIRCSTRINGTYPE:
533
0
      return lw_dist2d_circstring_curvepoly((LWCIRCSTRING *)lwg2, (LWCURVEPOLY *)lwg1, dl);
534
0
    case CURVEPOLYTYPE:
535
0
      dl->twisted = 1;
536
0
      return lw_dist2d_curvepoly_curvepoly((LWCURVEPOLY *)lwg1, (LWCURVEPOLY *)lwg2, dl);
537
0
    default:
538
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2));
539
0
      return LW_FALSE;
540
0
    }
541
0
  }
542
0
  default:
543
0
  {
544
0
    lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t1));
545
0
    return LW_FALSE;
546
0
  }
547
0
  }
548
549
0
  return LW_FALSE;
550
0
}
551
552
553
/** Geometries are distributed for the new faster distance-calculations */
554
int
555
lw_dist2d_distribute_fast(LWGEOM *lwg1, LWGEOM *lwg2, DISTPTS *dl)
556
0
{
557
0
  POINTARRAY *pa1, *pa2;
558
0
  int type1 = lwg1->type;
559
0
  int type2 = lwg2->type;
560
561
0
  LWDEBUGF(2, "lw_dist2d_distribute_fast is called with typ1=%d, type2=%d", lwg1->type, lwg2->type);
562
563
0
  switch (type1)
564
0
  {
565
0
  case LINETYPE:
566
0
    pa1 = ((LWLINE *)lwg1)->points;
567
0
    break;
568
0
  case POLYGONTYPE:
569
0
    pa1 = ((LWPOLY *)lwg1)->rings[0];
570
0
    break;
571
0
  case TRIANGLETYPE:
572
0
    pa1 = ((LWTRIANGLE *)lwg1)->points;
573
0
    break;
574
0
  default:
575
0
    lwerror("Unsupported geometry1 type: %s", lwtype_name(type1));
576
0
    return LW_FALSE;
577
0
  }
578
0
  switch (type2)
579
0
  {
580
0
  case LINETYPE:
581
0
    pa2 = ((LWLINE *)lwg2)->points;
582
0
    break;
583
0
  case POLYGONTYPE:
584
0
    pa2 = ((LWPOLY *)lwg2)->rings[0];
585
0
    break;
586
0
  case TRIANGLETYPE:
587
0
    pa2 = ((LWTRIANGLE *)lwg2)->points;
588
0
    break;
589
0
  default:
590
0
    lwerror("Unsupported geometry2 type: %s", lwtype_name(type1));
591
0
    return LW_FALSE;
592
0
  }
593
0
  dl->twisted = 1;
594
0
  return lw_dist2d_fast_ptarray_ptarray(pa1, pa2, dl, lwg1->bbox, lwg2->bbox);
595
0
}
596
597
/*------------------------------------------------------------------------------------------------------------
598
End of Preprocessing functions
599
--------------------------------------------------------------------------------------------------------------*/
600
601
/*------------------------------------------------------------------------------------------------------------
602
Brute force functions
603
The old way of calculating distances, now used for:
604
1)  distances to points (because there shouldn't be anything to gain by the new way of doing it)
605
2)  distances when subgeometries geometries bboxes overlaps
606
--------------------------------------------------------------------------------------------------------------*/
607
608
/**
609
point to point calculation
610
*/
611
int
612
lw_dist2d_point_point(LWPOINT *point1, LWPOINT *point2, DISTPTS *dl)
613
0
{
614
0
  const POINT2D *p1 = getPoint2d_cp(point1->point, 0);
615
0
  const POINT2D *p2 = getPoint2d_cp(point2->point, 0);
616
0
  return lw_dist2d_pt_pt(p1, p2, dl);
617
0
}
618
/**
619
point to line calculation
620
*/
621
int
622
lw_dist2d_point_line(LWPOINT *point, LWLINE *line, DISTPTS *dl)
623
0
{
624
0
  const POINT2D *p = getPoint2d_cp(point->point, 0);
625
0
  return lw_dist2d_pt_ptarray(p, line->points, dl);
626
0
}
627
628
int
629
lw_dist2d_point_tri(LWPOINT *point, LWTRIANGLE *tri, DISTPTS *dl)
630
0
{
631
0
  const POINT2D *pt = getPoint2d_cp(point->point, 0);
632
  /* Is point inside triangle? */
633
0
  if (dl->mode == DIST_MIN && ptarray_contains_point(tri->points, pt) != LW_OUTSIDE)
634
0
  {
635
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
636
0
    return LW_TRUE;
637
0
  }
638
639
0
  return lw_dist2d_pt_ptarray(pt, tri->points, dl);
640
0
}
641
642
int
643
lw_dist2d_point_circstring(LWPOINT *point, LWCIRCSTRING *circ, DISTPTS *dl)
644
0
{
645
0
  const POINT2D *p;
646
0
  p = getPoint2d_cp(point->point, 0);
647
0
  return lw_dist2d_pt_ptarrayarc(p, circ->points, dl);
648
0
}
649
650
/**
651
 * 1. see if pt in outer boundary. if no, then treat the outer ring like a line
652
 * 2. if in the boundary, test to see if its in a hole.
653
 *    if so, then return dist to hole, else return 0 (point in polygon)
654
 */
655
int
656
lw_dist2d_point_poly(LWPOINT *point, LWPOLY *poly, DISTPTS *dl)
657
0
{
658
0
  const POINT2D *p = getPoint2d_cp(point->point, 0);
659
660
  /* Max distance? Check only outer ring.*/
661
0
  if (dl->mode == DIST_MAX)
662
0
    return lw_dist2d_pt_ptarray(p, poly->rings[0], dl);
663
664
  /* Return distance to outer ring if not inside it */
665
0
  if (ptarray_contains_point(poly->rings[0], p) == LW_OUTSIDE)
666
0
    return lw_dist2d_pt_ptarray(p, poly->rings[0], dl);
667
668
  /*
669
   * Inside the outer ring.
670
   * Scan though each of the inner rings looking to
671
   * see if its inside.  If not, distance==0.
672
   * Otherwise, distance = pt to ring distance
673
   */
674
0
  for (uint32_t i = 1; i < poly->nrings; i++)
675
0
    if (ptarray_contains_point(poly->rings[i], p) != LW_OUTSIDE)
676
0
      return lw_dist2d_pt_ptarray(p, poly->rings[i], dl);
677
678
  /* Is inside the polygon */
679
0
  lw_dist2d_distpts_set(dl, 0.0, p, p);
680
0
  return LW_TRUE;
681
0
}
682
683
int
684
lw_dist2d_point_curvepoly(LWPOINT *point, LWCURVEPOLY *poly, DISTPTS *dl)
685
0
{
686
0
  const POINT2D *p = getPoint2d_cp(point->point, 0);
687
688
0
  if (dl->mode == DIST_MAX)
689
0
    lwerror("lw_dist2d_point_curvepoly cannot calculate max distance");
690
691
  /* Return distance to outer ring if not inside it */
692
0
  if (lwgeom_contains_point(poly->rings[0], p) == LW_OUTSIDE)
693
0
    return lw_dist2d_recursive((LWGEOM *)point, poly->rings[0], dl);
694
695
  /* Inside the outer ring.
696
   * Scan though each of the inner rings looking to see if its inside.  If not, distance==0.
697
   * Otherwise, distance = pt to ring distance.
698
   */
699
0
  for (uint32_t i = 1; i < poly->nrings; i++)
700
0
    if (lwgeom_contains_point(poly->rings[i], p) == LW_INSIDE)
701
0
      return lw_dist2d_recursive((LWGEOM *)point, poly->rings[i], dl);
702
703
  /* Is inside the polygon */
704
0
  lw_dist2d_distpts_set(dl, 0.0, p, p);
705
0
  return LW_TRUE;
706
0
}
707
708
int
709
lw_dist2d_line_line(LWLINE *line1, LWLINE *line2, DISTPTS *dl)
710
0
{
711
0
  POINTARRAY *pa1 = line1->points;
712
0
  POINTARRAY *pa2 = line2->points;
713
0
  return lw_dist2d_ptarray_ptarray(pa1, pa2, dl);
714
0
}
715
716
int
717
lw_dist2d_line_tri(LWLINE *line, LWTRIANGLE *tri, DISTPTS *dl)
718
0
{
719
0
  const POINT2D *pt = getPoint2d_cp(line->points, 0);
720
  /* Is there a point inside triangle? */
721
0
  if (dl->mode == DIST_MIN && ptarray_contains_point(tri->points, pt) != LW_OUTSIDE)
722
0
  {
723
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
724
0
    return LW_TRUE;
725
0
  }
726
727
0
  return lw_dist2d_ptarray_ptarray(line->points, tri->points, dl);
728
0
}
729
730
int
731
lw_dist2d_line_circstring(LWLINE *line1, LWCIRCSTRING *line2, DISTPTS *dl)
732
0
{
733
0
  return lw_dist2d_ptarray_ptarrayarc(line1->points, line2->points, dl);
734
0
}
735
736
/**
737
 * line to polygon calculation
738
 * Brute force.
739
 * Test line-ring distance against each ring.
740
 * If there's an intersection (distance==0) then return 0 (crosses boundary).
741
 * Otherwise, test to see if any point is inside outer rings of polygon,
742
 * but not in inner rings.
743
 * If so, return 0  (line inside polygon),
744
 * otherwise return min distance to a ring (could be outside
745
 * polygon or inside a hole)
746
 */
747
int
748
lw_dist2d_line_poly(LWLINE *line, LWPOLY *poly, DISTPTS *dl)
749
0
{
750
0
  POINTARRAY *pa = line->points;
751
0
  const POINT2D *pt = getPoint2d_cp(pa, 0);
752
753
  /* Line has a point outside poly. Check distance to outer ring only. */
754
0
  if (ptarray_contains_point(poly->rings[0], pt) == LW_OUTSIDE || dl->mode == DIST_MAX)
755
0
    return lw_dist2d_ptarray_ptarray(pa, poly->rings[0], dl);
756
757
0
  for (uint32_t i = 1; i < poly->nrings; i++)
758
0
  {
759
0
    if (!lw_dist2d_ptarray_ptarray(pa, poly->rings[i], dl))
760
0
      return LW_FALSE;
761
762
    /* just a check if the answer is already given */
763
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
764
0
      return LW_TRUE;
765
0
  }
766
767
  /* It's inside a hole, then the actual distance is the min ring distance */
768
0
  for (uint32_t i = 1; i < poly->nrings; i++)
769
0
    if (ptarray_contains_point(poly->rings[i], pt) != LW_OUTSIDE)
770
0
      return LW_TRUE;
771
772
  /* Not in hole, so inside polygon */
773
0
  if (dl->mode == DIST_MIN)
774
0
  {
775
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
776
0
  }
777
0
  return LW_TRUE;
778
0
}
779
780
int
781
lw_dist2d_line_curvepoly(LWLINE *line, LWCURVEPOLY *poly, DISTPTS *dl)
782
0
{
783
0
  const POINT2D *pt = getPoint2d_cp(line->points, 0);
784
785
  /* Line has a point outside curvepoly. Check distance to outer ring only. */
786
0
  if (lwgeom_contains_point(poly->rings[0], pt) == LW_OUTSIDE)
787
0
    return lw_dist2d_recursive((LWGEOM *)line, poly->rings[0], dl);
788
789
0
  for (uint32_t i = 1; i < poly->nrings; i++)
790
0
  {
791
0
    if (!lw_dist2d_recursive((LWGEOM *)line, poly->rings[i], dl))
792
0
      return LW_FALSE;
793
794
    /* just a check if the answer is already given */
795
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
796
0
      return LW_TRUE;
797
0
  }
798
799
  /* It's inside a hole, then distance is actual min distance */
800
0
  for (uint32_t i = 1; i < poly->nrings; i++)
801
0
    if (lwgeom_contains_point(poly->rings[i], pt) != LW_OUTSIDE)
802
0
      return LW_TRUE;
803
804
  /* Not in hole, so inside polygon */
805
0
  if (dl->mode == DIST_MIN)
806
0
  {
807
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
808
0
  }
809
0
  return LW_TRUE;
810
0
}
811
812
int
813
lw_dist2d_tri_tri(LWTRIANGLE *tri1, LWTRIANGLE *tri2, DISTPTS *dl)
814
0
{
815
0
  POINTARRAY *pa1 = tri1->points;
816
0
  POINTARRAY *pa2 = tri2->points;
817
0
  const POINT2D *pt = getPoint2d_cp(pa2, 0);
818
0
  if (dl->mode == DIST_MIN && ptarray_contains_point(pa1, pt) != LW_OUTSIDE)
819
0
  {
820
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
821
0
    return LW_TRUE;
822
0
  }
823
824
0
  pt = getPoint2d_cp(pa1, 0);
825
0
  if (dl->mode == DIST_MIN && ptarray_contains_point(pa2, pt) != LW_OUTSIDE)
826
0
  {
827
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
828
0
    return LW_TRUE;
829
0
  }
830
831
0
  return lw_dist2d_ptarray_ptarray(pa1, pa2, dl);
832
0
}
833
834
int
835
lw_dist2d_tri_poly(LWTRIANGLE *tri, LWPOLY *poly, DISTPTS *dl)
836
0
{
837
0
  POINTARRAY *pa = tri->points;
838
0
  const POINT2D *pt = getPoint2d_cp(pa, 0);
839
840
  /* If we are looking for maxdistance, just check the outer rings.*/
841
0
  if (dl->mode == DIST_MAX)
842
0
    return lw_dist2d_ptarray_ptarray(pa, poly->rings[0], dl);
843
844
  /* Triangle has a point outside poly. Check distance to outer ring only. */
845
0
  if (ptarray_contains_point(poly->rings[0], pt) == LW_OUTSIDE)
846
0
  {
847
0
    if (!lw_dist2d_ptarray_ptarray(pa, poly->rings[0], dl))
848
0
      return LW_FALSE;
849
850
    /* just a check if the answer is already given */
851
0
    if (dl->distance <= dl->tolerance)
852
0
      return LW_TRUE;
853
854
    /* Maybe poly is inside triangle? */
855
0
    const POINT2D *pt2 = getPoint2d_cp(poly->rings[0], 0);
856
0
    if (ptarray_contains_point(pa, pt2) != LW_OUTSIDE)
857
0
    {
858
0
      lw_dist2d_distpts_set(dl, 0.0, pt2, pt2);
859
0
      return LW_TRUE;
860
0
    }
861
0
  }
862
863
0
  for (uint32_t i = 1; i < poly->nrings; i++)
864
0
  {
865
0
    if (!lw_dist2d_ptarray_ptarray(pa, poly->rings[i], dl))
866
0
      return LW_FALSE;
867
868
    /* just a check if the answer is already given */
869
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
870
0
      return LW_TRUE;
871
0
  }
872
873
  /* It's inside a hole, then the actual distance is the min ring distance */
874
0
  for (uint32_t i = 1; i < poly->nrings; i++)
875
0
    if (ptarray_contains_point(poly->rings[i], pt) != LW_OUTSIDE)
876
0
      return LW_TRUE;
877
878
  /* Not in hole, so inside polygon */
879
0
  lw_dist2d_distpts_set(dl, 0.0, pt, pt);
880
0
  return LW_TRUE;
881
0
}
882
static const POINT2D *
883
lw_curvering_getfirstpoint2d_cp(LWGEOM *geom, POINT2D *scratch)
884
0
{
885
0
  switch (geom->type)
886
0
  {
887
0
  case LINETYPE:
888
0
    return getPoint2d_cp(((LWLINE *)geom)->points, 0);
889
0
  case CIRCSTRINGTYPE:
890
0
    return getPoint2d_cp(((LWCIRCSTRING *)geom)->points, 0);
891
0
  case COMPOUNDTYPE:
892
0
  {
893
0
    LWCOMPOUND *comp = (LWCOMPOUND *)geom;
894
0
    return lw_curvering_getfirstpoint2d_cp(comp->geoms[0], scratch);
895
0
  }
896
0
  case NURBSCURVETYPE:
897
0
  {
898
0
    LWPOINT *pt = lwnurbscurve_evaluate((LWNURBSCURVE *)geom, 0.0);
899
0
    if (!scratch || !pt || !lwpoint_getPoint2d_p(pt, scratch))
900
0
    {
901
0
      lwpoint_free(pt);
902
0
      lwerror("lw_curvering_getfirstpoint2d_cp: invalid NURBSCURVE ring");
903
0
    }
904
0
    lwpoint_free(pt);
905
0
    return scratch;
906
0
  }
907
0
  default:
908
0
    lwerror("lw_curvering_getfirstpoint2d_cp: unknown type");
909
0
  }
910
0
  return NULL;
911
0
}
912
913
int
914
lw_dist2d_tri_curvepoly(LWTRIANGLE *tri, LWCURVEPOLY *poly, DISTPTS *dl)
915
0
{
916
0
  const POINT2D *pt = getPoint2d_cp(tri->points, 0);
917
0
  POINT2D ringpt;
918
919
  /* If we are looking for maxdistance, just check the outer rings.*/
920
0
  if (dl->mode == DIST_MAX)
921
0
    return lw_dist2d_recursive((LWGEOM *)tri, poly->rings[0], dl);
922
923
  /* Line has a point outside curvepoly. Check distance to outer ring only. */
924
0
  if (lwgeom_contains_point(poly->rings[0], pt) == LW_OUTSIDE)
925
0
  {
926
0
    if (lw_dist2d_recursive((LWGEOM *)tri, poly->rings[0], dl))
927
0
      return LW_TRUE;
928
    /* Maybe poly is inside triangle? */
929
0
    if (lwgeom_contains_point((LWGEOM *)tri, lw_curvering_getfirstpoint2d_cp(poly->rings[0], &ringpt)) != LW_OUTSIDE)
930
0
    {
931
0
      lw_dist2d_distpts_set(dl, 0.0, pt, pt);
932
0
      return LW_TRUE;
933
0
    }
934
0
  }
935
936
0
  for (uint32_t i = 1; i < poly->nrings; i++)
937
0
  {
938
0
    if (!lw_dist2d_recursive((LWGEOM *)tri, poly->rings[i], dl))
939
0
      return LW_FALSE;
940
941
    /* just a check if the answer is already given */
942
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
943
0
      return LW_TRUE;
944
0
  }
945
946
  /* It's inside a hole, then distance is actual min distance */
947
0
  for (uint32_t i = 1; i < poly->nrings; i++)
948
0
    if (lwgeom_contains_point(poly->rings[i], pt) != LW_OUTSIDE)
949
0
      return LW_TRUE;
950
951
  /* Not in hole, so inside polygon */
952
0
  lw_dist2d_distpts_set(dl, 0.0, pt, pt);
953
0
  return LW_TRUE;
954
0
}
955
956
int
957
lw_dist2d_tri_circstring(LWTRIANGLE *tri, LWCIRCSTRING *line, DISTPTS *dl)
958
0
{
959
0
  POINT2D scratch;
960
0
  const POINT2D *pt = lw_curvering_getfirstpoint2d_cp((LWGEOM *)line, &scratch);
961
0
  if (ptarray_contains_point(tri->points, pt) != LW_OUTSIDE && dl->mode == DIST_MIN)
962
0
  {
963
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
964
0
    return LW_TRUE;
965
0
  }
966
967
0
  return lw_dist2d_ptarray_ptarrayarc(tri->points, line->points, dl);
968
0
}
969
970
/**
971
Function handling polygon to polygon calculation
972
1 if we are looking for maxdistance, just check the outer rings.
973
2 check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings
974
3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole of poly1
975
4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole of poly2
976
5 If we have come all the way here we know that the first point of one of them is inside the other ones outer ring
977
and not in holes so we check which one is inside.
978
 */
979
int
980
lw_dist2d_poly_poly(LWPOLY *poly1, LWPOLY *poly2, DISTPTS *dl)
981
0
{
982
0
  const POINT2D *pt;
983
984
0
  LWDEBUG(2, "lw_dist2d_poly_poly called");
985
986
  /*1 if we are looking for maxdistance, just check the outer rings.*/
987
0
  if (dl->mode == DIST_MAX)
988
0
    return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl);
989
990
  /* 2  check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings
991
  here it would be possible to handle the information about which one is inside which one and only search for the
992
  smaller ones in the bigger ones holes.*/
993
0
  pt = getPoint2d_cp(poly1->rings[0], 0);
994
0
  if (ptarray_contains_point(poly2->rings[0], pt) == LW_OUTSIDE)
995
0
  {
996
0
    pt = getPoint2d_cp(poly2->rings[0], 0);
997
0
    if (ptarray_contains_point(poly1->rings[0], pt) == LW_OUTSIDE)
998
0
      return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl);
999
0
  }
1000
1001
  /*3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole
1002
   * of poly1*/
1003
0
  pt = getPoint2d_cp(poly2->rings[0], 0);
1004
0
  for (uint32_t i = 1; i < poly1->nrings; i++)
1005
0
    if (ptarray_contains_point(poly1->rings[i], pt) != LW_OUTSIDE)
1006
0
      return lw_dist2d_ptarray_ptarray(poly1->rings[i], poly2->rings[0], dl);
1007
1008
  /*4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole
1009
   * of poly2*/
1010
0
  pt = getPoint2d_cp(poly1->rings[0], 0);
1011
0
  for (uint32_t i = 1; i < poly2->nrings; i++)
1012
0
    if (ptarray_contains_point(poly2->rings[i], pt) != LW_OUTSIDE)
1013
0
      return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[i], dl);
1014
1015
  /*5 If we have come all the way here we know that the first point of one of them is inside the other ones
1016
   * outer ring and not in holes so we check which one is inside.*/
1017
0
  pt = getPoint2d_cp(poly1->rings[0], 0);
1018
0
  if (ptarray_contains_point(poly2->rings[0], pt) != LW_OUTSIDE)
1019
0
  {
1020
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
1021
0
    return LW_TRUE;
1022
0
  }
1023
1024
0
  pt = getPoint2d_cp(poly2->rings[0], 0);
1025
0
  if (ptarray_contains_point(poly1->rings[0], pt) != LW_OUTSIDE)
1026
0
  {
1027
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
1028
0
    return LW_TRUE;
1029
0
  }
1030
1031
0
  lwerror("Unspecified error in function lw_dist2d_poly_poly");
1032
0
  return LW_FALSE;
1033
0
}
1034
1035
int
1036
lw_dist2d_poly_curvepoly(LWPOLY *poly1, LWCURVEPOLY *curvepoly2, DISTPTS *dl)
1037
0
{
1038
0
  LWCURVEPOLY *curvepoly1 = lwcurvepoly_construct_from_lwpoly(poly1);
1039
0
  int rv = lw_dist2d_curvepoly_curvepoly(curvepoly1, curvepoly2, dl);
1040
0
  lwgeom_free((LWGEOM *)curvepoly1);
1041
0
  return rv;
1042
0
}
1043
1044
int
1045
lw_dist2d_circstring_poly(LWCIRCSTRING *circ, LWPOLY *poly, DISTPTS *dl)
1046
0
{
1047
0
  LWCURVEPOLY *curvepoly = lwcurvepoly_construct_from_lwpoly(poly);
1048
0
  int rv = lw_dist2d_line_curvepoly((LWLINE *)circ, curvepoly, dl);
1049
0
  lwgeom_free((LWGEOM *)curvepoly);
1050
0
  return rv;
1051
0
}
1052
1053
int
1054
lw_dist2d_circstring_curvepoly(LWCIRCSTRING *circ, LWCURVEPOLY *poly, DISTPTS *dl)
1055
0
{
1056
0
  return lw_dist2d_line_curvepoly((LWLINE *)circ, poly, dl);
1057
0
}
1058
1059
int
1060
lw_dist2d_circstring_circstring(LWCIRCSTRING *line1, LWCIRCSTRING *line2, DISTPTS *dl)
1061
0
{
1062
0
  return lw_dist2d_ptarrayarc_ptarrayarc(line1->points, line2->points, dl);
1063
0
}
1064
1065
int
1066
lw_dist2d_curvepoly_curvepoly(LWCURVEPOLY *poly1, LWCURVEPOLY *poly2, DISTPTS *dl)
1067
0
{
1068
0
  const POINT2D *pt;
1069
0
  POINT2D scratch1, scratch2;
1070
1071
  /*1 if we are looking for maxdistance, just check the outer rings.*/
1072
0
  if (dl->mode == DIST_MAX)
1073
0
    return lw_dist2d_recursive(poly1->rings[0], poly2->rings[0], dl);
1074
1075
  /* 2  check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings
1076
  here it would be possible to handle the information about which one is inside which one and only search for the
1077
  smaller ones in the bigger ones holes.*/
1078
0
  pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0], &scratch1);
1079
0
  if (lwgeom_contains_point(poly2->rings[0], pt) == LW_OUTSIDE)
1080
0
  {
1081
0
    pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0], &scratch2);
1082
0
    if (lwgeom_contains_point(poly1->rings[0], pt) == LW_OUTSIDE)
1083
0
      return lw_dist2d_recursive(poly1->rings[0], poly2->rings[0], dl);
1084
0
  }
1085
1086
  /*3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole
1087
   * of poly1*/
1088
0
  pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0], &scratch2);
1089
0
  for (uint32_t i = 1; i < poly1->nrings; i++)
1090
0
    if (lwgeom_contains_point(poly1->rings[i], pt) != LW_OUTSIDE)
1091
0
      return lw_dist2d_recursive(poly1->rings[i], poly2->rings[0], dl);
1092
1093
  /*4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole
1094
   * of poly2*/
1095
0
  pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0], &scratch1);
1096
0
  for (uint32_t i = 1; i < poly2->nrings; i++)
1097
0
    if (lwgeom_contains_point(poly2->rings[i], pt) != LW_OUTSIDE)
1098
0
      return lw_dist2d_recursive(poly1->rings[0], poly2->rings[i], dl);
1099
1100
  /*5 If we have come all the way here we know that the first point of one of them is inside the other ones
1101
   * outer ring and not in holes so we check which one is inside.*/
1102
0
  pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0], &scratch1);
1103
0
  if (lwgeom_contains_point(poly2->rings[0], pt) != LW_OUTSIDE)
1104
0
  {
1105
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
1106
0
    return LW_TRUE;
1107
0
  }
1108
1109
0
  pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0], &scratch2);
1110
0
  if (lwgeom_contains_point(poly1->rings[0], pt) != LW_OUTSIDE)
1111
0
  {
1112
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
1113
0
    return LW_TRUE;
1114
0
  }
1115
1116
0
  lwerror("Unspecified error in function lw_dist2d_curvepoly_curvepoly");
1117
0
  return LW_FALSE;
1118
0
}
1119
1120
/**
1121
 * search all the segments of pointarray to see which one is closest to p1
1122
 * Returns minimum distance between point and pointarray
1123
 */
1124
int
1125
lw_dist2d_pt_ptarray(const POINT2D *p, POINTARRAY *pa, DISTPTS *dl)
1126
0
{
1127
0
  const POINT2D *start, *end;
1128
0
  int twist = dl->twisted;
1129
1130
0
  start = getPoint2d_cp(pa, 0);
1131
1132
0
  LWDEBUG(2, "lw_dist2d_pt_ptarray enter");
1133
1134
0
  if (!lw_dist2d_pt_pt(p, start, dl))
1135
0
    return LW_FALSE;
1136
1137
0
  LWDEBUGF(2, "lw_dist2d_pt_ptarray: distance from first point ? : %.15g", dl->distance);
1138
1139
0
  for (uint32_t t = 1; t < pa->npoints; t++)
1140
0
  {
1141
0
    dl->twisted = twist;
1142
0
    end = getPoint2d_cp(pa, t);
1143
0
    if (!lw_dist2d_pt_seg(p, start, end, dl))
1144
0
      return LW_FALSE;
1145
1146
0
    LWDEBUGF(2, "lw_dist2d_pt_ptarray: distance from seg %u ? : %.15g", t, dl->distance);
1147
1148
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1149
0
      return LW_TRUE; /*just a check if the answer is already given*/
1150
0
    start = end;
1151
0
  }
1152
1153
0
  return LW_TRUE;
1154
0
}
1155
1156
/**
1157
 * Search all the arcs of pointarray to see which one is closest to p1
1158
 * Returns minimum distance between point and arc pointarray.
1159
 */
1160
int
1161
lw_dist2d_pt_ptarrayarc(const POINT2D *p, const POINTARRAY *pa, DISTPTS *dl)
1162
0
{
1163
0
  uint32_t t;
1164
0
  const POINT2D *A1;
1165
0
  const POINT2D *A2;
1166
0
  const POINT2D *A3;
1167
0
  int twist = dl->twisted;
1168
1169
0
  LWDEBUG(2, "lw_dist2d_pt_ptarrayarc is called");
1170
1171
0
  if (pa->npoints % 2 == 0 || pa->npoints < 3)
1172
0
  {
1173
0
    lwerror("lw_dist2d_pt_ptarrayarc called with non-arc input");
1174
0
    return LW_FALSE;
1175
0
  }
1176
1177
0
  if (dl->mode == DIST_MAX)
1178
0
  {
1179
0
    lwerror("lw_dist2d_pt_ptarrayarc does not currently support DIST_MAX mode");
1180
0
    return LW_FALSE;
1181
0
  }
1182
1183
0
  A1 = getPoint2d_cp(pa, 0);
1184
1185
0
  if (!lw_dist2d_pt_pt(p, A1, dl))
1186
0
    return LW_FALSE;
1187
1188
0
  for (t = 1; t < pa->npoints; t += 2)
1189
0
  {
1190
0
    dl->twisted = twist;
1191
0
    A2 = getPoint2d_cp(pa, t);
1192
0
    A3 = getPoint2d_cp(pa, t + 1);
1193
1194
0
    if (lw_dist2d_pt_arc(p, A1, A2, A3, dl) == LW_FALSE)
1195
0
      return LW_FALSE;
1196
1197
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1198
0
      return LW_TRUE; /*just a check if the answer is already given*/
1199
1200
0
    A1 = A3;
1201
0
  }
1202
1203
0
  return LW_TRUE;
1204
0
}
1205
1206
/**
1207
 * test each segment of l1 against each segment of l2.
1208
 */
1209
int
1210
lw_dist2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS *dl)
1211
0
{
1212
0
  uint32_t t, u;
1213
0
  const POINT2D *start, *end;
1214
0
  const POINT2D *start2, *end2;
1215
0
  int twist = dl->twisted;
1216
1217
0
  LWDEBUGF(2, "lw_dist2d_ptarray_ptarray called (points: %d-%d)", l1->npoints, l2->npoints);
1218
1219
  /*  If we are searching for maxdistance we go straight to point-point calculation since the maxdistance have
1220
   * to be between two vertices*/
1221
0
  if (dl->mode == DIST_MAX)
1222
0
  {
1223
0
    for (t = 0; t < l1->npoints; t++) /*for each segment in L1 */
1224
0
    {
1225
0
      start = getPoint2d_cp(l1, t);
1226
0
      for (u = 0; u < l2->npoints; u++) /*for each segment in L2 */
1227
0
      {
1228
0
        start2 = getPoint2d_cp(l2, u);
1229
0
        lw_dist2d_pt_pt(start, start2, dl);
1230
0
      }
1231
0
    }
1232
0
  }
1233
0
  else
1234
0
  {
1235
0
    start = getPoint2d_cp(l1, 0);
1236
0
    for (t = 1; t < l1->npoints; t++) /*for each segment in L1 */
1237
0
    {
1238
0
      end = getPoint2d_cp(l1, t);
1239
0
      start2 = getPoint2d_cp(l2, 0);
1240
0
      for (u = 1; u < l2->npoints; u++) /*for each segment in L2 */
1241
0
      {
1242
0
        end2 = getPoint2d_cp(l2, u);
1243
0
        dl->twisted = twist;
1244
0
        lw_dist2d_seg_seg(start, end, start2, end2, dl);
1245
0
        if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1246
0
          return LW_TRUE; /*just a check if the answer is already given*/
1247
0
        start2 = end2;
1248
0
      }
1249
0
      start = end;
1250
0
    }
1251
0
  }
1252
0
  return LW_TRUE;
1253
0
}
1254
1255
/**
1256
 * Test each segment of pa against each arc of pb for distance.
1257
 */
1258
int
1259
lw_dist2d_ptarray_ptarrayarc(const POINTARRAY *pa, const POINTARRAY *pb, DISTPTS *dl)
1260
0
{
1261
0
  uint32_t t, u;
1262
0
  const POINT2D *A1;
1263
0
  const POINT2D *A2;
1264
0
  const POINT2D *B1;
1265
0
  const POINT2D *B2;
1266
0
  const POINT2D *B3;
1267
0
  int twist = dl->twisted;
1268
1269
0
  LWDEBUGF(2, "lw_dist2d_ptarray_ptarrayarc called (points: %d-%d)", pa->npoints, pb->npoints);
1270
1271
0
  if (pb->npoints % 2 == 0 || pb->npoints < 3)
1272
0
  {
1273
0
    lwerror("lw_dist2d_ptarray_ptarrayarc called with non-arc input");
1274
0
    return LW_FALSE;
1275
0
  }
1276
1277
0
  if (dl->mode == DIST_MAX)
1278
0
  {
1279
0
    lwerror("lw_dist2d_ptarray_ptarrayarc does not currently support DIST_MAX mode");
1280
0
    return LW_FALSE;
1281
0
  }
1282
0
  else
1283
0
  {
1284
0
    A1 = getPoint2d_cp(pa, 0);
1285
0
    for (t = 1; t < pa->npoints; t++) /* For each segment in pa */
1286
0
    {
1287
0
      A2 = getPoint2d_cp(pa, t);
1288
0
      B1 = getPoint2d_cp(pb, 0);
1289
0
      for (u = 1; u < pb->npoints; u += 2) /* For each arc in pb */
1290
0
      {
1291
0
        B2 = getPoint2d_cp(pb, u);
1292
0
        B3 = getPoint2d_cp(pb, u + 1);
1293
0
        dl->twisted = twist;
1294
1295
0
        lw_dist2d_seg_arc(A1, A2, B1, B2, B3, dl);
1296
1297
        /* If we've found a distance within tolerance, we're done */
1298
0
        if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1299
0
          return LW_TRUE;
1300
1301
0
        B1 = B3;
1302
0
      }
1303
0
      A1 = A2;
1304
0
    }
1305
0
  }
1306
0
  return LW_TRUE;
1307
0
}
1308
1309
/**
1310
 * Test each arc of pa against each arc of pb for distance.
1311
 */
1312
int
1313
lw_dist2d_ptarrayarc_ptarrayarc(const POINTARRAY *pa, const POINTARRAY *pb, DISTPTS *dl)
1314
0
{
1315
0
  uint32_t t, u;
1316
0
  const POINT2D *A1;
1317
0
  const POINT2D *A2;
1318
0
  const POINT2D *A3;
1319
0
  const POINT2D *B1;
1320
0
  const POINT2D *B2;
1321
0
  const POINT2D *B3;
1322
0
  int twist = dl->twisted;
1323
1324
0
  LWDEBUGF(2, "lw_dist2d_ptarrayarc_ptarrayarc called (points: %d-%d)", pa->npoints, pb->npoints);
1325
1326
0
  if (dl->mode == DIST_MAX)
1327
0
  {
1328
0
    lwerror("lw_dist2d_ptarrayarc_ptarrayarc does not currently support DIST_MAX mode");
1329
0
    return LW_FALSE;
1330
0
  }
1331
0
  else
1332
0
  {
1333
0
    A1 = getPoint2d_cp(pa, 0);
1334
0
    for (t = 1; t < pa->npoints; t += 2) /* For each segment in pa */
1335
0
    {
1336
0
      A2 = getPoint2d_cp(pa, t);
1337
0
      A3 = getPoint2d_cp(pa, t + 1);
1338
0
      B1 = getPoint2d_cp(pb, 0);
1339
0
      for (u = 1; u < pb->npoints; u += 2) /* For each arc in pb */
1340
0
      {
1341
0
        B2 = getPoint2d_cp(pb, u);
1342
0
        B3 = getPoint2d_cp(pb, u + 1);
1343
0
        dl->twisted = twist;
1344
1345
0
        lw_dist2d_arc_arc(A1, A2, A3, B1, B2, B3, dl);
1346
1347
        /* If we've found a distance within tolerance, we're done */
1348
0
        if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1349
0
          return LW_TRUE;
1350
1351
0
        B1 = B3;
1352
0
      }
1353
0
      A1 = A3;
1354
0
    }
1355
0
  }
1356
0
  return LW_TRUE;
1357
0
}
1358
1359
/**
1360
 * Calculate the shortest distance between an arc and an edge.
1361
 * Line/circle approach from http://stackoverflow.com/questions/1073336/circle-line-collision-detection
1362
 */
1363
int
1364
lw_dist2d_seg_arc(const POINT2D *A1,
1365
      const POINT2D *A2,
1366
      const POINT2D *B1,
1367
      const POINT2D *B2,
1368
      const POINT2D *B3,
1369
      DISTPTS *dl)
1370
0
{
1371
0
  POINT2D C;       /* center of arc circle */
1372
0
  double radius_C; /* radius of arc circle */
1373
0
  POINT2D D;       /* point on A closest to C */
1374
0
  double dist_C_D; /* distance from C to D */
1375
0
  int pt_in_arc, pt_in_seg;
1376
0
  DISTPTS dltmp;
1377
1378
  /* Bail out on crazy modes */
1379
0
  if (dl->mode < 0)
1380
0
    lwerror("lw_dist2d_seg_arc does not support maxdistance mode");
1381
1382
  /* What if the "arc" is a point? */
1383
0
  if (lw_arc_is_pt(B1, B2, B3))
1384
0
    return lw_dist2d_pt_seg(B1, A1, A2, dl);
1385
1386
  /* Calculate center and radius of the circle. */
1387
0
  radius_C = lw_arc_center(B1, B2, B3, &C);
1388
1389
  /* This "arc" is actually a line (B2 is collinear with B1,B3) */
1390
0
  if (radius_C < 0.0)
1391
0
    return lw_dist2d_seg_seg(A1, A2, B1, B3, dl);
1392
1393
  /* Calculate distance between the line and circle center */
1394
0
  lw_dist2d_distpts_init(&dltmp, DIST_MIN);
1395
0
  if (lw_dist2d_pt_seg(&C, A1, A2, &dltmp) == LW_FALSE)
1396
0
    lwerror("lw_dist2d_pt_seg failed in lw_dist2d_seg_arc");
1397
1398
0
  D = dltmp.p1;
1399
0
  dist_C_D = dltmp.distance;
1400
1401
  /* Line intersects circle, maybe arc intersects edge? */
1402
  /* If so, that's the closest point. */
1403
  /* If not, the closest point is one of the end points of A */
1404
0
  if (dist_C_D < radius_C)
1405
0
  {
1406
0
    double length_A;  /* length of the segment A */
1407
0
    POINT2D E, F;     /* points of intersection of edge A and circle(B) */
1408
0
    double dist_D_EF; /* distance from D_line to E or F (same distance both ways) */
1409
0
    POINT2D D_line;   /* foot of perpendicular from C to the infinite line through A1-A2 */
1410
0
    double dist_C_D_line; /* distance from C to the infinite line */
1411
0
    double t;
1412
1413
0
    length_A = sqrt((A2->x - A1->x) * (A2->x - A1->x) + (A2->y - A1->y) * (A2->y - A1->y));
1414
1415
    /*
1416
     * D (from lw_dist2d_pt_seg) is clamped to the segment, but we need the
1417
     * foot of the perpendicular on the *infinite* line to correctly compute
1418
     * circle-line intersections E and F. Using the clamped endpoint gives wrong
1419
     * intersection points when the perpendicular falls outside the segment.
1420
     */
1421
0
    t = ((C.x - A1->x) * (A2->x - A1->x) + (C.y - A1->y) * (A2->y - A1->y))
1422
0
        / (length_A * length_A);
1423
0
    D_line.x = A1->x + t * (A2->x - A1->x);
1424
0
    D_line.y = A1->y + t * (A2->y - A1->y);
1425
0
    dist_C_D_line = sqrt((C.x - D_line.x) * (C.x - D_line.x) + (C.y - D_line.y) * (C.y - D_line.y));
1426
1427
0
    dist_D_EF = sqrt(radius_C * radius_C - dist_C_D_line * dist_C_D_line);
1428
1429
    /* Point of intersection E */
1430
0
    E.x = D_line.x - (A2->x - A1->x) * dist_D_EF / length_A;
1431
0
    E.y = D_line.y - (A2->y - A1->y) * dist_D_EF / length_A;
1432
    /* Point of intersection F */
1433
0
    F.x = D_line.x + (A2->x - A1->x) * dist_D_EF / length_A;
1434
0
    F.y = D_line.y + (A2->y - A1->y) * dist_D_EF / length_A;
1435
1436
    /* If E is within A and within B then it's an intersection point */
1437
0
    pt_in_arc = lw_pt_in_arc(&E, B1, B2, B3);
1438
0
    pt_in_seg = lw_pt_in_seg(&E, A1, A2);
1439
1440
0
    if (pt_in_arc && pt_in_seg)
1441
0
    {
1442
0
      lw_dist2d_distpts_set(dl, 0.0, &E, &E);
1443
0
      return LW_TRUE;
1444
0
    }
1445
1446
    /* If F is within A and within B then it's an intersection point */
1447
0
    pt_in_arc = lw_pt_in_arc(&F, B1, B2, B3);
1448
0
    pt_in_seg = lw_pt_in_seg(&F, A1, A2);
1449
1450
0
    if (pt_in_arc && pt_in_seg)
1451
0
    {
1452
0
      lw_dist2d_distpts_set(dl, 0.0, &F, &F);
1453
0
      return LW_TRUE;
1454
0
    }
1455
0
  }
1456
1457
  /* Line grazes circle, maybe arc intersects edge? */
1458
  /* If so, grazing point is the closest point. */
1459
  /* If not, the closest point is one of the end points of A */
1460
0
  else if (dist_C_D == radius_C)
1461
0
  {
1462
    /* Closest point D is also the point of grazing */
1463
0
    pt_in_arc = lw_pt_in_arc(&D, B1, B2, B3);
1464
0
    pt_in_seg = lw_pt_in_seg(&D, A1, A2);
1465
1466
    /* Is D contained in both A and B? */
1467
0
    if (pt_in_arc && pt_in_seg)
1468
0
    {
1469
0
      lw_dist2d_distpts_set(dl, 0.0, &D, &D);
1470
0
      return LW_TRUE;
1471
0
    }
1472
0
  }
1473
  /* Line misses circle. */
1474
  /* If closest point to A on circle is within B, then that's the closest */
1475
  /* Otherwise, the closest point will be an end point of A */
1476
0
  else
1477
0
  {
1478
0
    POINT2D G; /* Point on circle closest to A */
1479
0
    G.x = C.x + (D.x - C.x) * radius_C / dist_C_D;
1480
0
    G.y = C.y + (D.y - C.y) * radius_C / dist_C_D;
1481
1482
0
    pt_in_arc = lw_pt_in_arc(&G, B1, B2, B3);
1483
0
    pt_in_seg = lw_pt_in_seg(&D, A1, A2);
1484
1485
    /* Closest point is on the interior of A and B */
1486
0
    if (pt_in_arc && pt_in_seg)
1487
0
      return lw_dist2d_pt_pt(&D, &G, dl);
1488
0
  }
1489
1490
  /* Now we test the many combinations of end points with either */
1491
  /* arcs or edges. Each previous check determined if the closest */
1492
  /* potential point was within the arc/segment inscribed on the */
1493
  /* line/circle holding the arc/segment. */
1494
1495
  /* Closest point is in the arc, but not in the segment, so */
1496
  /* one of the segment end points must be the closest. */
1497
0
  if (pt_in_arc && !pt_in_seg)
1498
0
  {
1499
0
    lw_dist2d_pt_arc(A1, B1, B2, B3, dl);
1500
0
    lw_dist2d_pt_arc(A2, B1, B2, B3, dl);
1501
0
    return LW_TRUE;
1502
0
  }
1503
  /* or, one of the arc end points is the closest */
1504
0
  else if (pt_in_seg && !pt_in_arc)
1505
0
  {
1506
0
    lw_dist2d_pt_seg(B1, A1, A2, dl);
1507
0
    lw_dist2d_pt_seg(B3, A1, A2, dl);
1508
0
    return LW_TRUE;
1509
0
  }
1510
  /* Finally, one of the end-point to end-point combos is the closest. */
1511
0
  else
1512
0
  {
1513
0
    lw_dist2d_pt_pt(A1, B1, dl);
1514
0
    lw_dist2d_pt_pt(A1, B3, dl);
1515
0
    lw_dist2d_pt_pt(A2, B1, dl);
1516
0
    lw_dist2d_pt_pt(A2, B3, dl);
1517
0
    return LW_TRUE;
1518
0
  }
1519
1520
0
  return LW_FALSE;
1521
0
}
1522
1523
int
1524
lw_dist2d_pt_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, DISTPTS *dl)
1525
0
{
1526
0
  double radius_A, d;
1527
0
  POINT2D C; /* center of circle defined by arc A */
1528
0
  POINT2D X; /* point circle(A) where line from C to P crosses */
1529
1530
0
  if (dl->mode < 0)
1531
0
    lwerror("lw_dist2d_pt_arc does not support maxdistance mode");
1532
1533
  /* What if the arc is a point? */
1534
0
  if (lw_arc_is_pt(A1, A2, A3))
1535
0
    return lw_dist2d_pt_pt(P, A1, dl);
1536
1537
  /* Calculate centers and radii of circles. */
1538
0
  radius_A = lw_arc_center(A1, A2, A3, &C);
1539
1540
  /* This "arc" is actually a line (A2 is colinear with A1,A3) */
1541
0
  if (radius_A < 0.0)
1542
0
    return lw_dist2d_pt_seg(P, A1, A3, dl);
1543
1544
  /* Distance from point to center */
1545
0
  d = distance2d_pt_pt(&C, P);
1546
1547
  /* P is the center of the circle */
1548
0
  if (FP_EQUALS(d, 0.0))
1549
0
  {
1550
0
    lw_dist2d_distpts_set(dl, radius_A, A1, P);
1551
0
    return LW_TRUE;
1552
0
  }
1553
1554
  /* X is the point on the circle where the line from P to C crosses */
1555
0
  X.x = C.x + (P->x - C.x) * radius_A / d;
1556
0
  X.y = C.y + (P->y - C.y) * radius_A / d;
1557
1558
  /* Is crossing point inside the arc? Or arc is actually circle? */
1559
0
  if (p2d_same(A1, A3) || lw_pt_in_arc(&X, A1, A2, A3))
1560
0
  {
1561
0
    lw_dist2d_pt_pt(P, &X, dl);
1562
0
  }
1563
0
  else
1564
0
  {
1565
    /* Distance is the minimum of the distances to the arc end points */
1566
0
    lw_dist2d_pt_pt(A1, P, dl);
1567
0
    lw_dist2d_pt_pt(A3, P, dl);
1568
0
  }
1569
0
  return LW_TRUE;
1570
0
}
1571
1572
1573
1574
/**
1575
 * @brief Calculates the intersection points of a circle and line.
1576
 *
1577
 * This function assumes the center and test point are distinct.
1578
 * Finds the line between the circle center and test point, and
1579
 * the two intersection points on the circle defined by that line.
1580
 * If those points fall within the provided arc (A1,A2,A3) then
1581
 * the points are added to the provided array (I) and the array
1582
 * length counter is incremented.
1583
 *
1584
 * @param A1   Point of arc
1585
 * @param A2   Point of arc
1586
 * @param A3   Point of arc
1587
 * @param center_A   Center of arc A circle
1588
 * @param radius_A   Radius of arc A circle
1589
 * @param P    Point to use in calculating intersection line
1590
 * @param I    [out] Pointer to an return value array
1591
 * @param ni   [out] Pointer to return array size counter
1592
 * @return int
1593
 */
1594
static int
1595
lw_dist2d_circle_intersections(
1596
  const POINT2D *A1, const POINT2D *A2, const POINT2D *A3,
1597
  const POINT2D *center_A,
1598
  double radius_A,
1599
  const POINT2D *P,
1600
  POINT2D *I,
1601
  uint32_t *ni)
1602
0
{
1603
0
  POINT2D R;
1604
1605
  // If the test point is on the center of the other
1606
  // arc, some other point has to be closer, by definition.
1607
0
  if (p2d_same(center_A, P))
1608
0
    return 0;
1609
1610
  // Calculate vector from the center to the pt
1611
0
  double dir_x = center_A->x - P->x;
1612
0
  double dir_y = center_A->y - P->y;
1613
0
  double dist = sqrt(dir_x * dir_x + dir_y * dir_y);
1614
1615
  // Normalize the direction vector to get a unit vector (length = 1)
1616
0
  double unit_x = dir_x / dist;
1617
0
  double unit_y = dir_y / dist;
1618
1619
  // Calculate the two intersection points on the circle
1620
  // Point 1: Move from center_A along the unit vector by distance radius_A
1621
0
  R.x = center_A->x + unit_x * radius_A;
1622
0
  R.y = center_A->y + unit_y * radius_A;
1623
0
  if (lw_pt_in_arc(&R, A1, A2, A3))
1624
0
    I[(*ni)++] = R;
1625
1626
  // Point 2: Move from center_A against the unit vector by distance radius_A
1627
0
  R.x = center_A->x - unit_x * radius_A;
1628
0
  R.y = center_A->y - unit_y * radius_A;
1629
0
  if (lw_pt_in_arc(&R, A1, A2, A3))
1630
0
    I[(*ni)++] = R;
1631
1632
0
  return 0;
1633
0
}
1634
1635
1636
/**
1637
 * @brief Calculates the intersection points of two overlapping circles.
1638
 *
1639
 * This function assumes the circles are known to intersect at one or two points.
1640
 * Specifically, the distance 'd' between their centers must satisfy:
1641
 * d < (rA + rB)  and  d > fabs(rA - rB)
1642
 * If these conditions are not met, the results are undefined.
1643
 *
1644
 * @param cA   [in] Pointer to the center point of the first circle (A).
1645
 * @param rA   [in] The radius of the first circle (A).
1646
 * @param cB   [in] Pointer to the center point of the second circle (B).
1647
 * @param rB   [in] The radius of the second circle (B).
1648
 * @param I    [out] Pointer to an array of at least 2 POINT2D structs to store the results.
1649
 * I[0] will contain the first intersection point.
1650
 * If a second exists, it will be in I[1].
1651
 * @return int The number of intersection points found (1 or 2). Returns 0 if
1652
 * the centers are coincident or another error occurs.
1653
 */
1654
static uint32_t
1655
lw_dist2d_circle_circle_intersections(
1656
  const POINT2D *cA, double rA,
1657
  const POINT2D *cB, double rB,
1658
  POINT2D *I)
1659
0
{
1660
  // Vector from center A to center B
1661
0
  double dx = cB->x - cA->x;
1662
0
  double dy = cB->y - cA->y;
1663
1664
  // Distance between the centers
1665
0
  double d = sqrt(dx * dx + dy * dy);
1666
1667
  // 'a' is the distance from the center of circle A to the point P,
1668
  // which is the base of the right triangle formed by cA, P, and an intersection point.
1669
0
  double a = (rA * rA - rB * rB + d * d) / (2.0 * d);
1670
1671
  // 'h' is the height of that right triangle.
1672
0
  double h_squared = rA * rA - a * a;
1673
1674
  // Due to floating point errors, h_squared can be slightly negative.
1675
  // This happens when the circles are perfectly tangent. Clamp to 0.
1676
0
  if (h_squared < 0.0)
1677
0
    h_squared = 0.0;
1678
1679
0
  double h = sqrt(h_squared);
1680
1681
  // Find the coordinates of point P
1682
0
  double Px = cA->x + a * (dx / d);
1683
0
  double Py = cA->y + a * (dy / d);
1684
1685
  // The two intersection points are found by moving from P by a distance 'h'
1686
  // in directions perpendicular to the line connecting the centers.
1687
  // The perpendicular vector to (dx, dy) is (-dy, dx).
1688
1689
  // Intersection point 1
1690
0
  I[0].x = Px - h * (dy / d);
1691
0
  I[0].y = Py + h * (dx / d);
1692
1693
  // If h is very close to 0, the circles are tangent and there's only one intersection point.
1694
0
  if (FP_IS_ZERO(h))
1695
0
    return 1;
1696
1697
  // Intersection point 2
1698
0
  I[1].x = Px + h * (dy / d);
1699
0
  I[1].y = Py - h * (dx / d);
1700
1701
0
  return 2;
1702
0
}
1703
1704
1705
int
1706
lw_dist2d_arc_arc(
1707
  const POINT2D *A1, const POINT2D *A2, const POINT2D *A3,
1708
  const POINT2D *B1, const POINT2D *B2, const POINT2D *B3,
1709
  DISTPTS *dl)
1710
0
{
1711
0
  POINT2D pts_A[16];            /* Points on A that might be the nearest */
1712
0
  POINT2D pts_B[16];            /* Points on B that might be the nearest */
1713
0
  uint32_t ai = 0, bi = 0;      /* Number of points in pts_A and pts_B */
1714
0
  POINT2D center_A, center_B;   /* Center points of arcs A and B */
1715
0
  double radius_A, radius_B, d; /* Radii of arcs A and B */
1716
0
  int is_disjoint, is_overlapping, is_contained, is_same_center;
1717
0
  POINT2D intersectionPts[2];
1718
1719
0
  if (dl->mode != DIST_MIN)
1720
0
    lwerror("lw_dist2d_arc_arc only supports mindistance");
1721
1722
  /* What if one or both of our "arcs" is actually a point? */
1723
0
  if (lw_arc_is_pt(B1, B2, B3) && lw_arc_is_pt(A1, A2, A3))
1724
0
    return lw_dist2d_pt_pt(B1, A1, dl);
1725
0
  else if (lw_arc_is_pt(B1, B2, B3))
1726
0
    return lw_dist2d_pt_arc(B1, A1, A2, A3, dl);
1727
0
  else if (lw_arc_is_pt(A1, A2, A3))
1728
0
    return lw_dist2d_pt_arc(A1, B1, B2, B3, dl);
1729
1730
  /* Calculate centers and radii of circles. */
1731
0
  radius_A = lw_arc_center(A1, A2, A3, &center_A);
1732
0
  radius_B = lw_arc_center(B1, B2, B3, &center_B);
1733
1734
  /* Two co-linear arcs?!? That's two segments. */
1735
0
  if (radius_A < 0 && radius_B < 0)
1736
0
    return lw_dist2d_seg_seg(A1, A3, B1, B3, dl);
1737
1738
  /* A is co-linear, delegate to lw_dist_seg_arc here. */
1739
0
  if (radius_A < 0)
1740
0
    return lw_dist2d_seg_arc(A1, A3, B1, B2, B3, dl);
1741
1742
  /* B is co-linear, delegate to lw_dist_seg_arc here. */
1743
0
  if (radius_B < 0)
1744
0
    return lw_dist2d_seg_arc(B1, B3, A1, A2, A3, dl);
1745
1746
  /* Circle relationships */
1747
0
  d = distance2d_pt_pt(&center_A, &center_B);
1748
0
  is_disjoint = (d > (radius_A + radius_B));
1749
0
  is_contained = (d < fabs(radius_A - radius_B));
1750
0
  is_same_center = p2d_same(&center_A, &center_B);
1751
0
  is_overlapping = ! (is_disjoint || is_contained || is_same_center);
1752
1753
  /*
1754
   * Prime the array of potential closest points with the
1755
   * arc end points, which frequently participate in closest
1756
   * points.
1757
   */
1758
0
  pts_A[ai++] = *A1;
1759
0
  pts_A[ai++] = *A3;
1760
0
  pts_B[bi++] = *B1;
1761
0
  pts_B[bi++] = *B3;
1762
1763
  /*
1764
   * Overlapping circles might have a zero distance
1765
   * case if the circle intersection points are inside both
1766
   * arcs.
1767
   */
1768
0
  if (is_overlapping)
1769
0
  {
1770
    /*
1771
     * Find the two points the circles intersect at.
1772
     */
1773
0
    uint32_t npoints = lw_dist2d_circle_circle_intersections(
1774
0
      &center_A, radius_A,
1775
0
      &center_B, radius_B,
1776
0
      intersectionPts);
1777
0
    for (uint32_t i = 0; i < npoints; i++)
1778
0
    {
1779
      /*
1780
       * If an intersection point is contained in both
1781
       * arcs, that is a location of zero distance, so
1782
       * we are done calculating.
1783
       */
1784
0
      if (lw_pt_in_arc(&intersectionPts[i], A1, A2, A3) &&
1785
0
        lw_pt_in_arc(&intersectionPts[i], B1, B2, B3))
1786
0
      {
1787
0
        lw_dist2d_distpts_set(dl, 0.0, &intersectionPts[i], &intersectionPts[i]);
1788
0
        return LW_TRUE;
1789
0
      }
1790
0
    }
1791
0
  }
1792
1793
  /*
1794
   * Join the circle centers and find the places that
1795
   * line intersects the circles. Where those places
1796
   * are in the arcs, they are potential sites of the
1797
   * closest points.
1798
   */
1799
0
  if (is_disjoint || is_contained || is_overlapping)
1800
0
  {
1801
0
    if (!is_same_center)
1802
0
    {
1803
      /* Add points on A that intersect line from center_A to center_B */
1804
0
      lw_dist2d_circle_intersections(
1805
0
          A1, A2, A3,
1806
0
          &center_A, radius_A, &center_B,
1807
0
          pts_A, &ai);
1808
1809
      /* Add points on B that intersect line from center_B to center_A */
1810
0
      lw_dist2d_circle_intersections(
1811
0
          B1, B2, B3,
1812
0
          &center_B, radius_B, &center_A,
1813
0
          pts_B, &bi);
1814
0
    }
1815
1816
    /* Add points on A that intersect line to B1 */
1817
0
    lw_dist2d_circle_intersections(
1818
0
        A1, A2, A3,
1819
0
        &center_A, radius_A, B1,
1820
0
        pts_A, &ai);
1821
1822
    /* Add points on A that intersect line to B3 */
1823
0
    lw_dist2d_circle_intersections(
1824
0
        A1, A2, A3,
1825
0
        &center_A, radius_A, B3,
1826
0
        pts_A, &ai);
1827
1828
    /* Add points on B that intersect line to A1 */
1829
0
    lw_dist2d_circle_intersections(
1830
0
        B1, B2, B3,
1831
0
        &center_B, radius_B, A1,
1832
0
        pts_B, &bi);
1833
1834
    /* Add points on B that intersect line to A3 */
1835
0
    lw_dist2d_circle_intersections(
1836
0
        B1, B2, B3,
1837
0
        &center_B, radius_B, A3,
1838
0
        pts_B, &bi);
1839
0
  }
1840
1841
  /*
1842
   * Now just brute force check all pairs of participating
1843
   * points, to find the pair that is closest together.
1844
   */
1845
0
  for (uint32_t i = 0; i < ai; i++)
1846
0
    for (uint32_t j = 0; j < bi; j++)
1847
0
      lw_dist2d_pt_pt(&pts_A[i], &pts_B[j], dl);
1848
1849
0
  return LW_TRUE;
1850
0
}
1851
1852
1853
/**
1854
Finds the shortest distance between two segments.
1855
This function is changed so it is not doing any comparison of distance
1856
but just sending every possible combination further to lw_dist2d_pt_seg
1857
*/
1858
int
1859
lw_dist2d_seg_seg(const POINT2D *A, const POINT2D *B, const POINT2D *C, const POINT2D *D, DISTPTS *dl)
1860
0
{
1861
0
  double s_top, s_bot, s;
1862
0
  double r_top, r_bot, r;
1863
1864
  /*A and B are the same point */
1865
0
  if ((A->x == B->x) && (A->y == B->y))
1866
0
  {
1867
0
    return lw_dist2d_pt_seg(A, C, D, dl);
1868
0
  }
1869
1870
  /*U and V are the same point */
1871
0
  if ((C->x == D->x) && (C->y == D->y))
1872
0
  {
1873
0
    dl->twisted = ((dl->twisted) * (-1));
1874
0
    return lw_dist2d_pt_seg(D, A, B, dl);
1875
0
  }
1876
1877
  /* AB and CD are line segments */
1878
  /* from comp.graphics.algo
1879
1880
  Solving the above for r and s yields
1881
        (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
1882
       r = ----------------------------- (eqn 1)
1883
        (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
1884
1885
      (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
1886
    s = ----------------------------- (eqn 2)
1887
      (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
1888
  Let P be the position vector of the intersection point, then
1889
    P=A+r(B-A) or
1890
    Px=Ax+r(Bx-Ax)
1891
    Py=Ay+r(By-Ay)
1892
  By examining the values of r & s, you can also determine some other limiting conditions:
1893
    If 0<=r<=1 & 0<=s<=1, intersection exists
1894
    r<0 or r>1 or s<0 or s>1 line segments do not intersect
1895
    If the denominator in eqn 1 is zero, AB & CD are parallel
1896
    If the numerator in eqn 1 is also zero, AB & CD are collinear.
1897
1898
  */
1899
0
  r_top = (A->y - C->y) * (D->x - C->x) - (A->x - C->x) * (D->y - C->y);
1900
0
  r_bot = (B->x - A->x) * (D->y - C->y) - (B->y - A->y) * (D->x - C->x);
1901
1902
0
  s_top = (A->y - C->y) * (B->x - A->x) - (A->x - C->x) * (B->y - A->y);
1903
0
  s_bot = (B->x - A->x) * (D->y - C->y) - (B->y - A->y) * (D->x - C->x);
1904
1905
0
  if ((r_bot == 0) || (s_bot == 0))
1906
0
  {
1907
0
    if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl)))
1908
0
    {
1909
      /* change the order of inputted geometries and that we notice by changing sign on dl->twisted*/
1910
0
      dl->twisted *= -1;
1911
0
      return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl)));
1912
0
    }
1913
0
    else
1914
0
      return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/
1915
0
  }
1916
1917
0
  s = s_top / s_bot;
1918
0
  r = r_top / r_bot;
1919
1920
0
  if (((r < 0) || (r > 1) || (s < 0) || (s > 1)) || (dl->mode == DIST_MAX))
1921
0
  {
1922
0
    if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl)))
1923
0
    {
1924
      /* change the order of inputted geometries and that we notice by changing sign on dl->twisted*/
1925
0
      dl->twisted *= -1;
1926
0
      return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl)));
1927
0
    }
1928
0
    else
1929
0
      return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/
1930
0
  }
1931
0
  else
1932
0
  {
1933
    /* If there is intersection we identify the intersection point and return it but only if we are looking
1934
     * for mindistance */
1935
0
    if (dl->mode == DIST_MIN)
1936
0
    {
1937
0
      POINT2D theP;
1938
1939
0
      if (((A->x == C->x) && (A->y == C->y)) || ((A->x == D->x) && (A->y == D->y)))
1940
0
      {
1941
0
        theP.x = A->x;
1942
0
        theP.y = A->y;
1943
0
      }
1944
0
      else if (((B->x == C->x) && (B->y == C->y)) || ((B->x == D->x) && (B->y == D->y)))
1945
0
      {
1946
0
        theP.x = B->x;
1947
0
        theP.y = B->y;
1948
0
      }
1949
0
      else
1950
0
      {
1951
0
        theP.x = A->x + r * (B->x - A->x);
1952
0
        theP.y = A->y + r * (B->y - A->y);
1953
0
      }
1954
0
      lw_dist2d_distpts_set(dl, 0, &theP, &theP);
1955
0
    }
1956
0
    return LW_TRUE;
1957
0
  }
1958
0
}
1959
1960
/*------------------------------------------------------------------------------------------------------------
1961
End of Brute force functions
1962
--------------------------------------------------------------------------------------------------------------*/
1963
1964
/*------------------------------------------------------------------------------------------------------------
1965
New faster distance calculations
1966
--------------------------------------------------------------------------------------------------------------*/
1967
1968
/**
1969
1970
The new faster calculation comparing pointarray to another pointarray
1971
the arrays can come from both polygons and linestrings.
1972
The naming is not good but comes from that it compares a
1973
chosen selection of the points not all of them
1974
*/
1975
int
1976
lw_dist2d_fast_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS *dl, GBOX *box1, GBOX *box2)
1977
0
{
1978
  /*here we define two lists to hold our calculated "z"-values and the order number in the geometry*/
1979
1980
0
  double k, thevalue;
1981
0
  float deltaX, deltaY, c1m, c2m;
1982
0
  POINT2D c1, c2;
1983
0
  const POINT2D *theP;
1984
0
  float min1X, max1X, max1Y, min1Y, min2X, max2X, max2Y, min2Y;
1985
0
  int t;
1986
0
  int n1 = l1->npoints;
1987
0
  int n2 = l2->npoints;
1988
1989
0
  LISTSTRUCT *list1, *list2;
1990
0
  list1 = (LISTSTRUCT *)lwalloc(sizeof(LISTSTRUCT) * n1);
1991
0
  list2 = (LISTSTRUCT *)lwalloc(sizeof(LISTSTRUCT) * n2);
1992
1993
0
  LWDEBUG(2, "lw_dist2d_fast_ptarray_ptarray is called");
1994
1995
0
  max1X = box1->xmax;
1996
0
  min1X = box1->xmin;
1997
0
  max1Y = box1->ymax;
1998
0
  min1Y = box1->ymin;
1999
0
  max2X = box2->xmax;
2000
0
  min2X = box2->xmin;
2001
0
  max2Y = box2->ymax;
2002
0
  min2Y = box2->ymin;
2003
  /*we want the center of the bboxes, and calculate the slope between the centerpoints*/
2004
0
  c1.x = min1X + (max1X - min1X) / 2;
2005
0
  c1.y = min1Y + (max1Y - min1Y) / 2;
2006
0
  c2.x = min2X + (max2X - min2X) / 2;
2007
0
  c2.y = min2Y + (max2Y - min2Y) / 2;
2008
2009
0
  deltaX = (c2.x - c1.x);
2010
0
  deltaY = (c2.y - c1.y);
2011
2012
  /*Here we calculate where the line perpendicular to the center-center line crosses the axes for each vertex
2013
  if the center-center line is vertical the perpendicular line will be horizontal and we find it's crossing the
2014
  Y-axes with z = y-kx */
2015
0
  if ((deltaX * deltaX) < (deltaY * deltaY)) /*North or South*/
2016
0
  {
2017
0
    k = -deltaX / deltaY;
2018
0
    for (t = 0; t < n1; t++) /*for each segment in L1 */
2019
0
    {
2020
0
      theP = getPoint2d_cp(l1, t);
2021
0
      thevalue = theP->y - (k * theP->x);
2022
0
      list1[t].themeasure = thevalue;
2023
0
      list1[t].pnr = t;
2024
0
    }
2025
0
    for (t = 0; t < n2; t++) /*for each segment in L2*/
2026
0
    {
2027
0
      theP = getPoint2d_cp(l2, t);
2028
0
      thevalue = theP->y - (k * theP->x);
2029
0
      list2[t].themeasure = thevalue;
2030
0
      list2[t].pnr = t;
2031
0
    }
2032
0
    c1m = c1.y - (k * c1.x);
2033
0
    c2m = c2.y - (k * c2.x);
2034
0
  }
2035
2036
  /*if the center-center line is horizontal the perpendicular line will be vertical. To eliminate problems with
2037
   dividing by zero we are here mirroring the coordinate-system and we find it's crossing the X-axes with z =
2038
   x-(1/k)y */
2039
0
  else /*West or East*/
2040
0
  {
2041
0
    k = -deltaY / deltaX;
2042
0
    for (t = 0; t < n1; t++) /*for each segment in L1 */
2043
0
    {
2044
0
      theP = getPoint2d_cp(l1, t);
2045
0
      thevalue = theP->x - (k * theP->y);
2046
0
      list1[t].themeasure = thevalue;
2047
0
      list1[t].pnr = t;
2048
      /* lwnotice("l1 %d, measure=%f",t,thevalue ); */
2049
0
    }
2050
0
    for (t = 0; t < n2; t++) /*for each segment in L2*/
2051
0
    {
2052
0
      theP = getPoint2d_cp(l2, t);
2053
0
      thevalue = theP->x - (k * theP->y);
2054
0
      list2[t].themeasure = thevalue;
2055
0
      list2[t].pnr = t;
2056
      /* lwnotice("l2 %d, measure=%f",t,thevalue ); */
2057
0
    }
2058
0
    c1m = c1.x - (k * c1.y);
2059
0
    c2m = c2.x - (k * c2.y);
2060
0
  }
2061
2062
  /*we sort our lists by the calculated values*/
2063
0
  qsort(list1, n1, sizeof(LISTSTRUCT), struct_cmp_by_measure);
2064
0
  qsort(list2, n2, sizeof(LISTSTRUCT), struct_cmp_by_measure);
2065
2066
0
  if (c1m < c2m)
2067
0
  {
2068
0
    if (!lw_dist2d_pre_seg_seg(l1, l2, list1, list2, k, dl))
2069
0
    {
2070
0
      lwfree(list1);
2071
0
      lwfree(list2);
2072
0
      return LW_FALSE;
2073
0
    }
2074
0
  }
2075
0
  else
2076
0
  {
2077
0
    dl->twisted = ((dl->twisted) * (-1));
2078
0
    if (!lw_dist2d_pre_seg_seg(l2, l1, list2, list1, k, dl))
2079
0
    {
2080
0
      lwfree(list1);
2081
0
      lwfree(list2);
2082
0
      return LW_FALSE;
2083
0
    }
2084
0
  }
2085
0
  lwfree(list1);
2086
0
  lwfree(list2);
2087
0
  return LW_TRUE;
2088
0
}
2089
2090
int
2091
struct_cmp_by_measure(const void *a, const void *b)
2092
0
{
2093
0
  LISTSTRUCT *ia = (LISTSTRUCT *)a;
2094
0
  LISTSTRUCT *ib = (LISTSTRUCT *)b;
2095
0
  return (ia->themeasure > ib->themeasure) ? 1 : ((ia->themeasure < ib->themeasure) ? -1 : 0);
2096
0
}
2097
2098
/** preparation before lw_dist2d_seg_seg. */
2099
int
2100
lw_dist2d_pre_seg_seg(POINTARRAY *l1, POINTARRAY *l2, LISTSTRUCT *list1, LISTSTRUCT *list2, double k, DISTPTS *dl)
2101
0
{
2102
0
  const POINT2D *p1, *p2, *p3, *p4, *p01, *p02;
2103
0
  int pnr1, pnr2, pnr3, pnr4, n1, n2, i, u, r, twist;
2104
0
  double maxmeasure;
2105
0
  n1 = l1->npoints;
2106
0
  n2 = l2->npoints;
2107
2108
0
  LWDEBUG(2, "lw_dist2d_pre_seg_seg is called");
2109
2110
0
  p1 = getPoint2d_cp(l1, list1[0].pnr);
2111
0
  p3 = getPoint2d_cp(l2, list2[0].pnr);
2112
0
  lw_dist2d_pt_pt(p1, p3, dl);
2113
0
  maxmeasure = sqrt(dl->distance * dl->distance + (dl->distance * dl->distance * k * k));
2114
0
  twist = dl->twisted; /*to keep the incoming order between iterations*/
2115
0
  for (i = (n1 - 1); i >= 0; --i)
2116
0
  {
2117
    /*we break this iteration when we have checked every point closer to our perpendicular "checkline" than
2118
     * our shortest found distance*/
2119
0
    if (((list2[0].themeasure - list1[i].themeasure)) > maxmeasure)
2120
0
      break;
2121
    /*because we are not iterating in the original point order we have to check the segment before and after
2122
     * every point*/
2123
0
    for (r = -1; r <= 1; r += 2)
2124
0
    {
2125
0
      pnr1 = list1[i].pnr;
2126
0
      p1 = getPoint2d_cp(l1, pnr1);
2127
0
      if (pnr1 + r < 0)
2128
0
      {
2129
0
        p01 = getPoint2d_cp(l1, (n1 - 1));
2130
0
        if ((p1->x == p01->x) && (p1->y == p01->y))
2131
0
          pnr2 = (n1 - 1);
2132
0
        else
2133
0
          pnr2 = pnr1; /* if it is a line and the last and first point is not the same we
2134
              avoid the edge between start and end this way*/
2135
0
      }
2136
2137
0
      else if (pnr1 + r > (n1 - 1))
2138
0
      {
2139
0
        p01 = getPoint2d_cp(l1, 0);
2140
0
        if ((p1->x == p01->x) && (p1->y == p01->y))
2141
0
          pnr2 = 0;
2142
0
        else
2143
0
          pnr2 = pnr1; /* if it is a line and the last and first point is not the same we
2144
              avoid the edge between start and end this way*/
2145
0
      }
2146
0
      else
2147
0
        pnr2 = pnr1 + r;
2148
2149
0
      p2 = getPoint2d_cp(l1, pnr2);
2150
0
      for (u = 0; u < n2; ++u)
2151
0
      {
2152
0
        if (((list2[u].themeasure - list1[i].themeasure)) >= maxmeasure)
2153
0
          break;
2154
0
        pnr3 = list2[u].pnr;
2155
0
        p3 = getPoint2d_cp(l2, pnr3);
2156
0
        if (pnr3 == 0)
2157
0
        {
2158
0
          p02 = getPoint2d_cp(l2, (n2 - 1));
2159
0
          if ((p3->x == p02->x) && (p3->y == p02->y))
2160
0
            pnr4 = (n2 - 1);
2161
0
          else
2162
0
            pnr4 = pnr3; /* if it is a line and the last and first point is not the
2163
                same we avoid the edge between start and end this way*/
2164
0
        }
2165
0
        else
2166
0
          pnr4 = pnr3 - 1;
2167
2168
0
        p4 = getPoint2d_cp(l2, pnr4);
2169
0
        dl->twisted = twist;
2170
0
        if (!lw_dist2d_selected_seg_seg(p1, p2, p3, p4, dl))
2171
0
          return LW_FALSE;
2172
2173
0
        if (pnr3 >= (n2 - 1))
2174
0
        {
2175
0
          p02 = getPoint2d_cp(l2, 0);
2176
0
          if ((p3->x == p02->x) && (p3->y == p02->y))
2177
0
            pnr4 = 0;
2178
0
          else
2179
0
            pnr4 = pnr3; /* if it is a line and the last and first point is not the
2180
                same we avoid the edge between start and end this way*/
2181
0
        }
2182
2183
0
        else
2184
0
          pnr4 = pnr3 + 1;
2185
2186
0
        p4 = getPoint2d_cp(l2, pnr4);
2187
0
        dl->twisted = twist; /*we reset the "twist" for each iteration*/
2188
0
        if (!lw_dist2d_selected_seg_seg(p1, p2, p3, p4, dl))
2189
0
          return LW_FALSE;
2190
        /*here we "translate" the found mindistance so it can be compared to our "z"-values*/
2191
0
        maxmeasure = sqrt(dl->distance * dl->distance + (dl->distance * dl->distance * k * k));
2192
0
      }
2193
0
    }
2194
0
  }
2195
2196
0
  return LW_TRUE;
2197
0
}
2198
2199
/**
2200
  This is the same function as lw_dist2d_seg_seg but
2201
  without any calculations to determine intersection since we
2202
  already know they do not intersect
2203
*/
2204
int
2205
lw_dist2d_selected_seg_seg(const POINT2D *A, const POINT2D *B, const POINT2D *C, const POINT2D *D, DISTPTS *dl)
2206
0
{
2207
  /*A and B are the same point */
2208
0
  if ((A->x == B->x) && (A->y == B->y))
2209
0
  {
2210
0
    return lw_dist2d_pt_seg(A, C, D, dl);
2211
0
  }
2212
  /*U and V are the same point */
2213
2214
0
  if ((C->x == D->x) && (C->y == D->y))
2215
0
  {
2216
0
    dl->twisted *= -1;
2217
0
    return lw_dist2d_pt_seg(D, A, B, dl);
2218
0
  }
2219
2220
0
  if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl)))
2221
0
  {
2222
    /* change the order of inputted geometries and that we notice by changing sign on dl->twisted */
2223
0
    dl->twisted *= -1;
2224
0
    return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl)));
2225
0
  }
2226
0
  else
2227
0
    return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/
2228
0
}
2229
2230
/*------------------------------------------------------------------------------------------------------------
2231
End of New faster distance calculations
2232
--------------------------------------------------------------------------------------------------------------*/
2233
2234
/*------------------------------------------------------------------------------------------------------------
2235
Functions in common for Brute force and new calculation
2236
--------------------------------------------------------------------------------------------------------------*/
2237
2238
int
2239
lw_dist2d_pt_seg(const POINT2D *C, const POINT2D *A, const POINT2D *B, DISTPTS *dl)
2240
0
{
2241
0
  POINT2D P;
2242
0
  int is_vertical   = (A->x == B->x);
2243
0
  int is_horizontal = (A->y == B->y);
2244
2245
  /*
2246
   * If A == B, then use pt-pt distance
2247
   */
2248
0
  if (is_vertical && is_horizontal)
2249
0
    return lw_dist2d_pt_pt(C, A, dl);
2250
2251
  /*
2252
   * We use comp.graphics.algorithms Frequently Asked Questions method
2253
   *
2254
   *  Let the point be C (Cx,Cy) and the line be AB (Ax,Ay) to (Bx,By).
2255
   *  Let P be the point of perpendicular projection of C on AB.  The parameter
2256
   *  r, which indicates P's position along AB, is computed by the dot product
2257
   *  of AC and AB divided by the square of the length of AB:
2258
   *
2259
   *  (1)     AC dot AB
2260
   *      r = ---------
2261
   *          ||AB||^2
2262
   *
2263
   *  The length of a line segment
2264
   *
2265
   *      L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 )
2266
   *
2267
   *  So (1) expands to:
2268
   *
2269
   *          (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay)
2270
   *      r = -------------------------------
2271
   *                        L^2
2272
   *
2273
   *  r has the following meaning:
2274
   *
2275
   *      r=0      P = A
2276
   *      r=1      P = B
2277
   *      r<0      P is on the backward extension of AB
2278
   *      r>1      P is on the forward extension of AB
2279
   *      0<r<1    P is interior to AB
2280
   *
2281
   */
2282
2283
0
  double dx = B->x - A->x;
2284
0
  double dy = B->y - A->y;
2285
0
  double L2 = (dx*dx) + (dy*dy);
2286
2287
0
  double r = ((C->x - A->x) * dx + (C->y - A->y) * dy) / L2;
2288
2289
  /*
2290
   * This is for finding the maxdistance.
2291
   * The maxdistance have to be between two vertices,
2292
   * compared to mindistance which can be between two vertices.
2293
   */
2294
0
  if (dl->mode == DIST_MAX)
2295
0
  {
2296
0
    if (r >= 0.5)
2297
0
      return lw_dist2d_pt_pt(C, A, dl);
2298
0
    else /* (r < 0.5) */
2299
0
      return lw_dist2d_pt_pt(C, B, dl);
2300
0
  }
2301
2302
  /*
2303
   * If projected point P is outside point A
2304
   */
2305
0
  if (r < 0)
2306
0
    return lw_dist2d_pt_pt(C, A, dl);
2307
2308
  /*
2309
   * If projected point P is is outside
2310
   * point B or on point B
2311
   */
2312
0
  if (r >= 1)
2313
0
    return lw_dist2d_pt_pt(C, B, dl);
2314
2315
  /*
2316
   * If point C is on the segment AB then
2317
   * distance is zero and nearest point is C
2318
   */
2319
0
  if ((A->y - C->y) * dx == (A->x - C->x) * dy)
2320
0
  {
2321
0
    lw_dist2d_distpts_set(dl, 0.0, C, C);
2322
0
  }
2323
2324
  /*
2325
   *  The point P can then be found:
2326
   *
2327
   *      Px = Ax + r(Bx-Ax)
2328
   *      Py = Ay + r(By-Ay)
2329
   *
2330
   *  And the distance from A to P = r*L.
2331
   */
2332
2333
  /*
2334
   * If the projection of point C on the segment is
2335
   * between A and B then we find that "point on segment"
2336
   * and send it to lw_dist2d_pt_pt
2337
   */
2338
0
  if (is_horizontal || is_vertical)
2339
0
  {
2340
0
    P.x = A->x + r * dx;
2341
0
    P.y = A->y + r * dy;
2342
0
    return lw_dist2d_pt_pt(C, &P, dl);
2343
0
  }
2344
2345
  /*
2346
   *
2347
   *  Use another parameter s to indicate the location along PC, with the
2348
   *  following meaning:
2349
   *         s<0      C is left of AB
2350
   *         s>0      C is right of AB
2351
   *         s=0      C is on AB
2352
   *
2353
   *  Compute s as follows:
2354
   *
2355
   *          (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
2356
   *      s = -----------------------------
2357
   *                      L^2
2358
   *
2359
   *
2360
   *  Then the distance from C to P = s*L.
2361
   */
2362
2363
  /*
2364
   * Calculate distance without reference to the
2365
   * projected point P.
2366
   */
2367
0
  double s = ((A->y - C->y) * dx - (A->x - C->x) * dy) / L2;
2368
2369
0
  double dist = fabs(s) * sqrt(L2);
2370
  // double dist = fabs(s) * hypot(dx, dy);
2371
0
  if (dist < dl->distance)
2372
0
  {
2373
0
    dl->distance = dist;
2374
0
    {
2375
0
      P.x = A->x + r * dx;
2376
0
      P.y = A->y + r * dy;
2377
0
      if (dl->twisted > 0)
2378
0
      {
2379
0
        dl->p1 = *C;
2380
0
        dl->p2 = P;
2381
0
      }
2382
0
      else
2383
0
      {
2384
0
        dl->p1 = P;
2385
0
        dl->p2 = *C;
2386
0
      }
2387
0
    }
2388
0
  }
2389
0
  return LW_TRUE;
2390
0
}
2391
2392
/** Compares incoming points and stores the points closest to each other or most far away from each other depending on
2393
 * dl->mode (max or min) */
2394
int
2395
lw_dist2d_pt_pt(const POINT2D *thep1, const POINT2D *thep2, DISTPTS *dl)
2396
0
{
2397
0
  double hside = thep2->x - thep1->x;
2398
0
  double vside = thep2->y - thep1->y;
2399
0
  double dist = sqrt(hside * hside + vside * vside);
2400
2401
  /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/
2402
0
  if (((dl->distance - dist) * (dl->mode)) > 0)
2403
0
  {
2404
0
    dl->distance = dist;
2405
2406
    /* To get the points in right order. twisted is updated between 1 and (-1) every time the order is
2407
     * changed earlier in the chain*/
2408
0
    if (dl->twisted > 0)
2409
0
    {
2410
0
      dl->p1 = *thep1;
2411
0
      dl->p2 = *thep2;
2412
0
    }
2413
0
    else
2414
0
    {
2415
0
      dl->p1 = *thep2;
2416
0
      dl->p2 = *thep1;
2417
0
    }
2418
0
  }
2419
0
  return LW_TRUE;
2420
0
}
2421
2422
/*------------------------------------------------------------------------------------------------------------
2423
End of Functions in common for Brute force and new calculation
2424
--------------------------------------------------------------------------------------------------------------*/
2425
2426
inline double
2427
distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
2428
0
{
2429
0
  double hside = p2->x - p1->x;
2430
0
  double vside = p2->y - p1->y;
2431
2432
0
  return hypot(hside, vside);
2433
0
}
2434
2435
/* return distance squared, useful to avoid sqrt calculations */
2436
double
2437
distance2d_sqr_pt_seg(const POINT2D *C, const POINT2D *A, const POINT2D *B)
2438
0
{
2439
  /*if start==end, then use pt distance */
2440
0
  if ((A->x == B->x) && (A->y == B->y))
2441
0
    return distance2d_sqr_pt_pt(C, A);
2442
2443
  /*
2444
   * otherwise, we use comp.graphics.algorithms
2445
   * Frequently Asked Questions method
2446
   *
2447
   *  (1)       AC dot AB
2448
   *         r = ---------
2449
   *              ||AB||^2
2450
   *  r has the following meaning:
2451
   *  r=0 P = A
2452
   *  r=1 P = B
2453
   *  r<0 P is on the backward extension of AB
2454
   *  r>1 P is on the forward extension of AB
2455
   *  0<r<1 P is interior to AB
2456
   */
2457
2458
0
  double ba_x = (B->x - A->x);
2459
0
  double ba_y = (B->y - A->y);
2460
0
  double ab_length_sqr = (ba_x * ba_x + ba_y * ba_y);
2461
0
  double ca_x = (C->x - A->x);
2462
0
  double ca_y = (C->y - A->y);
2463
0
  double dot_ac_ab = (ca_x * ba_x + ca_y * ba_y);
2464
2465
0
  if (dot_ac_ab <= 0)
2466
0
    return distance2d_sqr_pt_pt(C, A);
2467
0
  if (dot_ac_ab >= ab_length_sqr)
2468
0
    return distance2d_sqr_pt_pt(C, B);
2469
2470
  /*
2471
   * (2)
2472
   *       (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
2473
   *  s = -----------------------------
2474
   *                L^2
2475
   *
2476
   *  Then the distance from C to P = |s|*L.
2477
   *
2478
   */
2479
2480
0
  double s_numerator = ca_x * ba_y - ca_y * ba_x;
2481
2482
  /* Distance = (s_num / ab) * (s_num / ab) * ab == s_num * s_num / ab) */
2483
0
  return s_numerator * s_numerator / ab_length_sqr;
2484
0
}
2485
2486
/**
2487
 * Compute the azimuth of segment AB in radians.
2488
 * Return 0 on exception (same point), 1 otherwise.
2489
 */
2490
int
2491
azimuth_pt_pt(const POINT2D *A, const POINT2D *B, double *d)
2492
0
{
2493
0
  if (A->x == B->x )
2494
0
  {
2495
0
    if ( A->y == B->y)
2496
0
    {
2497
0
      return LW_FALSE;
2498
0
    }
2499
0
    *d = A->y < B->y ? 0.0 : M_PI;
2500
0
    return LW_TRUE;
2501
0
  }
2502
  /* TODO: use M_PI_2 instead of M_PI/2 ? */
2503
0
  *d = fmod(2 * M_PI + M_PI / 2 - atan2(B->y - A->y, B->x - A->x), 2 * M_PI);
2504
2505
  /* TODO: convert 2*M_PI to 0 ? */
2506
0
  return LW_TRUE;
2507
0
}
2508
2509
/**
2510
 * Azimuth is angle in radians from vertical axis
2511
 *
2512
 */
2513
int
2514
project_pt(const POINT2D *P, double distance, double azimuth, POINT2D *R)
2515
0
{
2516
0
  const double TWOPI = 2.0 * M_PI;
2517
0
  double slope;
2518
  /* Deal with azimuth out of (-360,360) range */
2519
0
  int orbits = floor(azimuth / TWOPI);
2520
0
  azimuth -= TWOPI * orbits;
2521
  /* Convert from azimuth to conventional slope */
2522
0
  slope = TWOPI - azimuth + M_PI_2;
2523
0
  if (slope > 0 && slope >  TWOPI) slope -= TWOPI;
2524
0
  if (slope < 0 && slope < -TWOPI) slope += TWOPI;
2525
2526
0
  double dx = cos(slope) * distance;
2527
0
  double dy = sin(slope) * distance;
2528
0
  R->x = P->x + dx;
2529
0
  R->y = P->y + dy;
2530
0
  return LW_TRUE;
2531
0
}
2532
2533
/**
2534
 * Azimuth is angle in radians from vertical axis
2535
 *
2536
 */
2537
int
2538
project_pt_pt(const POINT4D *A, const POINT4D *B, double distance, POINT4D *R)
2539
0
{
2540
  /* Convert from azimuth to conventional slope */
2541
0
  double len = distance2d_pt_pt((const POINT2D *)A, (const POINT2D *)B);
2542
0
  double prop = distance / len;
2543
0
  double dx = (B->x - A->x) * prop;
2544
0
  double dy = (B->y - A->y) * prop;
2545
0
  double dz = (B->z - A->z) * prop;
2546
0
  double dm = (B->m - A->m) * prop;
2547
0
  R->x = B->x + dx;
2548
0
  R->y = B->y + dy;
2549
0
  if (isfinite(dz)) R->z = B->z + dz;
2550
0
  if (isfinite(dm)) R->m = B->m + dm;
2551
0
  return LW_TRUE;
2552
0
}