1 """Define the :class:`~geographiclib.polygonarea.PolygonArea` class
3 The constructor initializes a empty polygon. The available methods are
5 * :meth:`~geographiclib.polygonarea.PolygonArea.Clear` reset the
7 * :meth:`~geographiclib.polygonarea.PolygonArea.AddPoint` add a vertex
9 * :meth:`~geographiclib.polygonarea.PolygonArea.AddEdge` add an edge
11 * :meth:`~geographiclib.polygonarea.PolygonArea.Compute` compute the
12 properties of the polygon
13 * :meth:`~geographiclib.polygonarea.PolygonArea.TestPoint` compute the
14 properties of the polygon with a tentative additional vertex
15 * :meth:`~geographiclib.polygonarea.PolygonArea.TestEdge` compute the
16 properties of the polygon with a tentative additional edge
18 The public attributes for this class are
20 * :attr:`~geographiclib.polygonarea.PolygonArea.earth`
21 :attr:`~geographiclib.polygonarea.PolygonArea.polyline`
22 :attr:`~geographiclib.polygonarea.PolygonArea.area0`
23 :attr:`~geographiclib.polygonarea.PolygonArea.num`
24 :attr:`~geographiclib.polygonarea.PolygonArea.lat1`
25 :attr:`~geographiclib.polygonarea.PolygonArea.lon1`
52 """Area of a geodesic polygon"""
55 """Count crossings of prime meridian for AddPoint."""
59 lon1 = Math.AngNormalize(lon1)
60 lon2 = Math.AngNormalize(lon2)
61 lon12, _ = Math.AngDiff(lon1, lon2)
62 cross = (1
if lon1 <= 0
and lon2 > 0
and lon12 > 0
63 else (-1
if lon2 <= 0
and lon1 > 0
and lon12 < 0
else 0))
68 """Count crossings of prime meridian for AddEdge."""
73 lon1 = math.fmod(lon1, 720.0); lon2 = math.fmod(lon2, 720.0)
74 return ( (0
if ((lon2 >= 0
and lon2 < 360)
or lon2 < -360)
else 1) -
75 (0
if ((lon1 >= 0
and lon1 < 360)
or lon1 < -360)
else 1) )
79 """Construct a PolygonArea object
81 :param earth: a :class:`~geographiclib.geodesic.Geodesic` object
82 :param polyline: if true, treat object as a polyline instead of a polygon
84 Initially the polygon has no vertices.
89 """The geodesic object (readonly)"""
91 """Is this a polyline? (readonly)"""
92 self.
area0 = 4 * math.pi * earth._c2
93 """The total area of the ellipsoid in meter^2 (readonly)"""
94 self.
_mask = (Geodesic.LATITUDE | Geodesic.LONGITUDE |
96 (Geodesic.EMPTY
if self.
polyline else
97 Geodesic.AREA | Geodesic.LONG_UNROLL))
101 """The current number of points in the polygon (readonly)"""
103 """The current latitude in degrees (readonly)"""
105 """The current longitude in degrees (readonly)"""
109 """Reset to empty polygon."""
117 """Add the next vertex to the polygon
119 :param lat: the latitude of the point in degrees
120 :param lon: the longitude of the point in degrees
122 This adds an edge from the current vertex to the new vertex.
129 _, s12, _, _, _, _, _, _, _, S12 = self.
earth._GenInverse(
140 """Add the next edge to the polygon
142 :param azi: the azimuth at the current the point in degrees
143 :param s: the length of the edge in meters
145 This specifies the new vertex in terms of the edge from the current
151 _, lat, lon, _, _, _, _, _, S12 = self.
earth._GenDirect(
162 def Compute(self, reverse = False, sign = True):
163 """Compute the properties of the polygon
165 :param reverse: if true then clockwise (instead of
166 counter-clockwise) traversal counts as a positive area
167 :param sign: if true then return a signed result for the area if the
168 polygon is traversed in the "wrong" direction instead of returning
169 the area for the rest of the earth
170 :return: a tuple of number, perimeter (meters), area (meters^2)
172 If the object is a polygon (and not a polygon), the perimeter
173 includes the length of a final edge connecting the current point to
174 the initial point. If the object is a polyline, then area is nan.
176 More points can be added to the polygon after this call.
183 return self.
num, perimeter, area
187 return self.
num, perimeter, area
189 _, s12, _, _, _, _, _, _, _, S12 = self.
earth._GenInverse(
196 tempsum.Add( (1
if tempsum.Sum() < 0
else -1) * self.
area0/2 )
199 if not reverse: tempsum.Negate()
202 if tempsum.Sum() > self.
area0/2:
203 tempsum.Add( -self.
area0 )
204 elif tempsum.Sum() <= -self.
area0/2:
205 tempsum.Add( self.
area0 )
207 if tempsum.Sum() >= self.
area0:
208 tempsum.Add( -self.
area0 )
209 elif tempsum.Sum() < 0:
210 tempsum.Add( self.
area0 )
212 area = 0.0 + tempsum.Sum()
213 return self.
num, perimeter, area
216 def TestPoint(self, lat, lon, reverse = False, sign = True):
217 """Compute the properties for a tentative additional vertex
219 :param lat: the latitude of the point in degrees
220 :param lon: the longitude of the point in degrees
221 :param reverse: if true then clockwise (instead of
222 counter-clockwise) traversal counts as a positive area
223 :param sign: if true then return a signed result for the area if the
224 polygon is traversed in the "wrong" direction instead of returning
225 the area for the rest of the earth
226 :return: a tuple of number, perimeter (meters), area (meters^2)
233 return 1, perimeter, area
238 for i
in ([0]
if self.
polyline else [0, 1]):
239 _, s12, _, _, _, _, _, _, _, S12 = self.
earth._GenInverse(
240 self.
lat1 if i == 0
else lat, self.
lon1 if i == 0
else lon,
241 self.
_lat0 if i != 0
else lat, self.
_lon0 if i != 0
else lon,
246 crossings += PolygonArea._transit(self.
lon1 if i == 0
else lon,
247 self.
_lon0 if i != 0
else lon)
250 return num, perimeter, area
253 tempsum += (1
if tempsum < 0
else -1) * self.
area0/2
256 if not reverse: tempsum *= -1
259 if tempsum > self.
area0/2:
260 tempsum -= self.
area0
261 elif tempsum <= -self.
area0/2:
262 tempsum += self.
area0
264 if tempsum >= self.
area0:
265 tempsum -= self.
area0
267 tempsum += self.
area0
270 return num, perimeter, area
273 def TestEdge(self, azi, s, reverse = False, sign = True):
274 """Compute the properties for a tentative additional edge
276 :param azi: the azimuth at the current the point in degrees
277 :param s: the length of the edge in meters
278 :param reverse: if true then clockwise (instead of
279 counter-clockwise) traversal counts as a positive area
280 :param sign: if true then return a signed result for the area if the
281 polygon is traversed in the "wrong" direction instead of returning
282 the area for the rest of the earth
283 :return: a tuple of number, perimeter (meters), area (meters^2)
288 return 0, Math.nan, Math.nan
292 return num, perimeter, Math.nan
296 _, lat, lon, _, _, _, _, _, S12 = self.
earth._GenDirect(
299 crossings += PolygonArea._transitdirect(self.
lon1, lon)
300 _, s12, _, _, _, _, _, _, _, S12 = self.
earth._GenInverse(
304 crossings += PolygonArea._transit(lon, self.
_lon0)
307 tempsum += (1
if tempsum < 0
else -1) * self.
area0/2
310 if not reverse: tempsum *= -1
313 if tempsum > self.
area0/2:
314 tempsum -= self.
area0
315 elif tempsum <= -self.
area0/2:
316 tempsum += self.
area0
318 if tempsum >= self.
area0:
319 tempsum -= self.
area0
321 tempsum += self.
area0
324 return num, perimeter, area