GRASS 8 Programmer's Manual 8.6.0dev(2026)-41d63e587c
Loading...
Searching...
No Matches
area_poly1.c
Go to the documentation of this file.
1/*!
2 * \file lib/gis/area_poly1.c
3 *
4 * \brief GIS Library - Polygon area calculation routines.
5 *
6 * (C) 2001-2013 by the GRASS Development Team
7 *
8 * This program is free software under the GNU General Public License
9 * (>=v2). Read the file COPYING that comes with GRASS for details.
10 *
11 * \author Original author CERL
12 */
13
14#include <math.h>
15#include <grass/gis.h>
16#include <geodesic.h>
17#include "pi.h"
18
19#define TWOPI M_PI + M_PI
20
21#define USE_GEOGRAPHICLIB 1
22
23static struct state {
24#ifndef USE_GEOGRAPHICLIB
25 double QA, QB, QC;
26 double QbarA, QbarB, QbarC, QbarD;
27
28 double AE; /** a^2(1-e^2) */
29
30 double Qp; /** Q at the north pole */
31
32 double E; /** Area of the earth */
33
34#else
35 struct geod_geodesic g; /** GeographicLib */
36#endif
37} state;
38
39static struct state *st = &state;
40
41#ifndef USE_GEOGRAPHICLIB
42static double Q(double x)
43{
44 double sinx, sinx2;
45
46 sinx = sin(x);
47 sinx2 = sinx * sinx;
48
49 return sinx * (1 + sinx2 * (st->QA + sinx2 * (st->QB + sinx2 * st->QC)));
50}
51
52static double Qbar(double x)
53{
54 double cosx, cosx2;
55
56 cosx = cos(x);
57 cosx2 = cosx * cosx;
58
59 return cosx *
60 (st->QbarA +
61 cosx2 * (st->QbarB + cosx2 * (st->QbarC + cosx2 * st->QbarD)));
62}
63#endif
64
65/*!
66 * \brief Begin area calculations.
67 *
68 * This initializes the polygon area calculations for the
69 * ellipsoid with semi-major axis <i>a</i> (in meters) and ellipsoid
70 * eccentricity squared <i>e2</i>.
71 *
72 * \param a semi-major axis
73 * \param e2 ellipsoid eccentricity squared
74 */
75void G_begin_ellipsoid_polygon_area(double a, double e2)
76{
77#ifdef USE_GEOGRAPHICLIB
78 double f;
79
80 /* GeographicLib */
82 f = 1.0 - sqrt(1.0 - e2);
83 geod_init(&st->g, a, f);
84
85#else
86 double e4, e6;
87
88 /* GRASS native */
89 e4 = e2 * e2;
90 e6 = e4 * e2;
91
92 st->AE = a * a * (1 - e2);
93
94 st->QA = (2.0 / 3.0) * e2;
95 st->QB = (3.0 / 5.0) * e4;
96 st->QC = (4.0 / 7.0) * e6;
97
98 st->QbarA = -1.0 - (2.0 / 3.0) * e2 - (3.0 / 5.0) * e4 - (4.0 / 7.0) * e6;
99 st->QbarB = (2.0 / 9.0) * e2 + (2.0 / 5.0) * e4 + (4.0 / 7.0) * e6;
100 st->QbarC = -(3.0 / 25.0) * e4 - (12.0 / 35.0) * e6;
101 st->QbarD = (4.0 / 49.0) * e6;
102
103 st->Qp = Q(M_PI_2);
104 st->E = 4 * M_PI * st->Qp * st->AE;
105 if (st->E < 0.0)
106 st->E = -st->E;
107#endif
108}
109
110/*!
111 * \brief Area of lat-long polygon.
112 *
113 * Returns the area in square meters of the polygon described by the
114 * <i>n</i> pairs of <i>lat,long</i> vertices for latitude-longitude
115 * grids.
116 *
117 * <b>Note:</b> This routine computes the area of a polygon on the
118 * ellipsoid. The sides of the polygon are rhumb lines and, in general,
119 * not geodesics. Each side is actually defined by a linear relationship
120 * between latitude and longitude, i.e., on a rectangular/equidistant
121 * cylindrical/Plate Carr{'e}e grid, the side would appear as a
122 * straight line. For two consecutive vertices of the polygon,
123 * (lat_1, long1) and (lat_2,long_2), the line joining them (i.e., the
124 * polygon's side) is defined by:
125 *
126 \verbatim
127 lat_2 - lat_1
128 lat = lat_1 + (long - long_1) * ---------------
129 long_2 - long_1
130 \endverbatim
131 *
132 * where long_1 < long < long_2.
133 * The values of QbarA, etc., are determined by the integration of
134 * the Q function. Into www.integral-calculator.com, paste this
135 * expression :
136 *
137 \verbatim
138 sin(x)+ (2/3)e^2(sin(x))^3 + (3/5)e^4(sin(x))^5 + (4/7)e^6(sin(x))^7
139 \endverbatim
140 *
141 * and you'll get their values. (Last checked 30 Oct 2013).
142 *
143 * This function correctly computes (within the limits of the series
144 * approximation) the area of a quadrilateral on the ellipsoid when
145 * two of its sides run along meridians and the other two sides run
146 * along parallels of latitude.
147 *
148 * \param lon array of longitudes
149 * \param lat array of latitudes
150 * \param n number of lat,lon pairs
151 *
152 * \return area in square meters
153 */
154double G_ellipsoid_polygon_area(const double *lon, const double *lat, int n)
155{
156 double area;
157
158#ifdef USE_GEOGRAPHICLIB
159 /* GeographicLib */
160 struct geod_polygon p;
161 double pP;
162 int i;
163
165 /* GeographicLib does not need a closed ring,
166 * see example for geod_polygonarea() in geodesic.h */
167 /* add points in reverse order */
168 i = n;
169 while (--i)
170 geod_polygon_addpoint(&st->g, &p, (double)lat[i], (double)lon[i]);
171 /* The area returned is signed
172 * with counter-clockwise traversal being treated as positive. */
173 geod_polygon_compute(&st->g, &p, FALSE, TRUE, &area, &pP);
174 area = fabs(area);
175
176#else
177 double x1, y1, x2, y2, dx, dy;
178 double Qbar1, Qbar2;
179 /* threshold for dy, should be between 1e-4 and 1e-7 */
180 double thresh = 1e-6;
181
182 /* GRASS native */
183 x2 = Radians(lon[n - 1]);
184 y2 = Radians(lat[n - 1]);
185 Qbar2 = Qbar(y2);
186
187 area = 0.0;
188
189 while (--n >= 0) {
190 x1 = x2;
191 y1 = y2;
192 Qbar1 = Qbar2;
193
194 x2 = Radians(*lon++);
195 y2 = Radians(*lat++);
196 Qbar2 = Qbar(y2);
197
198 if (x1 > x2)
199 while (x1 - x2 > M_PI)
200 x2 += TWOPI;
201 else if (x2 > x1)
202 while (x2 - x1 > M_PI)
203 x1 += TWOPI;
204
205 dx = x2 - x1;
206 dy = y2 - y1;
207
208 if (fabs(dy) > thresh) {
209 /* account for different latitudes y1, y2 */
210 area += dx * (st->Qp - (Qbar2 - Qbar1) / dy);
211 /* original:
212 * area += dx * st->Qp - (dx / dy) * (Qbar2 - Qbar1);
213 */
214 }
215 else {
216 /* latitudes y1, y2 are (nearly) identical */
217 /* if y2 becomes similar to y1, i.e. y2 -> y1
218 * Qbar2 - Qbar1 -> 0 and dy -> 0
219 * (Qbar2 - Qbar1) / dy -> ?
220 * (Qbar2 - Qbar1) / dy should approach Q((y1 + y2) / 2)
221 * Metz 2017
222 */
223 area += dx * (st->Qp - Q((y1 + y2) / 2));
224 }
225 }
226 if ((area *= st->AE) < 0.0)
227 area = -area;
228
229 /* kludge - if polygon circles the south pole the area will be
230 * computed as if it circled the north pole. The correction is
231 * the difference between total surface area of the earth and
232 * the "north pole" area.
233 */
234 if (area > st->E)
235 area = st->E;
236 if (area > st->E / 2)
237 area = st->E - area;
238#endif
239
240 return area;
241}
#define TWOPI
Definition area_poly1.c:19
void G_begin_ellipsoid_polygon_area(double a, double e2)
Begin area calculations.
Definition area_poly1.c:75
double G_ellipsoid_polygon_area(const double *lon, const double *lat, int n)
Area of lat-long polygon.
Definition area_poly1.c:154
int G_get_ellipsoid_parameters(double *, double *)
get ellipsoid parameters
Definition get_ellipse.c:66
#define M_PI_2
Definition gis.h:160
#define TRUE
Definition gis.h:78
#define FALSE
Definition gis.h:82
#define M_PI
Definition gis.h:157
float g
Definition named_colr.c:7
#define Radians(x)
Definition pi.h:6