PostGIS  3.7.0dev-r@@SVN_REVISION@@

◆ 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 {
370  GEOGRAPHIC_EDGE e;
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  return partlength / totlength;
552 }
char * s
Definition: cu_in_wkt.c:23
#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)
Definition: lwgeodetic.c:1170
#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, 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: