Coverage Report

Created: 2026-03-31 06:40

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)
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
    LWLINE *line = (LWLINE *)(comp->geoms[0]);
895
0
    return getPoint2d_cp(line->points, 0);
896
0
  }
897
0
  default:
898
0
    lwerror("lw_curvering_getfirstpoint2d_cp: unknown type");
899
0
  }
900
0
  return NULL;
901
0
}
902
903
int
904
lw_dist2d_tri_curvepoly(LWTRIANGLE *tri, LWCURVEPOLY *poly, DISTPTS *dl)
905
0
{
906
0
  const POINT2D *pt = getPoint2d_cp(tri->points, 0);
907
908
  /* If we are looking for maxdistance, just check the outer rings.*/
909
0
  if (dl->mode == DIST_MAX)
910
0
    return lw_dist2d_recursive((LWGEOM *)tri, poly->rings[0], dl);
911
912
  /* Line has a point outside curvepoly. Check distance to outer ring only. */
913
0
  if (lwgeom_contains_point(poly->rings[0], pt) == LW_OUTSIDE)
914
0
  {
915
0
    if (lw_dist2d_recursive((LWGEOM *)tri, poly->rings[0], dl))
916
0
      return LW_TRUE;
917
    /* Maybe poly is inside triangle? */
918
0
    if (lwgeom_contains_point((LWGEOM *)tri, lw_curvering_getfirstpoint2d_cp(poly->rings[0])) != LW_OUTSIDE)
919
0
    {
920
0
      lw_dist2d_distpts_set(dl, 0.0, pt, pt);
921
0
      return LW_TRUE;
922
0
    }
923
0
  }
924
925
0
  for (uint32_t i = 1; i < poly->nrings; i++)
926
0
  {
927
0
    if (!lw_dist2d_recursive((LWGEOM *)tri, poly->rings[i], dl))
928
0
      return LW_FALSE;
929
930
    /* just a check if the answer is already given */
931
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
932
0
      return LW_TRUE;
933
0
  }
934
935
  /* It's inside a hole, then distance is actual min distance */
936
0
  for (uint32_t i = 1; i < poly->nrings; i++)
937
0
    if (lwgeom_contains_point(poly->rings[i], pt) != LW_OUTSIDE)
938
0
      return LW_TRUE;
939
940
  /* Not in hole, so inside polygon */
941
0
  lw_dist2d_distpts_set(dl, 0.0, pt, pt);
942
0
  return LW_TRUE;
943
0
}
944
945
int
946
lw_dist2d_tri_circstring(LWTRIANGLE *tri, LWCIRCSTRING *line, DISTPTS *dl)
947
0
{
948
0
  const POINT2D *pt = lw_curvering_getfirstpoint2d_cp((LWGEOM *)line);
949
0
  if (ptarray_contains_point(tri->points, pt) != LW_OUTSIDE && dl->mode == DIST_MIN)
950
0
  {
951
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
952
0
    return LW_TRUE;
953
0
  }
954
955
0
  return lw_dist2d_ptarray_ptarrayarc(tri->points, line->points, dl);
956
0
}
957
958
/**
959
Function handling polygon to polygon calculation
960
1 if we are looking for maxdistance, just check the outer rings.
961
2 check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings
962
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
963
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
964
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
965
and not in holes so we check which one is inside.
966
 */
967
int
968
lw_dist2d_poly_poly(LWPOLY *poly1, LWPOLY *poly2, DISTPTS *dl)
969
0
{
970
0
  const POINT2D *pt;
971
972
0
  LWDEBUG(2, "lw_dist2d_poly_poly called");
973
974
  /*1 if we are looking for maxdistance, just check the outer rings.*/
975
0
  if (dl->mode == DIST_MAX)
976
0
    return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl);
977
978
  /* 2  check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings
979
  here it would be possible to handle the information about which one is inside which one and only search for the
980
  smaller ones in the bigger ones holes.*/
981
0
  pt = getPoint2d_cp(poly1->rings[0], 0);
982
0
  if (ptarray_contains_point(poly2->rings[0], pt) == LW_OUTSIDE)
983
0
  {
984
0
    pt = getPoint2d_cp(poly2->rings[0], 0);
985
0
    if (ptarray_contains_point(poly1->rings[0], pt) == LW_OUTSIDE)
986
0
      return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl);
987
0
  }
988
989
  /*3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole
990
   * of poly1*/
991
0
  pt = getPoint2d_cp(poly2->rings[0], 0);
992
0
  for (uint32_t i = 1; i < poly1->nrings; i++)
993
0
    if (ptarray_contains_point(poly1->rings[i], pt) != LW_OUTSIDE)
994
0
      return lw_dist2d_ptarray_ptarray(poly1->rings[i], poly2->rings[0], dl);
995
996
  /*4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole
997
   * of poly2*/
998
0
  pt = getPoint2d_cp(poly1->rings[0], 0);
999
0
  for (uint32_t i = 1; i < poly2->nrings; i++)
1000
0
    if (ptarray_contains_point(poly2->rings[i], pt) != LW_OUTSIDE)
1001
0
      return lw_dist2d_ptarray_ptarray(poly1->rings[0], poly2->rings[i], dl);
1002
1003
  /*5 If we have come all the way here we know that the first point of one of them is inside the other ones
1004
   * outer ring and not in holes so we check which one is inside.*/
1005
0
  pt = getPoint2d_cp(poly1->rings[0], 0);
1006
0
  if (ptarray_contains_point(poly2->rings[0], pt) != LW_OUTSIDE)
1007
0
  {
1008
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
1009
0
    return LW_TRUE;
1010
0
  }
1011
1012
0
  pt = getPoint2d_cp(poly2->rings[0], 0);
1013
0
  if (ptarray_contains_point(poly1->rings[0], pt) != LW_OUTSIDE)
1014
0
  {
1015
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
1016
0
    return LW_TRUE;
1017
0
  }
1018
1019
0
  lwerror("Unspecified error in function lw_dist2d_poly_poly");
1020
0
  return LW_FALSE;
1021
0
}
1022
1023
int
1024
lw_dist2d_poly_curvepoly(LWPOLY *poly1, LWCURVEPOLY *curvepoly2, DISTPTS *dl)
1025
0
{
1026
0
  LWCURVEPOLY *curvepoly1 = lwcurvepoly_construct_from_lwpoly(poly1);
1027
0
  int rv = lw_dist2d_curvepoly_curvepoly(curvepoly1, curvepoly2, dl);
1028
0
  lwgeom_free((LWGEOM *)curvepoly1);
1029
0
  return rv;
1030
0
}
1031
1032
int
1033
lw_dist2d_circstring_poly(LWCIRCSTRING *circ, LWPOLY *poly, DISTPTS *dl)
1034
0
{
1035
0
  LWCURVEPOLY *curvepoly = lwcurvepoly_construct_from_lwpoly(poly);
1036
0
  int rv = lw_dist2d_line_curvepoly((LWLINE *)circ, curvepoly, dl);
1037
0
  lwgeom_free((LWGEOM *)curvepoly);
1038
0
  return rv;
1039
0
}
1040
1041
int
1042
lw_dist2d_circstring_curvepoly(LWCIRCSTRING *circ, LWCURVEPOLY *poly, DISTPTS *dl)
1043
0
{
1044
0
  return lw_dist2d_line_curvepoly((LWLINE *)circ, poly, dl);
1045
0
}
1046
1047
int
1048
lw_dist2d_circstring_circstring(LWCIRCSTRING *line1, LWCIRCSTRING *line2, DISTPTS *dl)
1049
0
{
1050
0
  return lw_dist2d_ptarrayarc_ptarrayarc(line1->points, line2->points, dl);
1051
0
}
1052
1053
int
1054
lw_dist2d_curvepoly_curvepoly(LWCURVEPOLY *poly1, LWCURVEPOLY *poly2, DISTPTS *dl)
1055
0
{
1056
0
  const POINT2D *pt;
1057
1058
  /*1 if we are looking for maxdistance, just check the outer rings.*/
1059
0
  if (dl->mode == DIST_MAX)
1060
0
    return lw_dist2d_recursive(poly1->rings[0], poly2->rings[0], dl);
1061
1062
  /* 2  check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings
1063
  here it would be possible to handle the information about which one is inside which one and only search for the
1064
  smaller ones in the bigger ones holes.*/
1065
0
  pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0]);
1066
0
  if (lwgeom_contains_point(poly2->rings[0], pt) == LW_OUTSIDE)
1067
0
  {
1068
0
    pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0]);
1069
0
    if (lwgeom_contains_point(poly1->rings[0], pt) == LW_OUTSIDE)
1070
0
      return lw_dist2d_recursive(poly1->rings[0], poly2->rings[0], dl);
1071
0
  }
1072
1073
  /*3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole
1074
   * of poly1*/
1075
0
  pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0]);
1076
0
  for (uint32_t i = 1; i < poly1->nrings; i++)
1077
0
    if (lwgeom_contains_point(poly1->rings[i], pt) != LW_OUTSIDE)
1078
0
      return lw_dist2d_recursive(poly1->rings[i], poly2->rings[0], dl);
1079
1080
  /*4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole
1081
   * of poly2*/
1082
0
  pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0]);
1083
0
  for (uint32_t i = 1; i < poly2->nrings; i++)
1084
0
    if (lwgeom_contains_point(poly2->rings[i], pt) != LW_OUTSIDE)
1085
0
      return lw_dist2d_recursive(poly1->rings[0], poly2->rings[i], dl);
1086
1087
  /*5 If we have come all the way here we know that the first point of one of them is inside the other ones
1088
   * outer ring and not in holes so we check which one is inside.*/
1089
0
  pt = lw_curvering_getfirstpoint2d_cp(poly1->rings[0]);
1090
0
  if (lwgeom_contains_point(poly2->rings[0], pt) != LW_OUTSIDE)
1091
0
  {
1092
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
1093
0
    return LW_TRUE;
1094
0
  }
1095
1096
0
  pt = lw_curvering_getfirstpoint2d_cp(poly2->rings[0]);
1097
0
  if (lwgeom_contains_point(poly1->rings[0], pt) != LW_OUTSIDE)
1098
0
  {
1099
0
    lw_dist2d_distpts_set(dl, 0.0, pt, pt);
1100
0
    return LW_TRUE;
1101
0
  }
1102
1103
0
  lwerror("Unspecified error in function lw_dist2d_curvepoly_curvepoly");
1104
0
  return LW_FALSE;
1105
0
}
1106
1107
/**
1108
 * search all the segments of pointarray to see which one is closest to p1
1109
 * Returns minimum distance between point and pointarray
1110
 */
1111
int
1112
lw_dist2d_pt_ptarray(const POINT2D *p, POINTARRAY *pa, DISTPTS *dl)
1113
0
{
1114
0
  const POINT2D *start, *end;
1115
0
  int twist = dl->twisted;
1116
1117
0
  start = getPoint2d_cp(pa, 0);
1118
1119
0
  LWDEBUG(2, "lw_dist2d_pt_ptarray enter");
1120
1121
0
  if (!lw_dist2d_pt_pt(p, start, dl))
1122
0
    return LW_FALSE;
1123
1124
0
  LWDEBUGF(2, "lw_dist2d_pt_ptarray: distance from first point ? : %.15g", dl->distance);
1125
1126
0
  for (uint32_t t = 1; t < pa->npoints; t++)
1127
0
  {
1128
0
    dl->twisted = twist;
1129
0
    end = getPoint2d_cp(pa, t);
1130
0
    if (!lw_dist2d_pt_seg(p, start, end, dl))
1131
0
      return LW_FALSE;
1132
1133
0
    LWDEBUGF(2, "lw_dist2d_pt_ptarray: distance from seg %u ? : %.15g", t, dl->distance);
1134
1135
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1136
0
      return LW_TRUE; /*just a check if the answer is already given*/
1137
0
    start = end;
1138
0
  }
1139
1140
0
  return LW_TRUE;
1141
0
}
1142
1143
/**
1144
 * Search all the arcs of pointarray to see which one is closest to p1
1145
 * Returns minimum distance between point and arc pointarray.
1146
 */
1147
int
1148
lw_dist2d_pt_ptarrayarc(const POINT2D *p, const POINTARRAY *pa, DISTPTS *dl)
1149
0
{
1150
0
  uint32_t t;
1151
0
  const POINT2D *A1;
1152
0
  const POINT2D *A2;
1153
0
  const POINT2D *A3;
1154
0
  int twist = dl->twisted;
1155
1156
0
  LWDEBUG(2, "lw_dist2d_pt_ptarrayarc is called");
1157
1158
0
  if (pa->npoints % 2 == 0 || pa->npoints < 3)
1159
0
  {
1160
0
    lwerror("lw_dist2d_pt_ptarrayarc called with non-arc input");
1161
0
    return LW_FALSE;
1162
0
  }
1163
1164
0
  if (dl->mode == DIST_MAX)
1165
0
  {
1166
0
    lwerror("lw_dist2d_pt_ptarrayarc does not currently support DIST_MAX mode");
1167
0
    return LW_FALSE;
1168
0
  }
1169
1170
0
  A1 = getPoint2d_cp(pa, 0);
1171
1172
0
  if (!lw_dist2d_pt_pt(p, A1, dl))
1173
0
    return LW_FALSE;
1174
1175
0
  for (t = 1; t < pa->npoints; t += 2)
1176
0
  {
1177
0
    dl->twisted = twist;
1178
0
    A2 = getPoint2d_cp(pa, t);
1179
0
    A3 = getPoint2d_cp(pa, t + 1);
1180
1181
0
    if (lw_dist2d_pt_arc(p, A1, A2, A3, dl) == LW_FALSE)
1182
0
      return LW_FALSE;
1183
1184
0
    if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1185
0
      return LW_TRUE; /*just a check if the answer is already given*/
1186
1187
0
    A1 = A3;
1188
0
  }
1189
1190
0
  return LW_TRUE;
1191
0
}
1192
1193
/**
1194
 * test each segment of l1 against each segment of l2.
1195
 */
1196
int
1197
lw_dist2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS *dl)
1198
0
{
1199
0
  uint32_t t, u;
1200
0
  const POINT2D *start, *end;
1201
0
  const POINT2D *start2, *end2;
1202
0
  int twist = dl->twisted;
1203
1204
0
  LWDEBUGF(2, "lw_dist2d_ptarray_ptarray called (points: %d-%d)", l1->npoints, l2->npoints);
1205
1206
  /*  If we are searching for maxdistance we go straight to point-point calculation since the maxdistance have
1207
   * to be between two vertices*/
1208
0
  if (dl->mode == DIST_MAX)
1209
0
  {
1210
0
    for (t = 0; t < l1->npoints; t++) /*for each segment in L1 */
1211
0
    {
1212
0
      start = getPoint2d_cp(l1, t);
1213
0
      for (u = 0; u < l2->npoints; u++) /*for each segment in L2 */
1214
0
      {
1215
0
        start2 = getPoint2d_cp(l2, u);
1216
0
        lw_dist2d_pt_pt(start, start2, dl);
1217
0
      }
1218
0
    }
1219
0
  }
1220
0
  else
1221
0
  {
1222
0
    start = getPoint2d_cp(l1, 0);
1223
0
    for (t = 1; t < l1->npoints; t++) /*for each segment in L1 */
1224
0
    {
1225
0
      end = getPoint2d_cp(l1, t);
1226
0
      start2 = getPoint2d_cp(l2, 0);
1227
0
      for (u = 1; u < l2->npoints; u++) /*for each segment in L2 */
1228
0
      {
1229
0
        end2 = getPoint2d_cp(l2, u);
1230
0
        dl->twisted = twist;
1231
0
        lw_dist2d_seg_seg(start, end, start2, end2, dl);
1232
0
        if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1233
0
          return LW_TRUE; /*just a check if the answer is already given*/
1234
0
        start2 = end2;
1235
0
      }
1236
0
      start = end;
1237
0
    }
1238
0
  }
1239
0
  return LW_TRUE;
1240
0
}
1241
1242
/**
1243
 * Test each segment of pa against each arc of pb for distance.
1244
 */
1245
int
1246
lw_dist2d_ptarray_ptarrayarc(const POINTARRAY *pa, const POINTARRAY *pb, DISTPTS *dl)
1247
0
{
1248
0
  uint32_t t, u;
1249
0
  const POINT2D *A1;
1250
0
  const POINT2D *A2;
1251
0
  const POINT2D *B1;
1252
0
  const POINT2D *B2;
1253
0
  const POINT2D *B3;
1254
0
  int twist = dl->twisted;
1255
1256
0
  LWDEBUGF(2, "lw_dist2d_ptarray_ptarrayarc called (points: %d-%d)", pa->npoints, pb->npoints);
1257
1258
0
  if (pb->npoints % 2 == 0 || pb->npoints < 3)
1259
0
  {
1260
0
    lwerror("lw_dist2d_ptarray_ptarrayarc called with non-arc input");
1261
0
    return LW_FALSE;
1262
0
  }
1263
1264
0
  if (dl->mode == DIST_MAX)
1265
0
  {
1266
0
    lwerror("lw_dist2d_ptarray_ptarrayarc does not currently support DIST_MAX mode");
1267
0
    return LW_FALSE;
1268
0
  }
1269
0
  else
1270
0
  {
1271
0
    A1 = getPoint2d_cp(pa, 0);
1272
0
    for (t = 1; t < pa->npoints; t++) /* For each segment in pa */
1273
0
    {
1274
0
      A2 = getPoint2d_cp(pa, t);
1275
0
      B1 = getPoint2d_cp(pb, 0);
1276
0
      for (u = 1; u < pb->npoints; u += 2) /* For each arc in pb */
1277
0
      {
1278
0
        B2 = getPoint2d_cp(pb, u);
1279
0
        B3 = getPoint2d_cp(pb, u + 1);
1280
0
        dl->twisted = twist;
1281
1282
0
        lw_dist2d_seg_arc(A1, A2, B1, B2, B3, dl);
1283
1284
        /* If we've found a distance within tolerance, we're done */
1285
0
        if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1286
0
          return LW_TRUE;
1287
1288
0
        B1 = B3;
1289
0
      }
1290
0
      A1 = A2;
1291
0
    }
1292
0
  }
1293
0
  return LW_TRUE;
1294
0
}
1295
1296
/**
1297
 * Test each arc of pa against each arc of pb for distance.
1298
 */
1299
int
1300
lw_dist2d_ptarrayarc_ptarrayarc(const POINTARRAY *pa, const POINTARRAY *pb, DISTPTS *dl)
1301
0
{
1302
0
  uint32_t t, u;
1303
0
  const POINT2D *A1;
1304
0
  const POINT2D *A2;
1305
0
  const POINT2D *A3;
1306
0
  const POINT2D *B1;
1307
0
  const POINT2D *B2;
1308
0
  const POINT2D *B3;
1309
0
  int twist = dl->twisted;
1310
1311
0
  LWDEBUGF(2, "lw_dist2d_ptarrayarc_ptarrayarc called (points: %d-%d)", pa->npoints, pb->npoints);
1312
1313
0
  if (dl->mode == DIST_MAX)
1314
0
  {
1315
0
    lwerror("lw_dist2d_ptarrayarc_ptarrayarc does not currently support DIST_MAX mode");
1316
0
    return LW_FALSE;
1317
0
  }
1318
0
  else
1319
0
  {
1320
0
    A1 = getPoint2d_cp(pa, 0);
1321
0
    for (t = 1; t < pa->npoints; t += 2) /* For each segment in pa */
1322
0
    {
1323
0
      A2 = getPoint2d_cp(pa, t);
1324
0
      A3 = getPoint2d_cp(pa, t + 1);
1325
0
      B1 = getPoint2d_cp(pb, 0);
1326
0
      for (u = 1; u < pb->npoints; u += 2) /* For each arc in pb */
1327
0
      {
1328
0
        B2 = getPoint2d_cp(pb, u);
1329
0
        B3 = getPoint2d_cp(pb, u + 1);
1330
0
        dl->twisted = twist;
1331
1332
0
        lw_dist2d_arc_arc(A1, A2, A3, B1, B2, B3, dl);
1333
1334
        /* If we've found a distance within tolerance, we're done */
1335
0
        if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN)
1336
0
          return LW_TRUE;
1337
1338
0
        B1 = B3;
1339
0
      }
1340
0
      A1 = A3;
1341
0
    }
1342
0
  }
1343
0
  return LW_TRUE;
1344
0
}
1345
1346
/**
1347
 * Calculate the shortest distance between an arc and an edge.
1348
 * Line/circle approach from http://stackoverflow.com/questions/1073336/circle-line-collision-detection
1349
 */
1350
int
1351
lw_dist2d_seg_arc(const POINT2D *A1,
1352
      const POINT2D *A2,
1353
      const POINT2D *B1,
1354
      const POINT2D *B2,
1355
      const POINT2D *B3,
1356
      DISTPTS *dl)
1357
0
{
1358
0
  POINT2D C;       /* center of arc circle */
1359
0
  double radius_C; /* radius of arc circle */
1360
0
  POINT2D D;       /* point on A closest to C */
1361
0
  double dist_C_D; /* distance from C to D */
1362
0
  int pt_in_arc, pt_in_seg;
1363
0
  DISTPTS dltmp;
1364
1365
  /* Bail out on crazy modes */
1366
0
  if (dl->mode < 0)
1367
0
    lwerror("lw_dist2d_seg_arc does not support maxdistance mode");
1368
1369
  /* What if the "arc" is a point? */
1370
0
  if (lw_arc_is_pt(B1, B2, B3))
1371
0
    return lw_dist2d_pt_seg(B1, A1, A2, dl);
1372
1373
  /* Calculate center and radius of the circle. */
1374
0
  radius_C = lw_arc_center(B1, B2, B3, &C);
1375
1376
  /* This "arc" is actually a line (B2 is collinear with B1,B3) */
1377
0
  if (radius_C < 0.0)
1378
0
    return lw_dist2d_seg_seg(A1, A2, B1, B3, dl);
1379
1380
  /* Calculate distance between the line and circle center */
1381
0
  lw_dist2d_distpts_init(&dltmp, DIST_MIN);
1382
0
  if (lw_dist2d_pt_seg(&C, A1, A2, &dltmp) == LW_FALSE)
1383
0
    lwerror("lw_dist2d_pt_seg failed in lw_dist2d_seg_arc");
1384
1385
0
  D = dltmp.p1;
1386
0
  dist_C_D = dltmp.distance;
1387
1388
  /* Line intersects circle, maybe arc intersects edge? */
1389
  /* If so, that's the closest point. */
1390
  /* If not, the closest point is one of the end points of A */
1391
0
  if (dist_C_D < radius_C)
1392
0
  {
1393
0
    double length_A;  /* length of the segment A */
1394
0
    POINT2D E, F;     /* points of intersection of edge A and circle(B) */
1395
0
    double dist_D_EF; /* distance from D to E or F (same distance both ways) */
1396
1397
0
    dist_D_EF = sqrt(radius_C * radius_C - dist_C_D * dist_C_D);
1398
0
    length_A = sqrt((A2->x - A1->x) * (A2->x - A1->x) + (A2->y - A1->y) * (A2->y - A1->y));
1399
1400
    /* Point of intersection E */
1401
0
    E.x = D.x - (A2->x - A1->x) * dist_D_EF / length_A;
1402
0
    E.y = D.y - (A2->y - A1->y) * dist_D_EF / length_A;
1403
    /* Point of intersection F */
1404
0
    F.x = D.x + (A2->x - A1->x) * dist_D_EF / length_A;
1405
0
    F.y = D.y + (A2->y - A1->y) * dist_D_EF / length_A;
1406
1407
    /* If E is within A and within B then it's an intersection point */
1408
0
    pt_in_arc = lw_pt_in_arc(&E, B1, B2, B3);
1409
0
    pt_in_seg = lw_pt_in_seg(&E, A1, A2);
1410
1411
0
    if (pt_in_arc && pt_in_seg)
1412
0
    {
1413
0
      lw_dist2d_distpts_set(dl, 0.0, &E, &E);
1414
0
      return LW_TRUE;
1415
0
    }
1416
1417
    /* If F is within A and within B then it's an intersection point */
1418
0
    pt_in_arc = lw_pt_in_arc(&F, B1, B2, B3);
1419
0
    pt_in_seg = lw_pt_in_seg(&F, A1, A2);
1420
1421
0
    if (pt_in_arc && pt_in_seg)
1422
0
    {
1423
0
      lw_dist2d_distpts_set(dl, 0.0, &F, &F);
1424
0
      return LW_TRUE;
1425
0
    }
1426
0
  }
1427
1428
  /* Line grazes circle, maybe arc intersects edge? */
1429
  /* If so, grazing point is the closest point. */
1430
  /* If not, the closest point is one of the end points of A */
1431
0
  else if (dist_C_D == radius_C)
1432
0
  {
1433
    /* Closest point D is also the point of grazing */
1434
0
    pt_in_arc = lw_pt_in_arc(&D, B1, B2, B3);
1435
0
    pt_in_seg = lw_pt_in_seg(&D, A1, A2);
1436
1437
    /* Is D contained in both A and B? */
1438
0
    if (pt_in_arc && pt_in_seg)
1439
0
    {
1440
0
      lw_dist2d_distpts_set(dl, 0.0, &D, &D);
1441
0
      return LW_TRUE;
1442
0
    }
1443
0
  }
1444
  /* Line misses circle. */
1445
  /* If closest point to A on circle is within B, then that's the closest */
1446
  /* Otherwise, the closest point will be an end point of A */
1447
0
  else
1448
0
  {
1449
0
    POINT2D G; /* Point on circle closest to A */
1450
0
    G.x = C.x + (D.x - C.x) * radius_C / dist_C_D;
1451
0
    G.y = C.y + (D.y - C.y) * radius_C / dist_C_D;
1452
1453
0
    pt_in_arc = lw_pt_in_arc(&G, B1, B2, B3);
1454
0
    pt_in_seg = lw_pt_in_seg(&D, A1, A2);
1455
1456
    /* Closest point is on the interior of A and B */
1457
0
    if (pt_in_arc && pt_in_seg)
1458
0
      return lw_dist2d_pt_pt(&D, &G, dl);
1459
0
  }
1460
1461
  /* Now we test the many combinations of end points with either */
1462
  /* arcs or edges. Each previous check determined if the closest */
1463
  /* potential point was within the arc/segment inscribed on the */
1464
  /* line/circle holding the arc/segment. */
1465
1466
  /* Closest point is in the arc, but not in the segment, so */
1467
  /* one of the segment end points must be the closest. */
1468
0
  if (pt_in_arc && !pt_in_seg)
1469
0
  {
1470
0
    lw_dist2d_pt_arc(A1, B1, B2, B3, dl);
1471
0
    lw_dist2d_pt_arc(A2, B1, B2, B3, dl);
1472
0
    return LW_TRUE;
1473
0
  }
1474
  /* or, one of the arc end points is the closest */
1475
0
  else if (pt_in_seg && !pt_in_arc)
1476
0
  {
1477
0
    lw_dist2d_pt_seg(B1, A1, A2, dl);
1478
0
    lw_dist2d_pt_seg(B3, A1, A2, dl);
1479
0
    return LW_TRUE;
1480
0
  }
1481
  /* Finally, one of the end-point to end-point combos is the closest. */
1482
0
  else
1483
0
  {
1484
0
    lw_dist2d_pt_pt(A1, B1, dl);
1485
0
    lw_dist2d_pt_pt(A1, B3, dl);
1486
0
    lw_dist2d_pt_pt(A2, B1, dl);
1487
0
    lw_dist2d_pt_pt(A2, B3, dl);
1488
0
    return LW_TRUE;
1489
0
  }
1490
1491
0
  return LW_FALSE;
1492
0
}
1493
1494
int
1495
lw_dist2d_pt_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, DISTPTS *dl)
1496
0
{
1497
0
  double radius_A, d;
1498
0
  POINT2D C; /* center of circle defined by arc A */
1499
0
  POINT2D X; /* point circle(A) where line from C to P crosses */
1500
1501
0
  if (dl->mode < 0)
1502
0
    lwerror("lw_dist2d_pt_arc does not support maxdistance mode");
1503
1504
  /* What if the arc is a point? */
1505
0
  if (lw_arc_is_pt(A1, A2, A3))
1506
0
    return lw_dist2d_pt_pt(P, A1, dl);
1507
1508
  /* Calculate centers and radii of circles. */
1509
0
  radius_A = lw_arc_center(A1, A2, A3, &C);
1510
1511
  /* This "arc" is actually a line (A2 is colinear with A1,A3) */
1512
0
  if (radius_A < 0.0)
1513
0
    return lw_dist2d_pt_seg(P, A1, A3, dl);
1514
1515
  /* Distance from point to center */
1516
0
  d = distance2d_pt_pt(&C, P);
1517
1518
  /* P is the center of the circle */
1519
0
  if (FP_EQUALS(d, 0.0))
1520
0
  {
1521
0
    lw_dist2d_distpts_set(dl, radius_A, A1, P);
1522
0
    return LW_TRUE;
1523
0
  }
1524
1525
  /* X is the point on the circle where the line from P to C crosses */
1526
0
  X.x = C.x + (P->x - C.x) * radius_A / d;
1527
0
  X.y = C.y + (P->y - C.y) * radius_A / d;
1528
1529
  /* Is crossing point inside the arc? Or arc is actually circle? */
1530
0
  if (p2d_same(A1, A3) || lw_pt_in_arc(&X, A1, A2, A3))
1531
0
  {
1532
0
    lw_dist2d_pt_pt(P, &X, dl);
1533
0
  }
1534
0
  else
1535
0
  {
1536
    /* Distance is the minimum of the distances to the arc end points */
1537
0
    lw_dist2d_pt_pt(A1, P, dl);
1538
0
    lw_dist2d_pt_pt(A3, P, dl);
1539
0
  }
1540
0
  return LW_TRUE;
1541
0
}
1542
1543
1544
1545
/**
1546
 * @brief Calculates the intersection points of a circle and line.
1547
 *
1548
 * This function assumes the center and test point are distinct.
1549
 * Finds the line between the circle center and test point, and
1550
 * the two intersection points on the circle defined by that line.
1551
 * If those points fall within the provided arc (A1,A2,A3) then
1552
 * the points are added to the provided array (I) and the array
1553
 * length counter is incremented.
1554
 *
1555
 * @param A1   Point of arc
1556
 * @param A2   Point of arc
1557
 * @param A3   Point of arc
1558
 * @param center_A   Center of arc A circle
1559
 * @param radius_A   Radius of arc A circle
1560
 * @param P    Point to use in calculating intersection line
1561
 * @param I    [out] Pointer to an return value array
1562
 * @param ni   [out] Pointer to return array size counter
1563
 * @return int
1564
 */
1565
static int
1566
lw_dist2d_circle_intersections(
1567
  const POINT2D *A1, const POINT2D *A2, const POINT2D *A3,
1568
  const POINT2D *center_A,
1569
  double radius_A,
1570
  const POINT2D *P,
1571
  POINT2D *I,
1572
  uint32_t *ni)
1573
0
{
1574
0
  POINT2D R;
1575
1576
  // If the test point is on the center of the other
1577
  // arc, some other point has to be closer, by definition.
1578
0
  if (p2d_same(center_A, P))
1579
0
    return 0;
1580
1581
  // Calculate vector from the center to the pt
1582
0
  double dir_x = center_A->x - P->x;
1583
0
  double dir_y = center_A->y - P->y;
1584
0
  double dist = sqrt(dir_x * dir_x + dir_y * dir_y);
1585
1586
  // Normalize the direction vector to get a unit vector (length = 1)
1587
0
  double unit_x = dir_x / dist;
1588
0
  double unit_y = dir_y / dist;
1589
1590
  // Calculate the two intersection points on the circle
1591
  // Point 1: Move from center_A along the unit vector by distance radius_A
1592
0
  R.x = center_A->x + unit_x * radius_A;
1593
0
  R.y = center_A->y + unit_y * radius_A;
1594
0
  if (lw_pt_in_arc(&R, A1, A2, A3))
1595
0
    I[(*ni)++] = R;
1596
1597
  // Point 2: Move from center_A against the unit vector by distance radius_A
1598
0
  R.x = center_A->x - unit_x * radius_A;
1599
0
  R.y = center_A->y - unit_y * radius_A;
1600
0
  if (lw_pt_in_arc(&R, A1, A2, A3))
1601
0
    I[(*ni)++] = R;
1602
1603
0
  return 0;
1604
0
}
1605
1606
1607
/**
1608
 * @brief Calculates the intersection points of two overlapping circles.
1609
 *
1610
 * This function assumes the circles are known to intersect at one or two points.
1611
 * Specifically, the distance 'd' between their centers must satisfy:
1612
 * d < (rA + rB)  and  d > fabs(rA - rB)
1613
 * If these conditions are not met, the results are undefined.
1614
 *
1615
 * @param cA   [in] Pointer to the center point of the first circle (A).
1616
 * @param rA   [in] The radius of the first circle (A).
1617
 * @param cB   [in] Pointer to the center point of the second circle (B).
1618
 * @param rB   [in] The radius of the second circle (B).
1619
 * @param I    [out] Pointer to an array of at least 2 POINT2D structs to store the results.
1620
 * I[0] will contain the first intersection point.
1621
 * If a second exists, it will be in I[1].
1622
 * @return int The number of intersection points found (1 or 2). Returns 0 if
1623
 * the centers are coincident or another error occurs.
1624
 */
1625
static uint32_t
1626
lw_dist2d_circle_circle_intersections(
1627
  const POINT2D *cA, double rA,
1628
  const POINT2D *cB, double rB,
1629
  POINT2D *I)
1630
0
{
1631
  // Vector from center A to center B
1632
0
  double dx = cB->x - cA->x;
1633
0
  double dy = cB->y - cA->y;
1634
1635
  // Distance between the centers
1636
0
  double d = sqrt(dx * dx + dy * dy);
1637
1638
  // 'a' is the distance from the center of circle A to the point P,
1639
  // which is the base of the right triangle formed by cA, P, and an intersection point.
1640
0
  double a = (rA * rA - rB * rB + d * d) / (2.0 * d);
1641
1642
  // 'h' is the height of that right triangle.
1643
0
  double h_squared = rA * rA - a * a;
1644
1645
  // Due to floating point errors, h_squared can be slightly negative.
1646
  // This happens when the circles are perfectly tangent. Clamp to 0.
1647
0
  if (h_squared < 0.0)
1648
0
    h_squared = 0.0;
1649
1650
0
  double h = sqrt(h_squared);
1651
1652
  // Find the coordinates of point P
1653
0
  double Px = cA->x + a * (dx / d);
1654
0
  double Py = cA->y + a * (dy / d);
1655
1656
  // The two intersection points are found by moving from P by a distance 'h'
1657
  // in directions perpendicular to the line connecting the centers.
1658
  // The perpendicular vector to (dx, dy) is (-dy, dx).
1659
1660
  // Intersection point 1
1661
0
  I[0].x = Px - h * (dy / d);
1662
0
  I[0].y = Py + h * (dx / d);
1663
1664
  // If h is very close to 0, the circles are tangent and there's only one intersection point.
1665
0
  if (FP_IS_ZERO(h))
1666
0
    return 1;
1667
1668
  // Intersection point 2
1669
0
  I[1].x = Px + h * (dy / d);
1670
0
  I[1].y = Py - h * (dx / d);
1671
1672
0
  return 2;
1673
0
}
1674
1675
1676
int
1677
lw_dist2d_arc_arc(
1678
  const POINT2D *A1, const POINT2D *A2, const POINT2D *A3,
1679
  const POINT2D *B1, const POINT2D *B2, const POINT2D *B3,
1680
  DISTPTS *dl)
