Exemple #1
0
    def __init__(self, earth, polyline=False):
        """Construct a PolygonArea object

    :param earth: a :class:`~geographiclib.geodesic.Geodesic` object
    :param polyline: if true, treat object as a polyline instead of a polygon

    Initially the polygon has no vertices.
    """

        from geographiclib.geodesic import Geodesic
        self.earth = earth
        """The geodesic object (readonly)"""
        self.polyline = polyline
        """Is this a polyline? (readonly)"""
        self.area0 = 4 * math.pi * earth._c2
        """The total area of the ellipsoid in meter^2 (readonly)"""
        self._mask = (Geodesic.LATITUDE | Geodesic.LONGITUDE
                      | Geodesic.DISTANCE |
                      (Geodesic.EMPTY if self.polyline else Geodesic.AREA
                       | Geodesic.LONG_UNROLL))
        if not self.polyline: self._areasum = Accumulator()
        self._perimetersum = Accumulator()
        self.num = 0
        """The current number of points in the polygon (readonly)"""
        self.lat1 = Math.nan
        """The current latitude in degrees (readonly)"""
        self.lon1 = Math.nan
        """The current longitude in degrees (readonly)"""
        self.Clear()
Exemple #2
0
 def __init__(self, earth, polyline = False):
   from geographiclib.geodesic import Geodesic
   self._earth = earth
   self._area0 = 4 * math.pi * earth._c2
   self._polyline = polyline
   self._mask = Geodesic.DISTANCE | (0 if self._polyline else Geodesic.AREA)
   if not self._polyline: self._areasum = Accumulator()
   self._perimetersum = Accumulator()
   self.Clear()
Exemple #3
0
 def __init__(self, earth, polyline=False):
     from geographiclib.geodesic import Geodesic
     self._earth = earth
     self._area0 = 4 * math.pi * earth._c2
     self._polyline = polyline
     self._mask = (Geodesic.LATITUDE | Geodesic.LONGITUDE
                   | Geodesic.DISTANCE |
                   (Geodesic.EMPTY if self._polyline else Geodesic.AREA
                    | Geodesic.LONG_NOWRAP))
     if not self._polyline: self._areasum = Accumulator()
     self._perimetersum = Accumulator()
     self.Clear()
Exemple #4
0
    def Compute(self, reverse=False, sign=True):
        """Compute the properties of the polygon

    :param reverse: if true then clockwise (instead of
      counter-clockwise) traversal counts as a positive area
    :param sign: if true then 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: a tuple of number, perimeter (meters), area (meters^2)

    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 figure-8 polygon will partially
    cancel.

    If the object is a polygon (and not a polyline), the perimeter
    includes the length of a final edge connecting the current point to
    the initial point.  If the object is a polyline, then area is nan.

    More points can be added to the polygon after this call.

    """
        if self.polyline: area = Math.nan
        if self.num < 2:
            perimeter = 0.0
            if not self.polyline: area = 0.0
            return self.num, perimeter, area

        if self.polyline:
            perimeter = self._perimetersum.Sum()
            return self.num, perimeter, area

        _, s12, _, _, _, _, _, _, _, S12 = self.earth._GenInverse(
            self.lat1, self.lon1, self._lat0, self._lon0, self._mask)
        perimeter = self._perimetersum.Sum(s12)
        tempsum = Accumulator(self._areasum)
        tempsum.Add(S12)
        crossings = self._crossings + PolygonArea._transit(
            self.lon1, self._lon0)
        area = PolygonArea._areareduceA(tempsum, self.area0, crossings,
                                        reverse, sign)
        return self.num, perimeter, area
Exemple #5
0
class PolygonArea(object):
    """Area of a geodesic polygon"""
    def _transit(lon1, lon2):
        """Count crossings of prime meridian for AddPoint."""
        # Return 1 or -1 if crossing prime meridian in east or west direction.
        # Otherwise return zero.
        # Compute lon12 the same way as Geodesic::Inverse.
        lon1 = Math.AngNormalize(lon1)
        lon2 = Math.AngNormalize(lon2)
        lon12, _ = Math.AngDiff(lon1, lon2)
        cross = (1 if lon1 <= 0 and lon2 > 0 and lon12 > 0 else
                 (-1 if lon2 <= 0 and lon1 > 0 and lon12 < 0 else 0))
        return cross

    _transit = staticmethod(_transit)

    def _transitdirect(lon1, lon2):
        """Count crossings of prime meridian for AddEdge."""
        # We want to compute exactly
        #   int(floor(lon2 / 360)) - int(floor(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
        lon1 = math.fmod(lon1, 720.0)
        lon2 = math.fmod(lon2, 720.0)
        return ((0 if ((lon2 >= 0 and lon2 < 360) or lon2 < -360) else 1) -
                (0 if ((lon1 >= 0 and lon1 < 360) or lon1 < -360) else 1))

    _transitdirect = staticmethod(_transitdirect)

    def __init__(self, earth, polyline=False):
        """Construct a PolygonArea object

    :param earth: a :class:`~geographiclib.geodesic.Geodesic` object
    :param polyline: if true, treat object as a polyline instead of a polygon

    Initially the polygon has no vertices.
    """

        from geographiclib.geodesic import Geodesic
        self.earth = earth
        """The geodesic object (readonly)"""
        self.polyline = polyline
        """Is this a polyline? (readonly)"""
        self.area0 = 4 * math.pi * earth._c2
        """The total area of the ellipsoid in meter^2 (readonly)"""
        self._mask = (Geodesic.LATITUDE | Geodesic.LONGITUDE
                      | Geodesic.DISTANCE |
                      (Geodesic.EMPTY if self.polyline else Geodesic.AREA
                       | Geodesic.LONG_UNROLL))
        if not self.polyline: self._areasum = Accumulator()
        self._perimetersum = Accumulator()
        self.num = 0
        """The current number of points in the polygon (readonly)"""
        self.lat1 = Math.nan
        """The current latitude in degrees (readonly)"""
        self.lon1 = Math.nan
        """The current longitude in degrees (readonly)"""
        self.Clear()

    def Clear(self):
        """Reset to empty polygon."""
        self.num = 0
        self._crossings = 0
        if not self.polyline: self._areasum.Set(0)
        self._perimetersum.Set(0)
        self._lat0 = self._lon0 = self.lat1 = self.lon1 = Math.nan

    def AddPoint(self, lat, lon):
        """Add the next vertex to the polygon

    :param lat: the latitude of the point in degrees
    :param lon: the longitude of the point in degrees

    This adds an edge from the current vertex to the new vertex.
    """

        if self.num == 0:
            self._lat0 = self.lat1 = lat
            self._lon0 = self.lon1 = lon
        else:
            _, s12, _, _, _, _, _, _, _, S12 = self.earth._GenInverse(
                self.lat1, self.lon1, lat, lon, self._mask)
            self._perimetersum.Add(s12)
            if not self.polyline:
                self._areasum.Add(S12)
                self._crossings += PolygonArea._transit(self.lon1, lon)
            self.lat1 = lat
            self.lon1 = lon
        self.num += 1

    def AddEdge(self, azi, s):
        """Add the next edge to the polygon

    :param azi: the azimuth at the current the point in degrees
    :param s: the length of the edge in meters

    This specifies the new vertex in terms of the edge from the current
    vertex.

    """

        if self.num != 0:
            _, lat, lon, _, _, _, _, _, S12 = self.earth._GenDirect(
                self.lat1, self.lon1, azi, False, s, self._mask)
            self._perimetersum.Add(s)
            if not self.polyline:
                self._areasum.Add(S12)
                self._crossings += PolygonArea._transitdirect(self.lon1, lon)
            self.lat1 = lat
            self.lon1 = lon
            self.num += 1

    # return number, perimeter, area
    def Compute(self, reverse=False, sign=True):
        """Compute the properties of the polygon

    :param reverse: if true then clockwise (instead of
      counter-clockwise) traversal counts as a positive area
    :param sign: if true then 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: a tuple of number, perimeter (meters), area (meters^2)

    If the object is a polygon (and not a polygon), the perimeter
    includes the length of a final edge connecting the current point to
    the initial point.  If the object is a polyline, then area is nan.

    More points can be added to the polygon after this call.

    """
        if self.polyline: area = Math.nan
        if self.num < 2:
            perimeter = 0.0
            if not self.polyline: area = 0.0
            return self.num, perimeter, area

        if self.polyline:
            perimeter = self._perimetersum.Sum()
            return self.num, perimeter, area

        _, s12, _, _, _, _, _, _, _, S12 = self.earth._GenInverse(
            self.lat1, self.lon1, self._lat0, self._lon0, self._mask)
        perimeter = self._perimetersum.Sum(s12)
        tempsum = Accumulator(self._areasum)
        tempsum.Add(S12)
        crossings = self._crossings + PolygonArea._transit(
            self.lon1, self._lon0)
        if crossings & 1:
            tempsum.Add((1 if tempsum.Sum() < 0 else -1) * self.area0 / 2)
        # area is with the clockwise sense.  If !reverse convert to
        # counter-clockwise convention.
        if not reverse: tempsum.Negate()
        # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
        if sign:
            if tempsum.Sum() > self.area0 / 2:
                tempsum.Add(-self.area0)
            elif tempsum.Sum() <= -self.area0 / 2:
                tempsum.Add(self.area0)
        else:
            if tempsum.Sum() >= self.area0:
                tempsum.Add(-self.area0)
            elif tempsum.Sum() < 0:
                tempsum.Add(self.area0)

        area = 0.0 + tempsum.Sum()
        return self.num, perimeter, area

    # return number, perimeter, area
    def TestPoint(self, lat, lon, reverse=False, sign=True):
        """Compute the properties for a tentative additional vertex

    :param lat: the latitude of the point in degrees
    :param lon: the longitude of the point in degrees
    :param reverse: if true then clockwise (instead of
      counter-clockwise) traversal counts as a positive area
    :param sign: if true then 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: a tuple of number, perimeter (meters), area (meters^2)

    """
        if self.polyline: area = Math.nan
        if self.num == 0:
            perimeter = 0.0
            if not self.polyline: area = 0.0
            return 1, perimeter, area

        perimeter = self._perimetersum.Sum()
        tempsum = 0.0 if self.polyline else self._areasum.Sum()
        crossings = self._crossings
        num = self.num + 1
        for i in ([0] if self.polyline else [0, 1]):
            _, s12, _, _, _, _, _, _, _, S12 = self.earth._GenInverse(
                self.lat1 if i == 0 else lat, self.lon1 if i == 0 else lon,
                self._lat0 if i != 0 else lat, self._lon0 if i != 0 else lon,
                self._mask)
            perimeter += s12
            if not self.polyline:
                tempsum += S12
                crossings += PolygonArea._transit(
                    self.lon1 if i == 0 else lon,
                    self._lon0 if i != 0 else lon)

        if self.polyline:
            return num, perimeter, area

        if crossings & 1:
            tempsum += (1 if tempsum < 0 else -1) * self.area0 / 2
        # area is with the clockwise sense.  If !reverse convert to
        # counter-clockwise convention.
        if not reverse: tempsum *= -1
        # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
        if sign:
            if tempsum > self.area0 / 2:
                tempsum -= self.area0
            elif tempsum <= -self.area0 / 2:
                tempsum += self.area0
        else:
            if tempsum >= self.area0:
                tempsum -= self.area0
            elif tempsum < 0:
                tempsum += self.area0

        area = 0.0 + tempsum
        return num, perimeter, area

    # return num, perimeter, area
    def TestEdge(self, azi, s, reverse=False, sign=True):
        """Compute the properties for a tentative additional edge

    :param azi: the azimuth at the current the point in degrees
    :param s: the length of the edge in meters
    :param reverse: if true then clockwise (instead of
      counter-clockwise) traversal counts as a positive area
    :param sign: if true then 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: a tuple of number, perimeter (meters), area (meters^2)

    """

        if self.num == 0:  # we don't have a starting point!
            return 0, Math.nan, Math.nan
        num = self.num + 1
        perimeter = self._perimetersum.Sum() + s
        if self.polyline:
            return num, perimeter, Math.nan

        tempsum = self._areasum.Sum()
        crossings = self._crossings
        _, lat, lon, _, _, _, _, _, S12 = self.earth._GenDirect(
            self.lat1, self.lon1, azi, False, s, self._mask)
        tempsum += S12
        crossings += PolygonArea._transitdirect(self.lon1, lon)
        _, s12, _, _, _, _, _, _, _, S12 = self.earth._GenInverse(
            lat, lon, self._lat0, self._lon0, self._mask)
        perimeter += s12
        tempsum += S12
        crossings += PolygonArea._transit(lon, self._lon0)

        if crossings & 1:
            tempsum += (1 if tempsum < 0 else -1) * self.area0 / 2
        # area is with the clockwise sense.  If !reverse convert to
        # counter-clockwise convention.
        if not reverse: tempsum *= -1
        # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
        if sign:
            if tempsum > self.area0 / 2:
                tempsum -= self.area0
            elif tempsum <= -self.area0 / 2:
                tempsum += self.area0
        else:
            if tempsum >= self.area0:
                tempsum -= self.area0
            elif tempsum < 0:
                tempsum += self.area0

        area = 0.0 + tempsum
        return num, perimeter, area
