PostGIS  2.5.7dev-r@@SVN_REVISION@@

◆ ptarray_area_spheroid()

static double ptarray_area_spheroid ( const POINTARRAY pa,
const SPHEROID spheroid 
)
static

Definition at line 499 of file lwspheroid.c.

500 {
501  GEOGRAPHIC_POINT a, b;
502  POINT2D p;
503  int i;
504  double area = 0.0;
505  GBOX gbox2d;
506  int in_south = LW_FALSE;
507  double delta_lon_tolerance;
508  double latitude_min;
509 
510  gbox2d.flags = gflags(0, 0, 0);
511 
512  /* Return zero on non-sensical inputs */
513  if ( ! pa || pa->npoints < 4 )
514  return 0.0;
515 
516  /* Get the raw min/max values for the latitudes */
518 
519  if ( SIGNUM(gbox2d.ymin) != SIGNUM(gbox2d.ymax) )
520  lwerror("ptarray_area_spheroid: cannot handle ptarray that crosses equator");
521 
522  /* Geodetic bbox < 0.0 implies geometry is entirely in southern hemisphere */
523  if ( gbox2d.ymax < 0.0 )
524  in_south = LW_TRUE;
525 
526  LWDEBUGF(4, "gbox2d.ymax %.12g", gbox2d.ymax);
527 
528  /* Tolerance for strip area calculation */
529  if ( in_south )
530  {
531  delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymin) / 8.0) - 2.0) / 10000.0;
532  latitude_min = deg2rad(fabs(gbox2d.ymax));
533  }
534  else
535  {
536  delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymax) / 8.0) - 2.0) / 10000.0;
537  latitude_min = deg2rad(gbox2d.ymin);
538  }
539 
540  /* Initialize first point */
541  getPoint2d_p(pa, 0, &p);
542  geographic_point_init(p.x, p.y, &a);
543 
544  for ( i = 1; i < pa->npoints; i++ )
545  {
546  GEOGRAPHIC_POINT a1, b1;
547  double strip_area = 0.0;
548  double delta_lon = 0.0;
549  LWDEBUGF(4, "edge #%d", i);
550 
551  getPoint2d_p(pa, i, &p);
552  geographic_point_init(p.x, p.y, &b);
553 
554  a1 = a;
555  b1 = b;
556 
557  /* Flip into north if in south */
558  if ( in_south )
559  {
560  a1.lat = -1.0 * a1.lat;
561  b1.lat = -1.0 * b1.lat;
562  }
563 
564  LWDEBUGF(4, "in_south %d", in_south);
565 
566  LWDEBUGF(4, "crosses_dateline(a, b) %d", crosses_dateline(&a, &b) );
567 
568  if ( crosses_dateline(&a, &b) )
569  {
570  double shift;
571 
572  if ( a1.lon > 0.0 )
573  shift = (M_PI - a1.lon) + 0.088; /* About 5deg more */
574  else
575  shift = (M_PI - b1.lon) + 0.088; /* About 5deg more */
576 
577  LWDEBUGF(4, "shift: %.8g", shift);
578  LWDEBUGF(4, "before shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon);
579  point_shift(&a1, shift);
580  point_shift(&b1, shift);
581  LWDEBUGF(4, "after shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon);
582 
583  }
584 
585 
586  delta_lon = fabs(b1.lon - a1.lon);
587 
588  LWDEBUGF(4, "a1(%.18g %.18g) b1(%.18g %.18g)", a1.lat, a1.lon, b1.lat, b1.lon);
589  LWDEBUGF(4, "delta_lon %.18g", delta_lon);
590  LWDEBUGF(4, "delta_lon_tolerance %.18g", delta_lon_tolerance);
591 
592  if ( delta_lon > 0.0 )
593  {
594  if ( delta_lon < delta_lon_tolerance )
595  {
596  strip_area = spheroid_striparea(&a1, &b1, latitude_min, spheroid);
597  LWDEBUGF(4, "strip_area %.12g", strip_area);
598  area += strip_area;
599  }
600  else
601  {
602  GEOGRAPHIC_POINT p, q;
603  double step = floor(delta_lon / delta_lon_tolerance);
604  double distance = spheroid_distance(&a1, &b1, spheroid);
605  double pDistance = 0.0;
606  int j = 0;
607  LWDEBUGF(4, "step %.18g", step);
608  LWDEBUGF(4, "distance %.18g", distance);
609  step = distance / step;
610  LWDEBUGF(4, "step %.18g", step);
611  p = a1;
612  while (pDistance < (distance - step * 1.01))
613  {
614  double azimuth = spheroid_direction(&p, &b1, spheroid);
615  j++;
616  LWDEBUGF(4, " iteration %d", j);
617  LWDEBUGF(4, " azimuth %.12g", azimuth);
618  pDistance = pDistance + step;
619  LWDEBUGF(4, " pDistance %.12g", pDistance);
620  spheroid_project(&p, spheroid, step, azimuth, &q);
621  strip_area = spheroid_striparea(&p, &q, latitude_min, spheroid);
622  LWDEBUGF(4, " strip_area %.12g", strip_area);
623  area += strip_area;
624  LWDEBUGF(4, " area %.12g", area);
625  p.lat = q.lat;
626  p.lon = q.lon;
627  }
628  strip_area = spheroid_striparea(&p, &b1, latitude_min, spheroid);
629  area += strip_area;
630  }
631  }
632 
633  /* B gets incremented in the next loop, so we save the value here */
634  a = b;
635  }
636  return fabs(area);
637 }
int ptarray_calculate_gbox_cartesian(const POINTARRAY *pa, GBOX *gbox)
Calculate box (x/y) and add values to gbox.
Definition: g_box.c:542
uint8_t gflags(int hasz, int hasm, int geodetic)
Construct a new flags char.
Definition: g_util.c:145
#define LW_FALSE
Definition: liblwgeom.h:77
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition: lwgeom_api.c:348
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:76
#define SIGNUM(n)
Macro that returns: -1 if n < 0, 1 if n > 0, 0 if n == 0.
void point_shift(GEOGRAPHIC_POINT *p, double shift)
Shift a point around by a number of radians.
Definition: lwgeodetic.c:160
void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g)
Initialize a geographic point.
Definition: lwgeodetic.c:180
int crosses_dateline(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e)
Definition: lwgeodetic.c:666
#define deg2rad(d)
Conversion functions.
Definition: lwgeodetic.h:79
Datum distance(PG_FUNCTION_ARGS)
Datum area(PG_FUNCTION_ARGS)
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
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:191
static double spheroid_striparea(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, double latitude_min, const SPHEROID *spheroid)
This function doesn't work for edges crossing the dateline or in the southern hemisphere.
Definition: lwspheroid.c:460
int spheroid_project(const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g)
Given a location, an azimuth and a distance, computes the location of the projected point.
Definition: lwspheroid.c:364
double spheroid_direction(const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid)
Computes the direction of the geodesic joining two points on the spheroid.
Definition: lwspheroid.c:287
double ymax
Definition: liblwgeom.h:298
double ymin
Definition: liblwgeom.h:297
uint8_t flags
Definition: liblwgeom.h:294
Point in spherical coordinates on the world.
Definition: lwgeodetic.h:53
double y
Definition: liblwgeom.h:331
double x
Definition: liblwgeom.h:331
uint32_t npoints
Definition: liblwgeom.h:374

References area(), crosses_dateline(), deg2rad, distance(), GBOX::flags, geographic_point_init(), getPoint2d_p(), gflags(), GEOGRAPHIC_POINT::lat, GEOGRAPHIC_POINT::lon, LW_FALSE, LW_TRUE, LWDEBUGF, lwerror(), POINTARRAY::npoints, point_shift(), ptarray_calculate_gbox_cartesian(), SIGNUM, spheroid_direction(), spheroid_distance(), spheroid_project(), spheroid_striparea(), POINT2D::x, POINT2D::y, GBOX::ymax, and GBOX::ymin.

Referenced by lwgeom_area_spheroid().

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