1681
0
{
1682
0
  POINT2D pts_A[16];            /* Points on A that might be the nearest */
1683
0
  POINT2D pts_B[16];            /* Points on B that might be the nearest */
1684
0
  uint32_t ai = 0, bi = 0;      /* Number of points in pts_A and pts_B */
1685
0
  POINT2D center_A, center_B;   /* Center points of arcs A and B */
1686
0
  double radius_A, radius_B, d; /* Radii of arcs A and B */
1687
0
  int is_disjoint, is_overlapping, is_contained, is_same_center;
1688
0
  POINT2D intersectionPts[2];
1689
1690
0
  if (dl->mode != DIST_MIN)
1691
0
    lwerror("lw_dist2d_arc_arc only supports mindistance");
1692
1693
  /* What if one or both of our "arcs" is actually a point? */
1694
0
  if (lw_arc_is_pt(B1, B2, B3) && lw_arc_is_pt(A1, A2, A3))
1695
0
    return lw_dist2d_pt_pt(B1, A1, dl);
1696
0
  else if (lw_arc_is_pt(B1, B2, B3))
1697
0
    return lw_dist2d_pt_arc(B1, A1, A2, A3, dl);
1698
0
  else if (lw_arc_is_pt(A1, A2, A3))
1699
0
    return lw_dist2d_pt_arc(A1, B1, B2, B3, dl);
1700
1701
  /* Calculate centers and radii of circles. */
1702
0
  radius_A = lw_arc_center(A1, A2, A3, &center_A);
1703
0
  radius_B = lw_arc_center(B1, B2, B3, &center_B);
1704
1705
  /* Two co-linear arcs?!? That's two segments. */
1706
0
  if (radius_A < 0 && radius_B < 0)
1707
0
    return lw_dist2d_seg_seg(A1, A3, B1, B3, dl);
1708
1709
  /* A is co-linear, delegate to lw_dist_seg_arc here. */
1710
0
  if (radius_A < 0)
1711
0
    return lw_dist2d_seg_arc(A1, A3, B1, B2, B3, dl);
1712
1713
  /* B is co-linear, delegate to lw_dist_seg_arc here. */
1714
0
  if (radius_B < 0)
1715
0
    return lw_dist2d_seg_arc(B1, B3, A1, A2, A3, dl);
1716
1717
  /* Circle relationships */
1718
0
  d = distance2d_pt_pt(&center_A, &center_B);
1719
0
  is_disjoint = (d > (radius_A + radius_B));
1720
0
  is_contained = (d < fabs(radius_A - radius_B));
1721
0
  is_same_center = p2d_same(&center_A, &center_B);
1722
0
  is_overlapping = ! (is_disjoint || is_contained || is_same_center);
1723
1724
  /*
1725
   * Prime the array of potential closest points with the
1726
   * arc end points, which frequently participate in closest
1727
   * points.
1728
   */
1729
0
  pts_A[ai++] = *A1;
1730
0
  pts_A[ai++] = *A3;
1731
0
  pts_B[bi++] = *B1;
1732
0
  pts_B[bi++] = *B3;
1733
1734
  /*
1735
   * Overlapping circles might have a zero distance
1736
   * case if the circle intersection points are inside both
1737
   * arcs.
1738
   */
1739
0
  if (is_overlapping)
1740
0
  {
1741
    /*
1742
     * Find the two points the circles intersect at.
1743
     */
1744
0
    uint32_t npoints = lw_dist2d_circle_circle_intersections(
1745
0
      &center_A, radius_A,
1746
0
      &center_B, radius_B,
1747
0
      intersectionPts);
1748
0
    for (uint32_t i = 0; i < npoints; i++)
1749
0
    {
1750
      /*
1751
       * If an intersection point is contained in both
1752
       * arcs, that is a location of zero distance, so
1753
       * we are done calculating.
1754
       */
1755
0
      if (lw_pt_in_arc(&intersectionPts[i], A1, A2, A3) &&
1756
0
        lw_pt_in_arc(&intersectionPts[i], B1, B2, B3))
1757
0
      {
1758
0
        lw_dist2d_distpts_set(dl, 0.0, &intersectionPts[i], &intersectionPts[i]);
1759
0
        return LW_TRUE;
1760
0
      }
1761
0
    }
1762
0
  }
1763
1764
  /*
1765
   * Join the circle centers and find the places that
1766
   * line intersects the circles. Where those places
1767
   * are in the arcs, they are potential sites of the
1768
   * closest points.
1769
   */
1770
0
  if (is_disjoint || is_contained || is_overlapping)
1771
0
  {
1772
0
    if (!is_same_center)
1773
0
    {
1774
      /* Add points on A that intersect line from center_A to center_B */
1775
0
      lw_dist2d_circle_intersections(
1776
0
          A1, A2, A3,
1777
0
          &center_A, radius_A, &center_B,
1778
0
          pts_A, &ai);
1779
1780
      /* Add points on B that intersect line from center_B to center_A */
1781
0
      lw_dist2d_circle_intersections(
1782
0
          B1, B2, B3,
1783
0
          &center_B, radius_B, &center_A,
1784
0
          pts_B, &bi);
1785
0
    }
1786
1787
    /* Add points on A that intersect line to B1 */
1788
0
    lw_dist2d_circle_intersections(
1789
0
        A1, A2, A3,
1790
0
        &center_A, radius_A, B1,
1791
0
        pts_A, &ai);
1792
1793
    /* Add points on A that intersect line to B3 */
1794
0
    lw_dist2d_circle_intersections(
1795
0
        A1, A2, A3,
1796
0
        &center_A, radius_A, B3,
1797
0
        pts_A, &ai);
1798
1799
    /* Add points on B that intersect line to A1 */
1800
0
    lw_dist2d_circle_intersections(
1801
0
        B1, B2, B3,
1802
0
        &center_B, radius_B, A1,
1803
0
        pts_B, &bi);
1804
1805
    /* Add points on B that intersect line to A3 */
1806
0
    lw_dist2d_circle_intersections(
1807
0
        B1, B2, B3,
1808
0
        &center_B, radius_B, A3,
1809
0
        pts_B, &bi);
1810
0
  }
1811
1812
  /*
1813
   * Now just brute force check all pairs of participating
1814
   * points, to find the pair that is closest together.
1815
   */
1816
0
  for (uint32_t i = 0; i < ai; i++)
1817
0
    for (uint32_t j = 0; j < bi; j++)
1818
0
      lw_dist2d_pt_pt(&pts_A[i], &pts_B[j], dl);
1819
1820
0
  return LW_TRUE;
1821
0
}
1822
1823
1824
/**
1825
Finds the shortest distance between two segments.
1826
This function is changed so it is not doing any comparison of distance
1827
but just sending every possible combination further to lw_dist2d_pt_seg
1828
*/
1829
int
1830
lw_dist2d_seg_seg(const POINT2D *A, const POINT2D *B, const POINT2D *C, const POINT2D *D, DISTPTS *dl)
1831
0
{
1832
0
  double s_top, s_bot, s;
1833
0
  double r_top, r_bot, r;
1834
1835
  /*A and B are the same point */
1836
0
  if ((A->x == B->x) && (A->y == B->y))
1837
0
  {
1838
0
    return lw_dist2d_pt_seg(A, C, D, dl);
1839
0
  }
1840
1841
  /*U and V are the same point */
1842
0
  if ((C->x == D->x) && (C->y == D->y))
1843
0
  {
1844
0
    dl->twisted = ((dl->twisted) * (-1));
1845
0
    return lw_dist2d_pt_seg(D, A, B, dl);
1846
0
  }
1847
1848
  /* AB and CD are line segments */
1849
  /* from comp.graphics.algo
1850
1851
  Solving the above for r and s yields
1852
        (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
1853
       r = ----------------------------- (eqn 1)
1854
        (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
1855
1856
      (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
1857
    s = ----------------------------- (eqn 2)
1858
      (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
1859
  Let P be the position vector of the intersection point, then
1860
    P=A+r(B-A) or
1861
    Px=Ax+r(Bx-Ax)
1862
    Py=Ay+r(By-Ay)
1863
  By examining the values of r & s, you can also determine some other limiting conditions:
1864
    If 0<=r<=1 & 0<=s<=1, intersection exists
1865
    r<0 or r>1 or s<0 or s>1 line segments do not intersect
1866
    If the denominator in eqn 1 is zero, AB & CD are parallel
1867
    If the numerator in eqn 1 is also zero, AB & CD are collinear.
1868
1869
  */
1870
0
  r_top = (A->y - C->y) * (D->x - C->x) - (A->x - C->x) * (D->y - C->y);
1871
0
  r_bot = (B->x - A->x) * (D->y - C->y) - (B->y - A->y) * (D->x - C->x);
1872
1873
0
  s_top = (A->y - C->y) * (B->x - A->x) - (A->x - C->x) * (B->y - A->y);
1874
0
  s_bot = (B->x - A->x) * (D->y - C->y) - (B->y - A->y) * (D->x - C->x);
1875
1876
0
  if ((r_bot == 0) || (s_bot == 0))
1877
0
  {
1878
0
    if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl)))
1879
0
    {
1880
      /* change the order of inputted geometries and that we notice by changing sign on dl->twisted*/
1881
0
      dl->twisted *= -1;
1882
0
      return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl)));
1883
0
    }
1884
0
    else
1885
0
      return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/
1886
0
  }
1887
1888
0
  s = s_top / s_bot;
1889
0
  r = r_top / r_bot;
1890
1891
0
  if (((r < 0) || (r > 1) || (s < 0) || (s > 1)) || (dl->mode == DIST_MAX))
1892
0
  {
1893
0
    if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl)))
1894
0
    {
1895
      /* change the order of inputted geometries and that we notice by changing sign on dl->twisted*/
1896
0
      dl->twisted *= -1;
1897
0
      return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl)));
1898
0
    }
1899
0
    else
1900
0
      return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/
1901
0
  }
1902
0
  else
1903
0
  {
1904
    /* If there is intersection we identify the intersection point and return it but only if we are looking
1905
     * for mindistance */
1906
0
    if (dl->mode == DIST_MIN)
1907
0
    {
1908
0
      POINT2D theP;
1909
1910
0
      if (((A->x == C->x) && (A->y == C->y)) || ((A->x == D->x) && (A->y == D->y)))
1911
0
      {
1912
0
        theP.x = A->x;
1913
0
        theP.y = A->y;
1914
0
      }
1915
0
      else if (((B->x == C->x) && (B->y == C->y)) || ((B->x == D->x) && (B->y == D->y)))
1916
0
      {
1917
0
        theP.x = B->x;
1918
0
        theP.y = B->y;
1919
0
      }
1920
0
      else
1921
0
      {
1922
0
        theP.x = A->x + r * (B->x - A->x);
1923
0
        theP.y = A->y + r * (B->y - A->y);
1924
0
      }
1925
0
      lw_dist2d_distpts_set(dl, 0, &theP, &theP);
1926
0
    }
1927
0
    return LW_TRUE;
1928
0
  }
