I have a gridded array of data with an associated lat/lon and I want to use Cartopy to find whether each particular grid cell is over ocean or land.
I can get the land data simply from the Cartopy Feature interface
land_10m = cartopy.feature.NaturalEarthFeature('physical', 'land', '10m')
I can then get the geometries
all_geometries = [geometry for geometry in land_50m.geometries()]
But I can't figure out how to use these geometries to test with a particular location is over land or not.
This question seems to have come up before Mask Ocean or Land from data using Cartopy and the solution was pretty much "use basemap instead" which is a bit unsatisfactory.
======== Update ========
Thanks to bjlittle I have a solution that works and generates the plot below
from IPython import embed
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
from shapely.geometry import Point
import cartopy
land_10m = cartopy.feature.NaturalEarthFeature('physical', 'land', '10m')
land_polygons = list(land_10m.geometries())
lats = np.arange(48,58, 0.1)
lons = np.arange(-5,5, 0.1)
lon_grid, lat_grid = np.meshgrid(lons, lats)
points = [Point(point) for point in zip(lon_grid.ravel(), lat_grid.ravel())]
land = []
for land_polygon in land_polygons:
land.extend([tuple(point.coords)[0] for point in filter(land_polygon.covers, points)])
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.PlateCarree())
ax.add_feature(land_10m, zorder=0, edgecolor='black', facecolor=sns.xkcd_rgb['black'])
xs, ys = zip(*land)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=12, marker='s', c='red', alpha=0.5, zorder=2)
plt.show()
However this is extremely slow and ultimately I'll need to do this globally at a much higher resolution.
Does anyone have any suggestions on how to adapt the above to be faster?
==== Update 2 ====
Preparing the polygons makes a HUGE difference
Adding these two lines has sped up an example at 1 degree from 40 seconds to 0.3 seconds
from shapely.prepared import prep
land_polygons_prep = [prep(land_polygon) for land_polygon in land_polygons]
The example that I ran at 0.1 degree that took over half an hour (i.e. it ran over lunch) now runs in 1.3 seconds :-)