Exemple #6
0
    def Compute(self, reverse=False, sign=True):
        """Compute the properties of the polygon

    :param reverse: if true then clockwise (instead of
      counter-clockwise) traversal counts as a positive area
    :param sign: if true then 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: a tuple of number, perimeter (meters), area (meters^2)

    If the object is a polygon (and not a polygon), the perimeter
    includes the length of a final edge connecting the current point to
    the initial point.  If the object is a polyline, then area is nan.

    More points can be added to the polygon after this call.

    """
        if self.polyline: area = Math.nan
        if self.num < 2:
            perimeter = 0.0
            if not self.polyline: area = 0.0
            return self.num, perimeter, area

        if self.polyline:
            perimeter = self._perimetersum.Sum()
            return self.num, perimeter, area

        _, s12, _, _, _, _, _, _, _, S12 = self.earth._GenInverse(
            self.lat1, self.lon1, self._lat0, self._lon0, self._mask)
        perimeter = self._perimetersum.Sum(s12)
        tempsum = Accumulator(self._areasum)
        tempsum.Add(S12)
        crossings = self._crossings + PolygonArea._transit(
            self.lon1, self._lon0)
        if crossings & 1:
            tempsum.Add((1 if tempsum.Sum() < 0 else -1) * self.area0 / 2)
        # area is with the clockwise sense.  If !reverse convert to
        # counter-clockwise convention.
        if not reverse: tempsum.Negate()
        # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
        if sign:
            if tempsum.Sum() > self.area0 / 2:
                tempsum.Add(-self.area0)
            elif tempsum.Sum() <= -self.area0 / 2:
                tempsum.Add(self.area0)
        else:
            if tempsum.Sum() >= self.area0:
                tempsum.Add(-self.area0)
            elif tempsum.Sum() < 0:
                tempsum.Add(self.area0)

        area = 0.0 + tempsum.Sum()
        return self.num, perimeter, area