1929
0
}
1930
1931
/*------------------------------------------------------------------------------------------------------------
1932
End of Brute force functions
1933
--------------------------------------------------------------------------------------------------------------*/
1934
1935
/*------------------------------------------------------------------------------------------------------------
1936
New faster distance calculations
1937
--------------------------------------------------------------------------------------------------------------*/
1938
1939
/**
1940
1941
The new faster calculation comparing pointarray to another pointarray
1942
the arrays can come from both polygons and linestrings.
1943
The naming is not good but comes from that it compares a
1944
chosen selection of the points not all of them
1945
*/
1946
int
1947
lw_dist2d_fast_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2, DISTPTS *dl, GBOX *box1, GBOX *box2)
1948
0
{
1949
  /*here we define two lists to hold our calculated "z"-values and the order number in the geometry*/
1950
1951
0
  double k, thevalue;
1952
0
  float deltaX, deltaY, c1m, c2m;
1953
0
  POINT2D c1, c2;
1954
0
  const POINT2D *theP;
1955
0
  float min1X, max1X, max1Y, min1Y, min2X, max2X, max2Y, min2Y;
1956
0
  int t;
1957
0
  int n1 = l1->npoints;
1958
0
  int n2 = l2->npoints;
1959
1960
0
  LISTSTRUCT *list1, *list2;
1961
0
  list1 = (LISTSTRUCT *)lwalloc(sizeof(LISTSTRUCT) * n1);
1962
0
  list2 = (LISTSTRUCT *)lwalloc(sizeof(LISTSTRUCT) * n2);
1963
1964
0
  LWDEBUG(2, "lw_dist2d_fast_ptarray_ptarray is called");
1965
1966
0
  max1X = box1->xmax;
1967
0
  min1X = box1->xmin;
1968
0
  max1Y = box1->ymax;
1969
0
  min1Y = box1->ymin;
1970
0
  max2X = box2->xmax;
1971
0
  min2X = box2->xmin;
1972
0
  max2Y = box2->ymax;
1973
0
  min2Y = box2->ymin;
1974
  /*we want the center of the bboxes, and calculate the slope between the centerpoints*/
1975
0
  c1.x = min1X + (max1X - min1X) / 2;
1976
0
  c1.y = min1Y + (max1Y - min1Y) / 2;
1977
0
  c2.x = min2X + (max2X - min2X) / 2;
1978
0
  c2.y = min2Y + (max2Y - min2Y) / 2;
1979
1980
0
  deltaX = (c2.x - c1.x);
1981
0
  deltaY = (c2.y - c1.y);
1982
1983
  /*Here we calculate where the line perpendicular to the center-center line crosses the axes for each vertex
1984
  if the center-center line is vertical the perpendicular line will be horizontal and we find it's crossing the
1985
  Y-axes with z = y-kx */
1986
0
  if ((deltaX * deltaX) < (deltaY * deltaY)) /*North or South*/
1987
0
  {
1988
0
    k = -deltaX / deltaY;
1989
0
    for (t = 0; t < n1; t++) /*for each segment in L1 */
1990
0
    {
1991
0
      theP = getPoint2d_cp(l1, t);
1992
0
      thevalue = theP->y - (k * theP->x);
1993
0
      list1[t].themeasure = thevalue;
1994
0
      list1[t].pnr = t;
1995
0
    }
1996
0
    for (t = 0; t < n2; t++) /*for each segment in L2*/
1997
0
    {
1998
0
      theP = getPoint2d_cp(l2, t);
1999
0
      thevalue = theP->y - (k * theP->x);
2000
0
      list2[t].themeasure = thevalue;
2001
0
      list2[t].pnr = t;
2002
0
    }
2003
0
    c1m = c1.y - (k * c1.x);
2004
0
    c2m = c2.y - (k * c2.x);
2005
0
  }
2006
2007
  /*if the center-center line is horizontal the perpendicular line will be vertical. To eliminate problems with
2008
   dividing by zero we are here mirroring the coordinate-system and we find it's crossing the X-axes with z =
2009
   x-(1/k)y */
2010
0
  else /*West or East*/
2011
0
  {
2012
0
    k = -deltaY / deltaX;
2013
0
    for (t = 0; t < n1; t++) /*for each segment in L1 */
2014
0
    {
2015
0
      theP = getPoint2d_cp(l1, t);
2016
0
      thevalue = theP->x - (k * theP->y);
2017
0
      list1[t].themeasure = thevalue;
2018
0
      list1[t].pnr = t;
2019
      /* lwnotice("l1 %d, measure=%f",t,thevalue ); */
2020
0
    }
2021
0
    for (t = 0; t < n2; t++) /*for each segment in L2*/
2022
0
    {
2023
0
      theP = getPoint2d_cp(l2, t);
2024
0
      thevalue = theP->x - (k * theP->y);
2025
0
      list2[t].themeasure = thevalue;
2026
0
      list2[t].pnr = t;
2027
      /* lwnotice("l2 %d, measure=%f",t,thevalue ); */
2028
0
    }
2029
0
    c1m = c1.x - (k * c1.y);
2030
0
    c2m = c2.x - (k * c2.y);
2031
0
  }
2032
2033
  /*we sort our lists by the calculated values*/
2034
0
  qsort(list1, n1, sizeof(LISTSTRUCT), struct_cmp_by_measure);
2035
0
  qsort(list2, n2, sizeof(LISTSTRUCT), struct_cmp_by_measure);
2036
2037
0
  if (c1m < c2m)
2038
0
  {
2039
0
    if (!lw_dist2d_pre_seg_seg(l1, l2, list1, list2, k, dl))
2040
0
    {
2041
0
      lwfree(list1);
2042
0
      lwfree(list2);
2043
0
      return LW_FALSE;
2044
0
    }
2045
0
  }
2046
0
  else
2047
0
  {
2048
0
    dl->twisted = ((dl->twisted) * (-1));
2049
0
    if (!lw_dist2d_pre_seg_seg(l2, l1, list2, list1, k, dl))
2050
0
    {
2051
0
      lwfree(list1);
2052
0
      lwfree(list2);
2053
0
      return LW_FALSE;
2054
0
    }
2055
0
  }
2056
0
  lwfree(list1);
2057
0
  lwfree(list2);
2058
0
  return LW_TRUE;
2059
0
}
2060
2061
int
2062
struct_cmp_by_measure(const void *a, const void *b)
2063
0
{
2064
0
  LISTSTRUCT *ia = (LISTSTRUCT *)a;
2065
0
  LISTSTRUCT *ib = (LISTSTRUCT *)b;
2066
0
  return (ia->themeasure > ib->themeasure) ? 1 : ((ia->themeasure < ib->themeasure) ? -1 : 0);
2067
0
}
2068
2069
/** preparation before lw_dist2d_seg_seg. */
2070
int
2071
lw_dist2d_pre_seg_seg(POINTARRAY *l1, POINTARRAY *l2, LISTSTRUCT *list1, LISTSTRUCT *list2, double k, DISTPTS *dl)
2072
0
{
2073
0
  const POINT2D *p1, *p2, *p3, *p4, *p01, *p02;
2074
0
  int pnr1, pnr2, pnr3, pnr4, n1, n2, i, u, r, twist;
2075
0
  double maxmeasure;
2076
0
  n1 = l1->npoints;
2077
0
  n2 = l2->npoints;
2078
2079
0
  LWDEBUG(2, "lw_dist2d_pre_seg_seg is called");
2080
2081
0
  p1 = getPoint2d_cp(l1, list1[0].pnr);
2082
0
  p3 = getPoint2d_cp(l2, list2[0].pnr);
2083
0
  lw_dist2d_pt_pt(p1, p3, dl);
2084
0
  maxmeasure = sqrt(dl->distance * dl->distance + (dl->distance * dl->distance * k * k));
2085
0
  twist = dl->twisted; /*to keep the incoming order between iterations*/
2086
0
  for (i = (n1 - 1); i >= 0; --i)
2087
0
  {
2088
    /*we break this iteration when we have checked every point closer to our perpendicular "checkline" than
2089
     * our shortest found distance*/
2090
0
    if (((list2[0].themeasure - list1[i].themeasure)) > maxmeasure)
2091
0
      break;
2092
    /*because we are not iterating in the original point order we have to check the segment before and after
2093
     * every point*/
2094
0
    for (r = -1; r <= 1; r += 2)
2095
0
    {
2096
0
      pnr1 = list1[i].pnr;
2097
0
      p1 = getPoint2d_cp(l1, pnr1);
2098
0
      if (pnr1 + r < 0)
2099
0
      {
2100
0
        p01 = getPoint2d_cp(l1, (n1 - 1));
2101
0
        if ((p1->x == p01->x) && (p1->y == p01->y))
2102
0
          pnr2 = (n1 - 1);
2103
0
        else
2104
0
          pnr2 = pnr1; /* if it is a line and the last and first point is not the same we
2105
              avoid the edge between start and end this way*/
2106
0
      }
2107
2108
0
      else if (pnr1 + r > (n1 - 1))
2109
0
      {
2110
0
        p01 = getPoint2d_cp(l1, 0);
2111
0
        if ((p1->x == p01->x) && (p1->y == p01->y))
2112
0
          pnr2 = 0;
2113
0
        else
2114
0
          pnr2 = pnr1; /* if it is a line and the last and first point is not the same we
2115
              avoid the edge between start and end this way*/
2116
0
      }
2117
0
      else
2118
0
        pnr2 = pnr1 + r;
2119
2120
0
      p2 = getPoint2d_cp(l1, pnr2);
2121
0
      for (u = 0; u < n2; ++u)
2122
0
      {
2123
0
        if (((list2[u].themeasure - list1[i].themeasure)) >= maxmeasure)
2124
0
          break;
2125
0
        pnr3 = list2[u].pnr;
2126
0
        p3 = getPoint2d_cp(l2, pnr3);
2127
0
        if (pnr3 == 0)
2128
0
        {
2129
0
          p02 = getPoint2d_cp(l2, (n2 - 1));
2130
0
          if ((p3->x == p02->x) && (p3->y == p02->y))
2131
0
            pnr4 = (n2 - 1);
2132
0
          else
2133
0
            pnr4 = pnr3; /* if it is a line and the last and first point is not the
2134
                same we avoid the edge between start and end this way*/
2135
0
        }
2136
0
        else
2137
0
          pnr4 = pnr3 - 1;
2138
2139
0
        p4 = getPoint2d_cp(l2, pnr4);
2140
0
        dl->twisted = twist;
2141
0
        if (!lw_dist2d_selected_seg_seg(p1, p2, p3, p4, dl))
2142
0
          return LW_FALSE;
2143
2144
0
        if (pnr3 >= (n2 - 1))
2145
0
        {
2146
0
          p02 = getPoint2d_cp(l2, 0);
2147
0
          if ((p3->x == p02->x) && (p3->y == p02->y))
2148
0
            pnr4 = 0;
2149
0
          else
2150
0
            pnr4 = pnr3; /* if it is a line and the last and first point is not the
2151
                same we avoid the edge between start and end this way*/
2152
0
        }
2153
2154
0
        else
2155
0
          pnr4 = pnr3 + 1;
2156
2157
0
        p4 = getPoint2d_cp(l2, pnr4);
2158
0
        dl->twisted = twist; /*we reset the "twist" for each iteration*/
2159
0
        if (!lw_dist2d_selected_seg_seg(p1, p2, p3, p4, dl))
2160
0
          return LW_FALSE;
2161
        /*here we "translate" the found mindistance so it can be compared to our "z"-values*/
2162
0
        maxmeasure = sqrt(dl->distance * dl->distance + (dl->distance * dl->distance * k * k));
2163
0
      }
2164
0
    }
2165
0
  }
2166
2167
0
  return LW_TRUE;
2168
0
}
2169
2170
/**
2171
  This is the same function as lw_dist2d_seg_seg but
2172
  without any calculations to determine intersection since we
2173
  already know they do not intersect
2174
*/
2175
int
2176
lw_dist2d_selected_seg_seg(const POINT2D *A, const POINT2D *B, const POINT2D *C, const POINT2D *D, DISTPTS *dl)
2177
0
{
2178
  /*A and B are the same point */
2179
0
  if ((A->x == B->x) && (A->y == B->y))
2180
0
  {
2181
0
    return lw_dist2d_pt_seg(A, C, D, dl);
2182
0
  }
2183
  /*U and V are the same point */
2184
2185
0
  if ((C->x == D->x) && (C->y == D->y))
2186
0
  {
2187
0
    dl->twisted *= -1;
2188
0
    return lw_dist2d_pt_seg(D, A, B, dl);
2189
0
  }
2190
2191
0
  if ((lw_dist2d_pt_seg(A, C, D, dl)) && (lw_dist2d_pt_seg(B, C, D, dl)))
2192
0
  {
2193
    /* change the order of inputted geometries and that we notice by changing sign on dl->twisted */
2194
0
    dl->twisted *= -1;
2195
0
    return ((lw_dist2d_pt_seg(C, A, B, dl)) && (lw_dist2d_pt_seg(D, A, B, dl)));
2196
0
  }
2197
0
  else
2198
0
    return LW_FALSE; /* if any of the calls to lw_dist2d_pt_seg goes wrong we return false*/
2199
0
}
2200
2201
/*------------------------------------------------------------------------------------------------------------
2202
End of New faster distance calculations
2203
--------------------------------------------------------------------------------------------------------------*/
2204
2205
/*------------------------------------------------------------------------------------------------------------
2206
Functions in common for Brute force and new calculation
2207
--------------------------------------------------------------------------------------------------------------*/
2208
2209
/**
2210
lw_dist2d_comp from p to line A->B
2211
This one is now sending every occasion to lw_dist2d_pt_pt
2212
Before it was handling occasions where r was between 0 and 1 internally
2213
and just returning the distance without identifying the points.
2214
To get this points it was necessary to change and it also showed to be about 10%faster.
2215
*/
2216
int
2217
lw_dist2d_pt_seg(const POINT2D *p, const POINT2D *A, const POINT2D *B, DISTPTS *dl)
2218
0
{
2219
0
  double r;
2220
2221
0
  LWDEBUG(2, "lw_dist2d_pt_seg called");
2222
2223
  /*if start==end, then use pt distance */
2224
0
  if ((A->x == B->x) && (A->y == B->y))
2225
0
  {
2226
0
    LWDEBUG(2, "lw_dist2d_pt_seg found first and last segment points being the same");
2227
0
    return lw_dist2d_pt_pt(p, A, dl);
2228
0
  }
2229
2230
2231
  /*
2232
   * otherwise, we use comp.graphics.algorithms
2233
   * Frequently Asked Questions method
2234
   *
2235
   *  (1)        AC dot AB
2236
   *         r = ---------
2237
   *              ||AB||^2
2238
   *  r has the following meaning:
2239
   *  r=0 P = A
2240
   *  r=1 P = B
2241
   *  r<0 P is on the backward extension of AB
2242
   *  r>1 P is on the forward extension of AB
2243
   *  0<r<1 P is interior to AB
2244
   */
2245
2246
0
  r = ((p->x - A->x) * (B->x - A->x) + (p->y - A->y) * (B->y - A->y)) /
2247
0
      ((B->x - A->x) * (B->x - A->x) + (B->y - A->y) * (B->y - A->y));
2248
2249
0
  LWDEBUGF(2, "lw_dist2d_pt_seg found r = %.15g", r);
2250
2251
  /*This is for finding the maxdistance.
2252
  the maxdistance have to be between two vertices, compared to mindistance which can be between two vertices.*/
2253
0
  if (dl->mode == DIST_MAX)
2254
0
  {
2255
0
    if (r >= 0.5)
2256
0
      return lw_dist2d_pt_pt(p, A, dl);
2257
0
    else /* (r < 0.5) */
2258
0
      return lw_dist2d_pt_pt(p, B, dl);
2259
0
  }
2260
2261
0
  if (r < 0) /*If p projected on the line is outside point A*/
2262
0
    return lw_dist2d_pt_pt(p, A, dl);
2263
0
  if (r >= 1) /*If p projected on the line is outside point B or on point B*/
2264
0
    return lw_dist2d_pt_pt(p, B, dl);
2265
2266
  /*If the point p is on the segment this is a more robust way to find out that*/
2267
0
  if ((((A->y - p->y) * (B->x - A->x) == (A->x - p->x) * (B->y - A->y))) && (dl->mode == DIST_MIN))
2268
0
  {
2269
0
    lw_dist2d_distpts_set(dl, 0, p, p);
2270
0
  }
2271
2272
2273
  /*
2274
    (2)
2275
          (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
2276
      s = -----------------------------
2277
                    L^2
2278
2279
      Then the distance from C to P = |s|*L.
2280
  */
2281
2282
0
  double s = ((A->y - p->y) * (B->x - A->x) - (A->x - p->x) * (B->y - A->y)) /
2283
0
             ((B->x - A->x) * (B->x - A->x) + (B->y - A->y) * (B->y - A->y));
2284
2285
0
  double dist = fabs(s) * sqrt(((B->x - A->x) * (B->x - A->x) + (B->y - A->y) * (B->y - A->y)));
2286
0
  if ( dist < dl->distance )
2287
0
  {
2288
0
    dl->distance = dist;
2289
0
    {
2290
0
      POINT2D c;
2291
0
      c.x = A->x + r * (B->x - A->x);
2292
0
      c.y = A->y + r * (B->y - A->y);
2293
0
      if (dl->twisted > 0)
2294
0
      {
2295
0
        dl->p1 = *p;
2296
0
        dl->p2 = c;
2297
0
      }
2298
0
      else
2299
0
      {
2300
0
        dl->p1 = c;
2301
0
        dl->p2 = *p;
2302
0
      }
2303
0
    }
2304
0
  }
2305
2306
0
  return LW_TRUE;
2307
0
}
2308
2309
/** Compares incoming points and stores the points closest to each other or most far away from each other depending on
2310
 * dl->mode (max or min) */
2311
int
2312
lw_dist2d_pt_pt(const POINT2D *thep1, const POINT2D *thep2, DISTPTS *dl)
2313
0
{
2314
0
  double hside = thep2->x - thep1->x;
2315
0
  double vside = thep2->y - thep1->y;
2316
0
  double dist = sqrt(hside * hside + vside * vside);
2317
2318
  /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/
2319
0
  if (((dl->distance - dist) * (dl->mode)) > 0)
2320
0
  {
2321
0
    dl->distance = dist;
2322
2323
    /* To get the points in right order. twisted is updated between 1 and (-1) every time the order is
2324
     * changed earlier in the chain*/
2325
0
    if (dl->twisted > 0)
2326
0
    {
2327
0
      dl->p1 = *thep1;
2328
0
      dl->p2 = *thep2;
2329
0
    }
2330
0
    else
2331
0
    {
2332
0
      dl->p1 = *thep2;
2333
0
      dl->p2 = *thep1;
2334
0
    }
2335
0
  }
2336
0
  return LW_TRUE;
2337
0
}
2338
2339
/*------------------------------------------------------------------------------------------------------------
2340
End of Functions in common for Brute force and new calculation
2341
--------------------------------------------------------------------------------------------------------------*/
2342
2343
inline double
2344
distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
2345
0
{
2346
0
  double hside = p2->x - p1->x;
2347
0
  double vside = p2->y - p1->y;
2348
2349
0
  return hypot(hside, vside);
2350
0
}
2351
2352
/* return distance squared, useful to avoid sqrt calculations */
2353
double
2354
distance2d_sqr_pt_seg(const POINT2D *C, const POINT2D *A, const POINT2D *B)
2355
0
{
2356
  /*if start==end, then use pt distance */
2357
0
  if ((A->x == B->x) && (A->y == B->y))
2358
0
    return distance2d_sqr_pt_pt(C, A);
2359
2360
  /*
2361
   * otherwise, we use comp.graphics.algorithms
2362
   * Frequently Asked Questions method
2363
   *
2364
   *  (1)       AC dot AB
2365
   *         r = ---------
2366
   *              ||AB||^2
2367
   *  r has the following meaning:
2368
   *  r=0 P = A
2369
   *  r=1 P = B
2370
   *  r<0 P is on the backward extension of AB
2371
   *  r>1 P is on the forward extension of AB
2372
   *  0<r<1 P is interior to AB
2373
   */
2374
2375
0
  double ba_x = (B->x - A->x);
2376
0
  double ba_y = (B->y - A->y);
2377
0
  double ab_length_sqr = (ba_x * ba_x + ba_y * ba_y);
2378
0
  double ca_x = (C->x - A->x);
2379
0
  double ca_y = (C->y - A->y);
2380
0
  double dot_ac_ab = (ca_x * ba_x + ca_y * ba_y);
2381
2382
0
  if (dot_ac_ab <= 0)
2383
0
    return distance2d_sqr_pt_pt(C, A);
2384
0
  if (dot_ac_ab >= ab_length_sqr)
2385
0
    return distance2d_sqr_pt_pt(C, B);
2386
2387
  /*
2388
   * (2)
2389
   *       (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
2390
   *  s = -----------------------------
2391
   *                L^2
2392
   *
2393
   *  Then the distance from C to P = |s|*L.
2394
   *
2395
   */
2396
2397
0
  double s_numerator = ca_x * ba_y - ca_y * ba_x;
2398
2399
  /* Distance = (s_num / ab) * (s_num / ab) * ab == s_num * s_num / ab) */
2400
0
  return s_numerator * s_numerator / ab_length_sqr;
2401
0
}
2402
2403
/**
2404
 * Compute the azimuth of segment AB in radians.
2405
 * Return 0 on exception (same point), 1 otherwise.
2406
 */
2407
int
2408
azimuth_pt_pt(const POINT2D *A, const POINT2D *B, double *d)
2409
0
{
2410
0
  if (A->x == B->x && A->y == B->y)
2411
0
    return LW_FALSE;
2412
0
  *d = fmod(2 * M_PI + M_PI / 2 - atan2(B->y - A->y, B->x - A->x), 2 * M_PI);
2413
0
  return LW_TRUE;
2414
0
}
2415
2416
/**
2417
 * Azimuth is angle in radians from vertical axis
2418
 *
2419
 */
2420
int
2421
project_pt(const POINT2D *P, double distance, double azimuth, POINT2D *R)
2422
0
{
2423
0
  const double TWOPI = 2.0 * M_PI;
2424
0
  double slope;
2425
  /* Deal with azimuth out of (-360,360) range */
2426
0
  int orbits = floor(azimuth / TWOPI);
2427
0
  azimuth -= TWOPI * orbits;
2428
  /* Convert from azimuth to conventional slope */
2429
0
  slope = TWOPI - azimuth + M_PI_2;
2430
0
  if (slope > 0 && slope >  TWOPI) slope -= TWOPI;
2431
0
  if (slope < 0 && slope < -TWOPI) slope += TWOPI;
2432
2433
0
  double dx = cos(slope) * distance;
2434
0
  double dy = sin(slope) * distance;
2435
0
  R->x = P->x + dx;
2436
0
  R->y = P->y + dy;
2437
0
  return LW_TRUE;
2438
0
}
2439
2440
/**
2441
 * Azimuth is angle in radians from vertical axis
2442
 *
2443
 */
2444
int
2445
project_pt_pt(const POINT4D *A, const POINT4D *B, double distance, POINT4D *R)
2446
0
{
2447
  /* Convert from azimuth to conventional slope */
2448
0
  double len = distance2d_pt_pt((const POINT2D *)A, (const POINT2D *)B);
2449
0
  double prop = distance / len;
2450
0
  double dx = (B->x - A->x) * prop;
2451
0
  double dy = (B->y - A->y) * prop;
2452
0
  double dz = (B->z - A->z) * prop;
2453
0
  double dm = (B->m - A->m) * prop;
2454
0
  R->x = B->x + dx;
2455
0
  R->y = B->y + dy;
2456
0
  if (isfinite(dz)) R->z = B->z + dz;
2457
0
  if (isfinite(dm)) R->m = B->m + dm;
2458
0
  return LW_TRUE;
2459
0
}
2460