PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches

◆ ptarray_locate_point_spheroid()

double ptarray_locate_point_spheroid ( const POINTARRAY pa,
const POINT4D p4d,
const SPHEROID s,
double  tolerance,
double *  mindistout,
POINT4D proj4d 
)

Locate a point along the point array defining a geographic line.

Definition at line 362 of file lwgeodetic_measures.c.

369{
371 GEOGRAPHIC_POINT a, b, nearest = {0}; /* make compiler quiet */
372 POINT4D p1, p2;
373 const POINT2D *p;
374 POINT2D proj;
375 uint32_t i, seg = 0;
376 int use_sphere = (s->a == s->b ? 1 : 0);
377 int hasz;
378 double za = 0.0, zb = 0.0;
379 double distance,
380 length, /* Used for computing lengths */
381 seglength = 0.0, /* length of the segment where the closest point is located */
382 partlength = 0.0, /* length from the beginning of the point array to the closest point */
383 totlength = 0.0; /* length of the point array */
384
385 /* Initialize our point */
386 geographic_point_init(p4d->x, p4d->y, &a);
387
388 /* Handle point/point case here */
389 if ( pa->npoints <= 1)
390 {
391 double mindist = 0.0;
392 if ( pa->npoints == 1 )
393 {
394 p = getPoint2d_cp(pa, 0);
395 geographic_point_init(p->x, p->y, &b);
396 /* Sphere special case, axes equal */
397 mindist = s->radius * sphere_distance(&a, &b);
398 /* If close or greater than tolerance, get the real answer to be sure */
399 if ( ! use_sphere || mindist > 0.95 * tolerance )
400 {
401 mindist = spheroid_distance(&a, &b, s);
402 }
403 }
404 if ( mindistout ) *mindistout = mindist;
405 return 0.0;
406 }
407
408 /* Make result really big, so that everything will be smaller than it */
409 distance = FLT_MAX;
410
411 /* Initialize start of line */
412 p = getPoint2d_cp(pa, 0);
413 geographic_point_init(p->x, p->y, &(e.start));
414
415 /* Iterate through the edges in our line */
416 for ( i = 1; i < pa->npoints; i++ )
417 {
418 double d;
419 p = getPoint2d_cp(pa, i);
420 geographic_point_init(p->x, p->y, &(e.end));
421 /* Get the spherical distance between point and edge */
422 d = s->radius * edge_distance_to_point(&e, &a, &b);
423 /* New shortest distance! Record this distance / location / segment */
424 if ( d < distance )
425 {
426 distance = d;
427 nearest = b;
428 seg = i - 1;
429 }
430 /* We've gotten closer than the tolerance... */
431 if ( d < tolerance )
432 {
433 /* Working on a sphere? The answer is correct, return */
434 if ( use_sphere )
435 {
436 break;
437 }
438 /* Far enough past the tolerance that the spheroid calculation won't change things */
439 else if ( d < tolerance * 0.95 )
440 {
441 break;
442 }
443 /* On a spheroid and near the tolerance? Confirm that we are *actually* closer than tolerance */
444 else
445 {
446 d = spheroid_distance(&a, &nearest, s);
447 /* Yes, closer than tolerance, return! */
448 if ( d < tolerance )
449 break;
450 }
451 }
452 e.start = e.end;
453 }
454
455 if ( mindistout ) *mindistout = distance;
456
457 /* See if we have a third dimension */
458 hasz = (bool) FLAGS_GET_Z(pa->flags);
459
460 /* Initialize first point of array */
461 getPoint4d_p(pa, 0, &p1);
462 geographic_point_init(p1.x, p1.y, &a);
463 if ( hasz )
464 za = p1.z;
465
466 /* Loop and sum the length for each segment */
467 for ( i = 1; i < pa->npoints; i++ )
468 {
469 getPoint4d_p(pa, i, &p1);
470 geographic_point_init(p1.x, p1.y, &b);
471 if ( hasz )
472 zb = p1.z;
473
474 /* Special sphere case */
475 if ( s->a == s->b )
476 length = s->radius * sphere_distance(&a, &b);
477 /* Spheroid case */
478 else
479 length = spheroid_distance(&a, &b, s);
480
481 /* Add in the vertical displacement if we're in 3D */
482 if ( hasz )
483 length = sqrt( (zb-za)*(zb-za) + length*length );
484
485 /* Add this segment length to the total length */
486 totlength += length;
487
488 /* Add this segment length to the partial length */
489 if (i - 1 < seg)
490 partlength += length;
491 else if (i - 1 == seg)
492 /* Save segment length for computing the final value of partlength */
493 seglength = length;
494
495 /* B gets incremented in the next loop, so we save the value here */
496 a = b;
497 za = zb;
498 }
499
500 /* Copy nearest into 2D/4D holder */
501 proj4d->x = proj.x = rad2deg(nearest.lon);
502 proj4d->y = proj.y = rad2deg(nearest.lat);
503
504 /* Compute distance from beginning of the segment to closest point */
505
506 /* Start of the segment */
507 getPoint4d_p(pa, seg, &p1);
508 geographic_point_init(p1.x, p1.y, &a);
509
510 /* Closest point */
511 geographic_point_init(proj4d->x, proj4d->y, &b);
512
513 /* Special sphere case */
514 if ( s->a == s->b )
515 length = s->radius * sphere_distance(&a, &b);
516 /* Spheroid case */
517 else
518 length = spheroid_distance(&a, &b, s);
519
520 if ( hasz )
521 {
522 /* Compute Z and M values for closest point */
523 double f = length / seglength;
524 getPoint4d_p(pa, seg + 1, &p2);
525 proj4d->z = p1.z + ((p2.z - p1.z) * f);
526 proj4d->m = p1.m + ((p2.m - p1.m) * f);
527 /* Add in the vertical displacement if we're in 3D */
528 za = p1.z;
529 zb = proj4d->z;
530 length = sqrt( (zb-za)*(zb-za) + length*length );
531 }
532
533 /* Add this segment length to the total */
534 partlength += length;
535
536 /* Location of any point on a zero-length line is 0 */
537 /* See http://trac.osgeo.org/postgis/ticket/1772#comment:2 */
538 if ( partlength == 0 || totlength == 0 )
539 return 0.0;
540
541 /* For robustness, force 0 when closest point == startpoint */
542 p = getPoint2d_cp(pa, 0);
543 if ( seg == 0 && p2d_same(&proj, p) )
544 return 0.0;
545
546 /* For robustness, force 1 when closest point == endpoint */
547 p = getPoint2d_cp(pa, pa->npoints - 1);
548 if ( (seg >= (pa->npoints-2)) && p2d_same(&proj, p) )
549 return 1.0;
550
551 /* Floating point arithmetic is not reliable, make sure we return values [0,1] */
552 double result = partlength / totlength;
553 if ( result < 0.0 ) {
554 result = 0.0;
555 } else if ( result > 1.0 ) {
556 result = 1.0;
557 }
558 return result;
559}
char * s
Definition cu_in_wkt.c:23
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition cu_print.c:267
#define FLAGS_GET_Z(flags)
Definition liblwgeom.h:165
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition lwgeom_api.c:125
int p2d_same(const POINT2D *p1, const POINT2D *p2)
Definition lwalgorithm.c:57
void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g)
Initialize a geographic point.
Definition lwgeodetic.c:180
double sphere_distance(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e)
Given two points on a unit sphere, calculate their distance apart in radians.
Definition lwgeodetic.c:896
double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest)
#define rad2deg(r)
Definition lwgeodetic.h:81
double spheroid_distance(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid)
Computes the shortest distance along the surface of the spheroid between two points,...
Definition lwspheroid.c:79
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition lwinline.h:97
static double distance(double x1, double y1, double x2, double y2)
Definition lwtree.c:1032
GEOGRAPHIC_POINT start
Definition lwgeodetic.h:64
GEOGRAPHIC_POINT end
Definition lwgeodetic.h:65
Two-point great circle segment from a to b.
Definition lwgeodetic.h:63
Point in spherical coordinates on the world.
Definition lwgeodetic.h:54
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
double m
Definition liblwgeom.h:414
double x
Definition liblwgeom.h:414
double z
Definition liblwgeom.h:414
double y
Definition liblwgeom.h:414
lwflags_t flags
Definition liblwgeom.h:431
uint32_t npoints
Definition liblwgeom.h:427

References distance(), edge_distance_to_point(), GEOGRAPHIC_EDGE::end, POINTARRAY::flags, FLAGS_GET_Z, geographic_point_init(), getPoint2d_cp(), getPoint4d_p(), GEOGRAPHIC_POINT::lat, GEOGRAPHIC_POINT::lon, POINT4D::m, POINTARRAY::npoints, p2d_same(), rad2deg, result, s, sphere_distance(), spheroid_distance(), GEOGRAPHIC_EDGE::start, POINT2D::x, POINT4D::x, POINT2D::y, POINT4D::y, and POINT4D::z.

Referenced by geography_line_locate_point().

Here is the call graph for this function:
Here is the caller graph for this function: