简体   繁体   中英

Area elements in geographic coordinates in Python

I have a need to do some integration/summation over gridded weather data (rainfall), in standard geographic coordinates. I therefore need to calculate area elements associated with each of my grid points. Basically the real areas of the grid cells. We're talking a standard, equally-spaced, rectilinear lat/lon grid here.

How can I do this in Python?

I know I can get them reasonably accurately just using a spherical coordinates approximation, but this has to be super standard, so might as well use a package with a proper Earth shape model in it. However I've searched around a lot and not found much on how to do this basic task in the common geospatial packages. Seems like it's generally hidden under the hood or something, handled internally. I'm sure it can be dug out somehow though.

Edit: A hand-crafted solution is here: How to calculate size in m2 of each lat/long grid square , but I'd rather use a standard library if possible.

See pyproj.Geod.inv , that can be used to return the distance (in meters) between 2 points (in lon/lat).

g=Geod(ellps='WGS84')
lon2D,lat2D = np.meshgrid(np.arange(0,20,0.1),np.arange(30,45,0.1))
_,_, distEW = g.inv(lon2D[:,:-1],lat2D[:,1:], lon2D[:,1:], lat2D[:,1:])
_,_, distNS = g.inv(lon2D[1:,:],lat2D[1:,:], lon2D[1:,:], lat2D[:-1,:])
pixel_area = distEW[1:,:] * distNS[:,1:] 

Olivier's answer is great and concise, but for my own curiosity I wanted to see how it compared to a more accurate calculation (their answer assumes the grid cells are small enough to approximate as squares):

import numpy as np
from pyproj import Geod
from shapely.geometry import LineString, Point, Polygon

lons = np.arange(0,20,0.1)
lats = np.arange(30,45,0.1)

# Fast way, square approx of grid cells
geod = Geod(ellps="WGS84")
lon2D,lat2D = np.meshgrid(lons, lats)
_,_, distEW = geod.inv(lon2D[:,:-1],lat2D[:,1:], lon2D[:,1:], lat2D[:,1:])
_,_, distNS = geod.inv(lon2D[1:,:],lat2D[1:,:], lon2D[1:,:], lat2D[:-1,:])
square_area = distEW[1:,:] * distNS[:,1:]

# Slower way, geodesic areas

def stack_coords(x, y):
    """Stacks coordinate lists into a 2D array of coordinate pairs,
       flattened to list of coordinate pairs"""
    out = np.vstack(np.stack(np.meshgrid(x, y), axis=2))
    return out

def get_lat_squares(lats, lons):
    """Construct shapely Polygons for a single longitude but
       every latitude.
       Area is constant with longitude so just copy results.
    """
    lats_0 = lats[:-1]
    lats_1 = lats[1:]
    lons_0 = lons[0:1]
    lons_1 = lons[1:2]

    c1 = stack_coords(lons_0, lats_0)
    c2 = stack_coords(lons_1, lats_0)
    c3 = stack_coords(lons_1, lats_1)
    c4 = stack_coords(lons_0, lats_1)

    squares = []
    for p1, p2, p3, p4 in zip(c1, c2, c3, c4):
        squares.append(Polygon([p1, p2, p3, p4]))
    return squares

def get_areas(lats, lons):
    squares = get_lat_squares(lats, lons)
    geod = Geod(ellps="WGS84")
    areas = []
    for square in squares:
        area = geod.geometry_area_perimeter(square)[0]
        areas.append(area)
    return np.array(areas)

geodesic_areas = get_areas(lats, lons)

for a1, a2 in zip(geodesic_areas, square_area[:,0]):
   print(a1, a2)

Output:

106904546.2618103 106850809.52460858
106798611.31295013 106744711.14938568
106692351.02400208 106638287.58503169
106585765.66596985 106531539.10307406
106478855.51091766 106424465.9760592
...
88300037.70746613 88224423.89253764
88150258.98899078 88074508.72317643
88000204.78475189 87924318.28878595
87849875.51327515 87773853.00843135

Pretty close! It obviously depends on the size of the grid steps though. I'm still curious about other answers to the question, I did say "area elements" in the question, I guess I was imagining extracting Jacobian factors from some coordinate transform in eg pyproj. The shape integrals here should be more accurate of course due to finite size, but I guess I thought it would be faster and easier to get the Jacobian factors... seems like they aren't easily exposed though?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM