PostGIS  3.4.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 361 of file lwgeodetic_measures.c.

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