Coverage for pygeodesy/geodesicx/gxarea.py : 95%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*-
U{PolygonArea<https://GeographicLib.SourceForge.io/1.52/python/ code.html#module-geographiclib.polygonarea>} and C{Accumulator}.
Class L{GeodesicAreaExact} is intended to work with instances of class L{GeodesicExact} or of I{wrapped} class C{Geodesic} I{Karney}'s Python U{geographiclib <https://GeographicLib.SourceForge.io/1.52/python/index.html>}
Copyright (C) Charles Karney (2012-2021) <Charles@Karney.com> and licensed under the MIT/X11 License. For more information, see U{GeographicLib<https://GeographicLib.SourceForge.io>}. ''' # make sure int/int division yields float quotient
# from pygeodesy.basics import isodd, unsigned0 # from .karney _norm180, _remainder, _sum2_, unsigned0 # from pygeodesy.streprs import pairs # from .named
'''Area and perimeter of a geodesic polygon, an enhanced version of I{Karney}'s Python class U{PolygonArea <https://GeographicLib.SourceForge.io/html/python/ code.html#module-geographiclib.polygonarea>} using the more accurate surface area.
@note: The name of this class C{*Exact} is a misnomer, see I{Karney}'s comments at C++ attribute U{GeodesicExact._c2 <https://GeographicLib.SourceForge.io/html/ GeodesicExact_8cpp_source.html>}. ''' _lat1 = _lon1 = NAN
'''New L{GeodesicAreaExact} instance.
@arg geodesic: A geodesic (L{GeodesicExact}, I{wrapped} C{Geodesic} or L{GeodesicSolve}). @kwarg polyline: If C{True} compute perimeter only, otherwise area and perimeter (C{bool}). @kwarg name: Optional name (C{str}).
@raise GeodesicError: Invalid B{C{geodesic}}. ''' callable(geodesic._GDictInverse)): raise TypeError except (AttributeError, TypeError): raise GeodesicError(geodesic=geodesic)
# use the class-level Caps since the values # differ between GeodesicExact and Geodesic self.verbose = True
'''Add another polygon edge.
@arg azi: Azimuth at the current point (compass C{degrees}). @arg s: Length of the edge (C{meter}). ''' raise GeodesicError(num=self.num) else: a = _0_0 if self.verbose: # PYCHOK no cover self._print(self.num, p, a, r, lat1=r.lat2, lon1=r.lon2, azi=azi, s=s)
'''Add another polygon point.
@arg lat: Latitude of the point (C{degrees}). @arg lon: Longitude of the point (C{degrees}). ''' else: else: if self.verbose: # PYCHOK no cover self._print(self.num, p, a, r, lat1=lat, lon1=lon, s=s)
'''Get the ellipsoid's surface area (C{meter} I{squared}), more accurate for very I{oblate} ellipsoids. '''
'''Compute the accumulated perimeter and area.
@kwarg reverse: If C{True}, clockwise traversal counts as a positive area instead of counter-clockwise (C{bool}). @kwarg sign: If C{True}, return a signed result for the area if the polygon is traversed in the "wrong" direction instead of returning the area for the rest of the earth.
@return: 3-Tuple C{(number, perimeter (meter), area (meter*2))}. The C{perimeter} includes the length of a final edge, connecting the current to the initial point, if this polygon was initialized with C{polyline=False}. And C{area=NAN} for perimeter only, C{polyline=True}.
@note: Arbitrarily complex polygons are allowed. In the case of self-intersecting polygons, the area is accumulated "algebraically". E.g., the areas of the 2 loops in a I{figure-8} polygon will partially cancel.
@note: More points and edges can be added after this call. ''' else: if self.verbose: # PYCHOK no cover self._print(n, p, a, r, lat0=self.lat0, lon0=self.lon0)
'''(INTERNAL) Edge helper. ''' # Count crossings of prime meridian exactly as # int(ceil(lon2 / 360)) - int(ceil(lon1 / 360)) # Since we only need the parity of the result we # can use std::remquo but this is buggy with g++ # 4.8.3 and requires C++11. So instead we do: # int(True) == 1, int(False) == 0 int(lon1 > 360 or -360 < lon1 <= 0))
'''Get this area's ellipsoid (C{Ellipsoid[2]}). '''
'''Get this area's geodesic object (C{Geodesic[Exact]}). '''
'''(INTERNAL) Point helper. ''' # count crossings of prime meridian as +1 or -1 # if in east or west direction, otherwise 0 -int(lon2 <= 0 and lon1 > 0 and lon12 < 0))
'''Get the first point's latitude (C{degrees}). '''
'''Get the most recent point's latitude (C{degrees}). '''
'''Get the first point's longitude (C{degrees}). '''
'''Get the most recent point's longitude (C{degrees}). '''
'''Get the current number of points (C{int}). '''
'''Is this perimter only (C{boo}), area NAN? '''
def _print(self, n, p, a, r, **kwds): # PYCHOK no cover '''(INTERNAL) Print a verbose line. ''' d = _Dict(p=p, s12=r.s12 if r else NAN, **kwds) if self._Area: d.set_(a=a, S12=r.S12 if r else NAN) t = _COMMASPACE_.join(pairs(d, prec=10)) printf('%s %s: %s (%s)', self.named2, n, t, callername(up=2))
'''(INTERNAL) Reduce accumulated area to allowed range. ''' # area is with the clockwise sense. If !reverse # convert to counter-clockwise convention. # If sign put area in (-area0x/2, area0x/2], # otherwise put area in [0, area0x) a = Area.Add(-a0) a = Area.Add( a0)
'''Reset this polygon to empty. ''' self._lat1 = self._lon1 = NAN if self.verbose: # PYCHOK no cover printf('%s %s: (%s)', self.named2, n, self.Reset.__name__)
'''Compute the properties for a tentative, additional edge
@arg azi: Azimuth at the current the point (compass C{degrees}). @arg s: Length of the edge (C{meter}). @kwarg reverse: If C{True}, clockwise traversal counts as a positive area instead of counter-clockwise (C{bool}). @kwarg sign: If C{True}, return a signed result for the area if the polygon is traversed in the "wrong" direction instead of returning the area for the rest of the earth.
@return: 3-Tuple C{(number, perimeter (meter), area (meter*2))}.
@raise GeodesicError: No points. ''' a, r = NAN, None raise GeodesicError(num=self.num) else: if self.verbose: # PYCHOK no cover self._print(n, p, a, r, azi=azi, s=s)
'''Compute the properties for a tentative, additional vertex
@arg lat: Latitude of the point (C{degrees}). @arg lon: Longitude of the point (C{degrees}). @kwarg reverse: If C{True}, clockwise traversal counts as a positive area instead of counter-clockwise (C{bool}). @kwarg sign: If C{True}, return a signed result for the area if the polygon is traversed in the "wrong" direction instead of returning the area for the rest of the earth.
@return: 3-Tuple C{(number, perimeter (meter), area (meter*2))}. ''' else: else: a, r = NAN, None if self.verbose: # PYCHOK no cover self._print(n, p, a, r, lat=lat, lon=lon)
'''Return this C{GeodesicExactArea} as string.
@kwarg prec: The C{float} precision, number of decimal digits (0..9). Trailing zero decimals are stripped for B{C{prec}} values of 1 and above, but kept for negative B{C{prec}} values. @kwarg sep: Separator to join (C{str}).
@return: Area items (C{str}). ''' perimeter=p, polyline=self.polyline)
'''Get the C{verbose} option (C{bool}). '''
'''Set the C{verbose} option.
@arg verbose: Print a message after each method invokation (C{bool}). ''' self._verbose = bool(verbose)
'''For C{geographiclib} compatibility, sub-class of L{GeodesicAreaExact}. ''' '''New L{PolygonArea} instance.
@arg earth: A geodesic (L{GeodesicExact}, I{wrapped} C{Geodesic} or L{GeodesicSolve}). @kwarg polyline: If C{True} perimeter only, otherwise area and perimeter (C{bool}). @kwarg name: Optional name (C{str}).
@raise GeodesicError: Invalid B{C{earth}}. ''' GeodesicAreaExact.__init__(self, earth, polyline=polyline, name=name)
'''Like C{math.fsum}, but allowing a running sum.
Original from I{Karney}'s U{geographiclib <https://PyPI.org/project/geographiclib>}C{.accumulator}, enhanced to return the current sum by most methods. '''
'''New L{_Accumulator}. '''
'''Add a value.
@return: Current C{sum}. '''
'''Negate sum.
@return: Current C{sum}. '''
'''Get the current number of C{Add}itions (C{int}). ''' return self._n
'''Remainder on division by B{C{y}}.
@return: Current C{sum} / B{C{y}}. '''
'''Set value from argument. '''
'''Return C{sum + B{y}}.
@note: B{C{y}} is included in the returned result, but I{not} accumulated. ''' else:
'''Return this C{_Accumulator} as string.
@kwarg prec: The C{float} precision, number of decimal digits (0..9). Trailing zero decimals are stripped for B{C{prec}} values of 1 and above, but kept for negative B{C{prec}} values. @kwarg sep: Separator to join (C{str}).
@return: Accumulator (C{str}). ''' d = dict(n=self.num, s=self._s, t=self._t) return sep.join(pairs(d, prec=prec))
# **) MIT License # # Copyright (C) 2016-2022 -- mrJean1 at Gmail -- All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. |