How to calculate the area of a polygon on the earth's surface using python?

前端 未结 9 513
生来不讨喜
生来不讨喜 2020-11-29 19:09

The title basically says it all. I need to calculate the area inside a polygon on the Earth\'s surface using Python. Calculating area enclosed by arbitrary polygon on Earth&

9条回答
  •  庸人自扰
    2020-11-29 19:37

    You can compute the area directly on the sphere, instead of using an equal-area projection.

    Moreover, according to this discussion, it seems that Girard's theorem (sulkeh's answer) does not give accurate results in certain cases, for example "the area enclosed by a 30º lune from pole to pole and bounded by the prime meridian and 30ºE" (see here).

    A more precise solution would be to perform line integral directly on the sphere. The comparison below shows this method is more precise.

    Like all other answers, I should mention the caveat that we assume a spherical earth, but I assume that for non-critical purposes this is enough.

    Python implementation

    Here is a Python 3 implementation which uses line integral and Green's theorem:

    def polygon_area(lats, lons, radius = 6378137):
        """
        Computes area of spherical polygon, assuming spherical Earth. 
        Returns result in ratio of the sphere's area if the radius is specified.
        Otherwise, in the units of provided radius.
        lats and lons are in degrees.
        """
        from numpy import arctan2, cos, sin, sqrt, pi, power, append, diff, deg2rad
        lats = np.deg2rad(lats)
        lons = np.deg2rad(lons)
    
        # Line integral based on Green's Theorem, assumes spherical Earth
    
        #close polygon
        if lats[0]!=lats[-1]:
            lats = append(lats, lats[0])
            lons = append(lons, lons[0])
    
        #colatitudes relative to (0,0)
        a = sin(lats/2)**2 + cos(lats)* sin(lons/2)**2
        colat = 2*arctan2( sqrt(a), sqrt(1-a) )
    
        #azimuths relative to (0,0)
        az = arctan2(cos(lats) * sin(lons), sin(lats)) % (2*pi)
    
        # Calculate diffs
        # daz = diff(az) % (2*pi)
        daz = diff(az)
        daz = (daz + pi) % (2 * pi) - pi
    
        deltas=diff(colat)/2
        colat=colat[0:-1]+deltas
    
        # Perform integral
        integrands = (1-cos(colat)) * daz
    
        # Integrate 
        area = abs(sum(integrands))/(4*pi)
    
        area = min(area,1-area)
        if radius is not None: #return in units of radius
            return area * 4*pi*radius**2
        else: #return in ratio of sphere total area
            return area
    

    I wrote a somewhat more explicit version (and with many more references and TODOs...) in the sphericalgeometry package there.

    Numerical Comparison

    Colorado will be the reference, since all previous answers were evaluated on its area. Its precise total area is 104,093.67 square miles (from the US Census Bureau, p. 89, see also here), or 269601367661 square meters. I found no source for the actual methodology of the USCB, but I assume it is based on summing actual measurements on ground, or precise computations using WGS84/EGM2008.

    Method                 | Author     | Result       | Variation from ground truth
    --------------------------------------------------------------------------------
    Albers Equal Area      | sgillies   | 268952044107 | -0.24%
    Sinusoidal             | J. Kington | 268885360163 | -0.26%
    Girard's theorem       | sulkeh     | 268930758560 | -0.25%
    Equal Area Cylindrical | Jason      | 268993609651 | -0.22%
    Line integral          | Yellows    | 269397764066 | **-0.07%**
    

    Conclusion: using direct integral is more precise.

    Performance

    I have not benchmarked the different methods, and comparing pure Python code with compiled PROJ projections would not be meaningful. Intuitively less computations are needed. On the other hand, trigonometric functions may be computationally intensive.

提交回复
热议问题