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 e.g. 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?