GeographicLib 2.3
PolygonArea.cpp
Go to the documentation of this file.
1/**
2 * \file PolygonArea.cpp
3 * \brief Implementation for GeographicLib::PolygonAreaT class
4 *
5 * Copyright (c) Charles Karney (2010-2023) <karney@alum.mit.edu> and licensed
6 * under the MIT/X11 License. For more information, see
7 * https://geographiclib.sourceforge.io/
8 **********************************************************************/
9
11
12#if defined(_MSC_VER)
13// Squelch warnings about enum-float expressions
14# pragma warning (disable: 5055)
15#endif
16
17namespace GeographicLib {
18
19 using namespace std;
20
21 template<class GeodType>
22 int PolygonAreaT<GeodType>::transit(real lon1, real lon2) {
23 // Return 1 or -1 if crossing prime meridian in east or west direction.
24 // Otherwise return zero. longitude = +/-0 considered to be positive.
25 // This is (should be?) compatible with transitdirect which computes
26 // exactly the parity of
27 // int(floor((lon1 + lon12) / 360)) - int(floor(lon1 / 360)))
28 real lon12 = Math::AngDiff(lon1, lon2);
29 lon1 = Math::AngNormalize(lon1);
30 lon2 = Math::AngNormalize(lon2);
31 // N.B. lon12 == 0 gives cross = 0
32 return
33 // edge case lon1 = 180, lon2 = 360->0, lon12 = 180 to give 1
34 lon12 > 0 && ((lon1 < 0 && lon2 >= 0) ||
35 // lon12 > 0 && lon1 > 0 && lon2 == 0 implies lon1 == 180
36 (lon1 > 0 && lon2 == 0)) ? 1 :
37 // non edge case lon1 = -180, lon2 = -360->-0, lon12 = -180
38 (lon12 < 0 && lon1 >= 0 && lon2 < 0 ? -1 : 0);
39 // This was the old method (treating +/- 0 as negative). However, with the
40 // new scheme for handling longitude differences this fails on:
41 // lon1 = -180, lon2 = -360->-0, lon12 = -180 gives 0 not -1.
42 // return
43 // lon1 <= 0 && lon2 > 0 && lon12 > 0 ? 1 :
44 // (lon2 <= 0 && lon1 > 0 && lon12 < 0 ? -1 : 0);
45 }
46
47 // an alternate version of transit to deal with longitudes in the direct
48 // problem.
49 template<class GeodType>
50 int PolygonAreaT<GeodType>::transitdirect(real lon1, real lon2) {
51 // Compute exactly the parity of
52 // int(floor(lon2 / 360)) - int(floor(lon1 / 360))
53 // C++ C remainder -> [-360, 360]
54 // Java % -> (-720, 720) switch to IEEEremainder -> [-360, 360]
55 // JS % -> (-720, 720)
56 // Python fmod -> (-720, 720) swith to Math.remainder
57 // Fortran, Octave skip
58 // If mod function gives result in [-360, 360]
59 // [0, 360) -> 0; [-360, 0) or 360 -> 1
60 // If mod function gives result in (-720, 720)
61 // [0, 360) or [-inf, -360) -> 0; [-360, 0) or [360, inf) -> 1
62 lon1 = remainder(lon1, real(2 * Math::td));
63 lon2 = remainder(lon2, real(2 * Math::td));
64 return ( (lon2 >= 0 && lon2 < Math::td ? 0 : 1) -
65 (lon1 >= 0 && lon1 < Math::td ? 0 : 1) );
66 }
67
68 template<class GeodType>
69 void PolygonAreaT<GeodType>::AddPoint(real lat, real lon) {
70 if (_num == 0) {
71 _lat0 = _lat1 = lat;
72 _lon0 = _lon1 = lon;
73 } else {
74 real s12, S12, t;
75 _earth.GenInverse(_lat1, _lon1, lat, lon, _mask,
76 s12, t, t, t, t, t, S12);
77 _perimetersum += s12;
78 if (!_polyline) {
79 _areasum += S12;
80 _crossings += transit(_lon1, lon);
81 }
82 _lat1 = lat; _lon1 = lon;
83 }
84 ++_num;
85 }
86
87 template<class GeodType>
88 void PolygonAreaT<GeodType>::AddEdge(real azi, real s) {
89 if (_num) { // Do nothing if _num is zero
90 real lat, lon, S12, t;
91 _earth.GenDirect(_lat1, _lon1, azi, false, s, _mask,
92 lat, lon, t, t, t, t, t, S12);
93 _perimetersum += s;
94 if (!_polyline) {
95 _areasum += S12;
96 _crossings += transitdirect(_lon1, lon);
97 }
98 _lat1 = lat; _lon1 = lon;
99 ++_num;
100 }
101 }
102
103 template<class GeodType>
104 unsigned PolygonAreaT<GeodType>::Compute(bool reverse, bool sign,
105 real& perimeter, real& area) const
106 {
107 real s12, S12, t;
108 if (_num < 2) {
109 perimeter = 0;
110 if (!_polyline)
111 area = 0;
112 return _num;
113 }
114 if (_polyline) {
115 perimeter = _perimetersum();
116 return _num;
117 }
118 _earth.GenInverse(_lat1, _lon1, _lat0, _lon0, _mask,
119 s12, t, t, t, t, t, S12);
120 perimeter = _perimetersum(s12);
121 Accumulator<> tempsum(_areasum);
122 tempsum += S12;
123 int crossings = _crossings + transit(_lon1, _lon0);
124 AreaReduce(tempsum, crossings, reverse, sign);
125 area = real(0) + tempsum();
126 return _num;
127 }
128
129 template<class GeodType>
130 unsigned PolygonAreaT<GeodType>::TestPoint(real lat, real lon,
131 bool reverse, bool sign,
132 real& perimeter, real& area) const
133 {
134 if (_num == 0) {
135 perimeter = 0;
136 if (!_polyline)
137 area = 0;
138 return 1;
139 }
140 perimeter = _perimetersum();
141 real tempsum = _polyline ? 0 : _areasum();
142 int crossings = _crossings;
143 unsigned num = _num + 1;
144 for (int i = 0; i < (_polyline ? 1 : 2); ++i) {
145 real s12, S12, t;
146 _earth.GenInverse(i == 0 ? _lat1 : lat, i == 0 ? _lon1 : lon,
147 i != 0 ? _lat0 : lat, i != 0 ? _lon0 : lon,
148 _mask, s12, t, t, t, t, t, S12);
149 perimeter += s12;
150 if (!_polyline) {
151 tempsum += S12;
152 crossings += transit(i == 0 ? _lon1 : lon,
153 i != 0 ? _lon0 : lon);
154 }
155 }
156
157 if (_polyline)
158 return num;
159
160 AreaReduce(tempsum, crossings, reverse, sign);
161 area = real(0) + tempsum;
162 return num;
163 }
164
165 template<class GeodType>
166 unsigned PolygonAreaT<GeodType>::TestEdge(real azi, real s,
167 bool reverse, bool sign,
168 real& perimeter, real& area) const
169 {
170 if (_num == 0) { // we don't have a starting point!
171 perimeter = Math::NaN();
172 if (!_polyline)
173 area = Math::NaN();
174 return 0;
175 }
176 unsigned num = _num + 1;
177 perimeter = _perimetersum() + s;
178 if (_polyline)
179 return num;
180
181 real tempsum = _areasum();
182 int crossings = _crossings;
183 {
184 real lat, lon, s12, S12, t;
185 _earth.GenDirect(_lat1, _lon1, azi, false, s, _mask,
186 lat, lon, t, t, t, t, t, S12);
187 tempsum += S12;
188 crossings += transitdirect(_lon1, lon);
189 _earth.GenInverse(lat, lon, _lat0, _lon0, _mask,
190 s12, t, t, t, t, t, S12);
191 perimeter += s12;
192 tempsum += S12;
193 crossings += transit(lon, _lon0);
194 }
195
196 AreaReduce(tempsum, crossings, reverse, sign);
197 area = real(0) + tempsum;
198 return num;
199 }
200
201 template<class GeodType>
202 template<typename T>
203 void PolygonAreaT<GeodType>::AreaReduce(T& area, int crossings,
204 bool reverse, bool sign) const {
205 Remainder(area);
206 if (crossings & 1) area += (area < 0 ? 1 : -1) * _area0/2;
207 // area is with the clockwise sense. If !reverse convert to
208 // counter-clockwise convention.
209 if (!reverse) area *= -1;
210 // If sign put area in (-_area0/2, _area0/2], else put area in [0, _area0)
211 if (sign) {
212 if (area > _area0/2)
213 area -= _area0;
214 else if (area <= -_area0/2)
215 area += _area0;
216 } else {
217 if (area >= _area0)
218 area -= _area0;
219 else if (area < 0)
220 area += _area0;
221 }
222 }
223
224 template class GEOGRAPHICLIB_EXPORT PolygonAreaT<Geodesic>;
225 template class GEOGRAPHICLIB_EXPORT PolygonAreaT<GeodesicExact>;
226 template class GEOGRAPHICLIB_EXPORT PolygonAreaT<Rhumb>;
227
228} // namespace GeographicLib
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:67
GeographicLib::Math::real real
Definition: GeodSolve.cpp:29
Header for GeographicLib::PolygonAreaT class.
An accumulator for sums.
Definition: Accumulator.hpp:40
static T AngNormalize(T x)
Definition: Math.cpp:71
static T NaN()
Definition: Math.cpp:250
static T AngDiff(T x, T y, T &e)
Definition: Math.cpp:82
@ td
degrees per turn
Definition: Math.hpp:142
void AddPoint(real lat, real lon)
Definition: PolygonArea.cpp:69
unsigned Compute(bool reverse, bool sign, real &perimeter, real &area) const
unsigned TestPoint(real lat, real lon, bool reverse, bool sign, real &perimeter, real &area) const
void AddEdge(real azi, real s)
Definition: PolygonArea.cpp:88
unsigned TestEdge(real azi, real s, bool reverse, bool sign, real &perimeter, real &area) const
Namespace for GeographicLib.
Definition: Accumulator.cpp:12