123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- /*!
- * \file lib/gis/area_poly1.c
- *
- * \brief GIS Library - Polygon area calculation routines.
- *
- * (C) 2001-2013 by the GRASS Development Team
- *
- * This program is free software under the GNU General Public License
- * (>=v2). Read the file COPYING that comes with GRASS for details.
- *
- * \author Original author CERL
- */
- #include <math.h>
- #include <grass/gis.h>
- #include "pi.h"
- #define TWOPI M_PI + M_PI
- static struct state {
- double QA, QB, QC;
- double QbarA, QbarB, QbarC, QbarD;
- double AE; /** a^2(1-e^2) */
- double Qp; /** Q at the north pole */
- double E; /** Area of the earth */
- } state;
- static struct state *st = &state;
- static double Q(double x)
- {
- double sinx, sinx2;
- sinx = sin(x);
- sinx2 = sinx * sinx;
- return sinx * (1 + sinx2 * (st->QA + sinx2 * (st->QB + sinx2 * st->QC)));
- }
- static double Qbar(double x)
- {
- double cosx, cosx2;
- cosx = cos(x);
- cosx2 = cosx * cosx;
- return cosx * (st->QbarA + cosx2 * (st->QbarB + cosx2 * (st->QbarC + cosx2 * st->QbarD)));
- }
- /*!
- * \brief Begin area calculations.
- *
- * This initializes the polygon area calculations for the
- * ellipsoid with semi-major axis <i>a</i> (in meters) and ellipsoid
- * eccentricity squared <i>e2</i>.
- *
- * \param a semi-major axis
- * \param e2 ellipsoid eccentricity squared
- */
- void G_begin_ellipsoid_polygon_area(double a, double e2)
- {
- double e4, e6;
- e4 = e2 * e2;
- e6 = e4 * e2;
- st->AE = a * a * (1 - e2);
- st->QA = (2.0 / 3.0) * e2;
- st->QB = (3.0 / 5.0) * e4;
- st->QC = (4.0 / 7.0) * e6;
- st->QbarA = -1.0 - (2.0 / 3.0) * e2 - (3.0 / 5.0) * e4 - (4.0 / 7.0) * e6;
- st->QbarB = (2.0 / 9.0) * e2 + (2.0 / 5.0) * e4 + (4.0 / 7.0) * e6;
- st->QbarC = -(3.0 / 25.0) * e4 - (12.0 / 35.0) * e6;
- st->QbarD = (4.0 / 49.0) * e6;
- st->Qp = Q(M_PI_2);
- st->E = 4 * M_PI * st->Qp * st->AE;
- if (st->E < 0.0)
- st->E = -st->E;
- }
- /*!
- * \brief Area of lat-long polygon.
- *
- * Returns the area in square meters of the polygon described by the
- * <i>n</i> pairs of <i>lat,long</i> vertices for latitude-longitude
- * grids.
- *
- * <b>Note:</b> This routine computes the area of a polygon on the
- * ellipsoid. The sides of the polygon are rhumb lines and, in general,
- * not geodesics. Each side is actually defined by a linear relationship
- * between latitude and longitude, i.e., on a rectangular/equidistant
- * cylindrical/Plate Carr{'e}e grid, the side would appear as a
- * straight line. For two consecutive vertices of the polygon,
- * (lat_1, long1) and (lat_2,long_2), the line joining them (i.e., the
- * polygon's side) is defined by:
- *
- \verbatim
- lat_2 - lat_1
- lat = lat_1 + (long - long_1) * ---------------
- long_2 - long_1
- \endverbatim
- *
- * where long_1 < long < long_2.
- * The values of QbarA, etc., are determined by the integration of
- * the Q function. Into www.integral-calculator.com, paste this
- * expression :
- *
- \verbatim
- sin(x)+ (2/3)e^2(sin(x))^3 + (3/5)e^4(sin(x))^5 + (4/7)e^6(sin(x))^7
- \endverbatim
- *
- * and you'll get their values. (Last checked 30 Oct 2013).
- *
- * This function correctly computes (within the limits of the series
- * approximation) the area of a quadrilateral on the ellipsoid when
- * two of its sides run along meridians and the other two sides run
- * along parallels of latitude.
- *
- * \param lon array of longitudes
- * \param lat array of latitudes
- * \param n number of lat,lon pairs
- *
- * \return area in square meters
- */
- double G_ellipsoid_polygon_area(const double *lon, const double *lat, int n)
- {
- double x1, y1, x2, y2, dx, dy;
- double Qbar1, Qbar2;
- double area;
- double thresh = 1e-6; /* threshold for dy, should be between 1e-4 and 1e-7 */
- x2 = Radians(lon[n - 1]);
- y2 = Radians(lat[n - 1]);
- Qbar2 = Qbar(y2);
- area = 0.0;
- while (--n >= 0) {
- x1 = x2;
- y1 = y2;
- Qbar1 = Qbar2;
- x2 = Radians(*lon++);
- y2 = Radians(*lat++);
- Qbar2 = Qbar(y2);
- if (x1 > x2)
- while (x1 - x2 > M_PI)
- x2 += TWOPI;
- else if (x2 > x1)
- while (x2 - x1 > M_PI)
- x1 += TWOPI;
- dx = x2 - x1;
- dy = y2 - y1;
- if (fabs(dy) > thresh) {
- /* account for different latitudes y1, y2 */
- area += dx * (st->Qp - (Qbar2 - Qbar1) / dy);
- /* original:
- * area += dx * st->Qp - (dx / dy) * (Qbar2 - Qbar1);
- */
- }
- else {
- /* latitudes y1, y2 are (nearly) identical */
- /* if y2 becomes similar to y1, i.e. y2 -> y1
- * Qbar2 - Qbar1 -> 0 and dy -> 0
- * (Qbar2 - Qbar1) / dy -> ?
- * (Qbar2 - Qbar1) / dy should approach Q((y1 + y2) / 2)
- * Metz 2017
- */
- area += dx * (st->Qp - Q((y1 + y2) / 2));
- }
- }
- if ((area *= st->AE) < 0.0)
- area = -area;
- /* kludge - if polygon circles the south pole the area will be
- * computed as if it cirlced the north pole. The correction is
- * the difference between total surface area of the earth and
- * the "north pole" area.
- */
- if (area > st->E)
- area = st->E;
- if (area > st->E / 2)
- area = st->E - area;
- return area;
- }
|