Exemple #7
0
    def Compute(self, reverse, sign):
        """Return the number, perimeter, and area for the polygon."""
        if self._polyline: area = Math.nan
        if self._num < 2:
            perimeter = 0
            if not self._polyline: area = 0
            return self._num, perimeter, area

        if self._polyline:
            perimeter = self._perimetersum.Sum()
            return self._num, perimeter, area

        _, s12, _, _, _, _, _, S12 = self._earth.GenInverse(
            self._lat1, self._lon1, self._lat0, self._lon0, self._mask)
        perimeter = self._perimetersum.Sum(s12)
        tempsum = Accumulator(self._areasum)
        tempsum.Add(S12)
        crossings = self._crossings + PolygonArea.transit(
            self._lon1, self._lon0)
        if crossings & 1:
            tempsum.Add((1 if tempsum < 0 else -1) * self._area0 / 2)
        # area is with the clockwise sense.  If !reverse convert to
        # counter-clockwise convention.
        if not reverse: tempsum.Negate()
        # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
        if sign:
            if tempsum.Sum() > self._area0 / 2:
                tempsum.Add(-self._area0)
            elif tempsum.Sum() <= -self._area0 / 2:
                tempsum.Add(self._area0)
        else:
            if tempsum.Sum() >= self._area0:
                tempsum.Add(-self._area0)
            elif tempsum.Sum() < 0:
                tempsum.Add(self._area0)

        area = 0 + tempsum.Sum()
        return self._num, perimeter, area
Exemple #8
0
class PolygonArea(object):
    """Area of a geodesic polygon"""
    def transit(lon1, lon2):
        """Count crossings of prime meridian."""
        # Return 1 or -1 if crossing prime meridian in east or west direction.
        # Otherwise return zero.
        # Compute lon12 the same way as Geodesic::Inverse.
        lon1 = Math.AngNormalize(lon1)
        lon2 = Math.AngNormalize(lon2)
        lon12 = Math.AngDiff(lon1, lon2)
        cross = (1 if lon1 < 0 and lon2 >= 0 and lon12 > 0 else
                 (-1 if lon2 < 0 and lon1 >= 0 and lon12 < 0 else 0))
        return cross

    transit = staticmethod(transit)

    def __init__(self, earth, polyline=False):
        from geographiclib.geodesic import Geodesic
        self._earth = earth
        self._area0 = 4 * math.pi * earth._c2
        self._polyline = polyline
        self._mask = (Geodesic.LATITUDE | Geodesic.LONGITUDE
                      | Geodesic.DISTANCE |
                      (Geodesic.EMPTY if self._polyline else Geodesic.AREA))
        if not self._polyline: self._areasum = Accumulator()
        self._perimetersum = Accumulator()
        self.Clear()

    def Clear(self):
        """Reset to empty polygon."""
        self._num = 0
        self._crossings = 0
        if not self._polyline: self._areasum.Set(0)
        self._perimetersum.Set(0)
        self._lat0 = self._lon0 = self._lat1 = self._lon1 = Math.nan

    def AddPoint(self, lat, lon):
        """Add a vertex to the polygon."""
        if self._num == 0:
            self._lat0 = self._lat1 = lat
            self._lon0 = self._lon1 = lon
        else:
            _, s12, _, _, _, _, _, S12 = self._earth.GenInverse(
                self._lat1, self._lon1, lat, lon, self._mask)
            self._perimetersum.Add(s12)
            if not self._polyline:
                self._areasum.Add(S12)
                self._crossings += PolygonArea.transit(self._lon1, lon)
            self._lat1 = lat
            self._lon1 = lon
        self._num += 1

    def AddEdge(self, azi, s):
        """Add an edge to the polygon."""
        if self._num != 0:
            _, lat, lon, _, _, _, _, _, S12 = self._earth.GenDirect(
                self._lat1, self._lon1, azi, False, s, self._mask)
            self._perimetersum.Add(s)
            if not self._polyline:
                self._areasum.Add(S12)
                self._crossings += PolygonArea.transit(self._lon1, lon)
            self._lat1 = lat
            self._lon1 = lon
            self._num += 1

    # return number, perimeter, area
    def Compute(self, reverse, sign):
        """Return the number, perimeter, and area for the polygon."""
        if self._polyline: area = Math.nan
        if self._num < 2:
            perimeter = 0
            if not self._polyline: area = 0
            return self._num, perimeter, area

        if self._polyline:
            perimeter = self._perimetersum.Sum()
            return self._num, perimeter, area

        _, s12, _, _, _, _, _, S12 = self._earth.GenInverse(
            self._lat1, self._lon1, self._lat0, self._lon0, self._mask)
        perimeter = self._perimetersum.Sum(s12)
        tempsum = Accumulator(self._areasum)
        tempsum.Add(S12)
        crossings = self._crossings + PolygonArea.transit(
            self._lon1, self._lon0)
        if crossings & 1:
            tempsum.Add((1 if tempsum < 0 else -1) * self._area0 / 2)
        # area is with the clockwise sense.  If !reverse convert to
        # counter-clockwise convention.
        if not reverse: tempsum.Negate()
        # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
        if sign:
            if tempsum.Sum() > self._area0 / 2:
                tempsum.Add(-self._area0)
            elif tempsum.Sum() <= -self._area0 / 2:
                tempsum.Add(self._area0)
        else:
            if tempsum.Sum() >= self._area0:
                tempsum.Add(-self._area0)
            elif tempsum.Sum() < 0:
                tempsum.Add(self._area0)

        area = 0 + tempsum.Sum()
        return self._num, perimeter, area

    # return number, perimeter, area
    def TestPoint(self, lat, lon, reverse, sign):
        """Return the results for a tentative additional vertex."""
        if self._polyline: area = Math.nan
        if self._num == 0:
            perimeter = 0
            if not self._polyline: area = 0
            return 1, perimeter, area

        perimeter = self._perimetersum.Sum()
        tempsum = 0 if self._polyline else self._areasum.Sum()
        crossings = self._crossings
        num = self._num + 1
        for i in ([0] if self._polyline else [0, 1]):
            _, s12, _, _, _, _, _, S12 = self._earth.GenInverse(
                self._lat1 if i == 0 else lat, self._lon1 if i == 0 else lon,
                self._lat0 if i != 0 else lat, self._lon0 if i != 0 else lon,
                self._mask)
            perimeter += s12
            if not self._polyline:
                tempsum += S12
                crossings += PolygonArea.transit(self._lon1 if i == 0 else lon,
                                                 self._lon0 if i != 0 else lon)

        if self._polyline:
            return num, perimeter, area

        if crossings & 1:
            tempsum += (1 if tempsum < 0 else -1) * self._area0 / 2
        # area is with the clockwise sense.  If !reverse convert to
        # counter-clockwise convention.
        if not reverse: tempsum *= -1
        # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
        if sign:
            if tempsum > self._area0 / 2:
                tempsum -= self._area0
            elif tempsum <= -self._area0 / 2:
                tempsum += self._area0
        else:
            if tempsum >= self._area0:
                tempsum -= self._area0
            elif tempsum < 0:
                tempsum += self._area0

        area = 0 + tempsum
        return num, perimeter, area

    # return number, perimeter, area (for backward compatibility)
    def TestCompute(self, lat, lon, reverse, sign):
        return self.TestPoint(lat, lon, reverse, sign)

    # return num, perimeter, area
    def TestEdge(self, azi, s, reverse, sign):
        """Return the results for a tentative additional edge."""
        if self._num == 0:  # we don't have a starting point!
            return 0, Math.nan, Math.nan
        num = self._num + 1
        perimeter = self._perimetersum.Sum() + s
        if self._polyline:
            return num, perimeter, Math.nan

        tempsum = self._areasum.Sum()
        crossings = self._crossings
        _, lat, lon, _, _, _, _, _, S12 = self._earth.GenDirect(
            self._lat1, self._lon1, azi, False, s, self._mask)
        tempsum += S12
        crossings += PolygonArea.transit(self._lon1, lon)
        _, s12, _, _, _, _, _, S12 = self._earth.GenInverse(
            lat, lon, self._lat0, self._lon0, self._mask)
        perimeter += s12
        tempsum += S12
        crossings += PolygonArea.transit(lon, self._lon0)

        if crossings & 1:
            tempsum += (1 if tempsum < 0 else -1) * self._area0 / 2
        # area is with the clockwise sense.  If !reverse convert to
        # counter-clockwise convention.
        if not reverse: tempsum *= -1
        # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
        if sign:
            if tempsum > self._area0 / 2:
                tempsum -= self._area0
            elif tempsum <= -self._area0 / 2:
                tempsum += self._area0
        else:
            if tempsum >= self._area0:
                tempsum -= self._area0
            elif tempsum < 0:
                tempsum += self._area0

        area = 0 + tempsum
        return num, perimeter, area

    def CurrentPoint(self):
        """Return the current point as a lat, lon tuple."""
        return self._lat1, self._lon1

    def Area(earth, points, polyline):
        """Return the number, perimeter, and area for a set of vertices."""
        poly = PolygonArea(earth, polyline)
        for p in points:
            poly.AddPoint(p['lat'], p['lon'])
        return poly.Compute(False, True)

    Area = staticmethod(Area)
Exemple #9
0
class PolygonArea(object):
  """Area of a geodesic polygon"""

  def transit(lon1, lon2):
    # Return 1 or -1 if crossing prime meridian in east or west direction.
    # Otherwise return zero.
    from geographiclib.geodesic import Geodesic
    # Compute lon12 the same way as Geodesic::Inverse.
    lon1 = Math.AngNormalize(lon1);
    lon2 = Math.AngNormalize(lon2);
    lon12 = Math.AngDiff(lon1, lon2);
    cross = (1 if lon1 < 0 and lon2 >= 0 and lon12 > 0
             else (-1 if lon2 < 0 and lon1 >= 0 and lon12 < 0 else 0))
    return cross
  transit = staticmethod(transit)

  def __init__(self, earth, polyline = False):
    from geographiclib.geodesic import Geodesic
    self._earth = earth
    self._area0 = 4 * math.pi * earth._c2
    self._polyline = polyline
    self._mask = Geodesic.DISTANCE | (0 if self._polyline else Geodesic.AREA)
    if not self._polyline: self._areasum = Accumulator()
    self._perimetersum = Accumulator()
    self.Clear()

  def Clear(self):
    self._num = 0
    self._crossings = 0
    if not self._polyline: self._areasum.Set(0)
    self._perimetersum.Set(0)
    self._lat0 = self._lon0 = self._lat1 = self._lon1 = 0

  def AddPoint(self, lat, lon):
    if self._num == 0:
      self._lat0 = self._lat1 = lat
      self._lon0 = self._lon1 = lon
    else:
      t, s12, t, t, t, t, t, S12 = self._earth.GenInverse(
        self._lat1, self._lon1, lat, lon, self._mask)
      self._perimetersum.Add(s12)
      if not self._polyline:
        self._areasum.Add(S12)
        self._crossings += PolygonArea.transit(self._lon1, lon)
      self._lat1 = lat
      self._lon1 = lon
    self._num += 1

  # return number, perimeter, area
  def Compute(self, reverse, sign):
    if self._polyline: area = Math.nan
    if self._num < 2:
      perimeter = 0
      if not self._polyline: area = 0
      return self._num, perimeter, area

    if self._polyline:
      perimeter = self._perimetersum.Sum()
      return self._num, perimeter, area

    t, s12, t, t, t, t, t, S12 = self._earth.GenInverse(
      self._lat1, self._lon1, self._lat0, self._lon0, self._mask)
    perimeter = self._perimetersum.Sum(s12)
    tempsum = Accumulator(self._areasum)
    tempsum.Add(S12)
    crossings = self._crossings + PolygonArea.transit(self._lon1, self._lon0)
    if crossings & 1:
      tempsum.Add( (1 if tempsum < 0 else -1) * self._area0/2 )
    # area is with the clockwise sense.  If !reverse convert to
    # counter-clockwise convention.
    if not reverse: tempsum.Negate()
    # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
    if sign:
      if tempsum.Sum() > self._area0/2:
        tempsum.Add( -self._area0 )
      elif tempsum.Sum() <= -self._area0/2:
        tempsum.Add( +self._area0 )
    else:
      if tempsum.Sum() >= self._area0:
        tempsum.Add( -self._area0 )
      elif tempsum.Sum() < 0:
        tempsum.Add( +self._area0 )

    area = 0 + tempsum.Sum()
    return self._num, perimeter, area

  # return number, perimeter, area
  def TestCompute(self, lat, lon, reverse, sign):
    if self._polyline: area = Math.nan
    if self._num == 0:
      perimeter = 0
      if not self._polyline: area = 0
      return 1, perimeter, area

    perimeter = self._perimetersum.Sum()
    tempsum = 0 if self._polyline else self._areasum.Sum()
    crossings = self._crossings; num = self._num + 1
    for i in ([0] if self._polyline else [0, 1]):
      t, s12, t, t, t, t, t, S12 = self._earth.GenInverse(
        self._lat1 if i == 0 else lat, self._lon1 if i == 0 else lon,
        self._lat0 if i != 0 else lat, self._lon0 if i != 0 else lon,
        self._mask)
      perimeter += s12
      if not self._polyline:
        tempsum += S12
        crossings += PolygonArea.transit(self._lon1 if i == 0 else lon,
                                         self._lon0 if i != 0 else lon)

    if self._polyline:
      return num, perimeter, area

    if crossings & 1:
      tempsum += (1 if tempsum < 0 else -1) * self._area0/2
    # area is with the clockwise sense.  If !reverse convert to
    # counter-clockwise convention.
    if not reverse: tempsum *= -1
    # If sign put area in (-area0/2, area0/2], else put area in [0, area0)
    if sign:
      if tempsum > self._area0/2:
        tempsum -= self._area0
      elif tempsum <= -self._area0/2:
        tempsum += self._area0
    else:
      if tempsum >= self._area0:
        tempsum -= self._area0
      elif tempsum < 0:
        tempsum += self._area0

    area = 0 + tempsum
    return num, perimeter, area

  def Area(earth, points, polyline):
    poly = PolygonArea(earth, polyline)
    for p in points:
      poly.AddPoint(p['lat'], p['lon'])
    return poly.Compute(False, True)
  Area = staticmethod(Area)