Creating a land/ocean mask in Cartopy?
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 :-)
python cartopy
add a comment |
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 :-)
python cartopy
add a comment |
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 :-)
python cartopy
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 :-)
python cartopy
python cartopy
edited Nov 21 '18 at 13:17
Rob
asked Nov 15 '18 at 15:40
RobRob
1571114
1571114
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
To highlight one approach that you could take, I've based my answer on the excellent Cartopy Hurricane Katrina (2005) gallery example, which plots the storm track of Katrina as it sweeps across the Gulf of Mexico, and up over the United States.
By essentially using a simple mixture of the cartopy.io.shapereader and some shapely predicates, we can get the job done. My example is a little bit verbose, but don't be put off... it's not that scary:
import matplotlib.pyplot as plt
import shapely.geometry as sgeom
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
def sample_data():
"""
Return a list of latitudes and a list of longitudes (lons, lats)
for Hurricane Katrina (2005).
The data was originally sourced from the HURDAT2 dataset from AOML/NOAA:
http://www.aoml.noaa.gov/hrd/hurdat/newhurdat-all.html on 14th Dec 2012.
"""
lons = [-75.1, -75.7, -76.2, -76.5, -76.9, -77.7, -78.4, -79.0,
-79.6, -80.1, -80.3, -81.3, -82.0, -82.6, -83.3, -84.0,
-84.7, -85.3, -85.9, -86.7, -87.7, -88.6, -89.2, -89.6,
-89.6, -89.6, -89.6, -89.6, -89.1, -88.6, -88.0, -87.0,
-85.3, -82.9]
lats = [23.1, 23.4, 23.8, 24.5, 25.4, 26.0, 26.1, 26.2, 26.2, 26.0,
25.9, 25.4, 25.1, 24.9, 24.6, 24.4, 24.4, 24.5, 24.8, 25.2,
25.7, 26.3, 27.2, 28.2, 29.3, 29.5, 30.2, 31.1, 32.6, 34.1,
35.6, 37.0, 38.6, 40.1]
return lons, lats
# create the figure and geoaxes
fig = plt.figure(figsize=(14, 12))
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
# load the shapefile geometries
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
states = list(shpreader.Reader(states_shp).geometries())
# get the storm track lons and lats
lons, lats = sample_data()
# to get the effect of having just the states without a map "background"
# turn off the outline and background patches
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
# turn the lons and lats into a shapely LineString
track = sgeom.LineString(zip(lons, lats))
# turn the lons and lats into shapely Points
points = [sgeom.Point(point) for point in zip(lons, lats)]
# determine the storm track points that fall on land
land =
for state in states:
land.extend([tuple(point.coords)[0] for point in filter(state.covers, points)])
# determine the storm track points that fall on sea
sea = set([tuple(point.coords)[0] for point in points]) - set(land)
# plot the state polygons
facecolor = [0.9375, 0.9375, 0.859375]
for state in states:
ax.add_geometries([state], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor='black', zorder=1)
# plot the storm track land points
xs, ys = zip(*land)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='s', c='green', alpha=0.5, zorder=2)
# plot the storm track sea points
xs, ys = zip(*sea)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='x', c='blue', alpha=0.5, zorder=2)
# plot the storm track
ax.add_geometries([track], ccrs.PlateCarree(),
facecolor='none', edgecolor='k')
plt.show()
The above example should generate the following plot, which highlights how to discriminate between land and sea points using shapely geometries.
HTH
"covers" does not seem to be documented anywhere. If it is, could you point me towards it or explain what it does? (cf intersects, etc)
– Rob
Nov 21 '18 at 12:50
Nice one Rob. Yeah, if you're going to iterate over many geometries, then take the prepared geometry option everytime... as you discovered it makes a massive optimisation for runtime. The only downside with prepared geometries are that the functionality isn't as rich, but clearly that's okay for your use case. BTW here the link for prepared and covers, see shapely.readthedocs.io/en/stable/…
– bjlittle
Nov 24 '18 at 19:39
Thanks. All that link says though is "Prepared geometries instances have the following methods: contains, contains_properly, covers, and intersects. All have exactly the same arguments and usage as their counterparts in non-prepared geometric objects." It doesn't seem to define what covers, etc actually are (for either prepared or non-prepared).
– Rob
Nov 24 '18 at 21:34
add a comment |
Here's a code example using the solutions above that solves this problem:
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
from shapely.prepared import prep
import seaborn as sns
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_polygons_prep = [prep(land_polygon) for land_polygon in land_polygons]
land =
for land_polygon in land_polygons_prep:
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)
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53322952%2fcreating-a-land-ocean-mask-in-cartopy%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
To highlight one approach that you could take, I've based my answer on the excellent Cartopy Hurricane Katrina (2005) gallery example, which plots the storm track of Katrina as it sweeps across the Gulf of Mexico, and up over the United States.
By essentially using a simple mixture of the cartopy.io.shapereader and some shapely predicates, we can get the job done. My example is a little bit verbose, but don't be put off... it's not that scary:
import matplotlib.pyplot as plt
import shapely.geometry as sgeom
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
def sample_data():
"""
Return a list of latitudes and a list of longitudes (lons, lats)
for Hurricane Katrina (2005).
The data was originally sourced from the HURDAT2 dataset from AOML/NOAA:
http://www.aoml.noaa.gov/hrd/hurdat/newhurdat-all.html on 14th Dec 2012.
"""
lons = [-75.1, -75.7, -76.2, -76.5, -76.9, -77.7, -78.4, -79.0,
-79.6, -80.1, -80.3, -81.3, -82.0, -82.6, -83.3, -84.0,
-84.7, -85.3, -85.9, -86.7, -87.7, -88.6, -89.2, -89.6,
-89.6, -89.6, -89.6, -89.6, -89.1, -88.6, -88.0, -87.0,
-85.3, -82.9]
lats = [23.1, 23.4, 23.8, 24.5, 25.4, 26.0, 26.1, 26.2, 26.2, 26.0,
25.9, 25.4, 25.1, 24.9, 24.6, 24.4, 24.4, 24.5, 24.8, 25.2,
25.7, 26.3, 27.2, 28.2, 29.3, 29.5, 30.2, 31.1, 32.6, 34.1,
35.6, 37.0, 38.6, 40.1]
return lons, lats
# create the figure and geoaxes
fig = plt.figure(figsize=(14, 12))
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
# load the shapefile geometries
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
states = list(shpreader.Reader(states_shp).geometries())
# get the storm track lons and lats
lons, lats = sample_data()
# to get the effect of having just the states without a map "background"
# turn off the outline and background patches
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
# turn the lons and lats into a shapely LineString
track = sgeom.LineString(zip(lons, lats))
# turn the lons and lats into shapely Points
points = [sgeom.Point(point) for point in zip(lons, lats)]
# determine the storm track points that fall on land
land =
for state in states:
land.extend([tuple(point.coords)[0] for point in filter(state.covers, points)])
# determine the storm track points that fall on sea
sea = set([tuple(point.coords)[0] for point in points]) - set(land)
# plot the state polygons
facecolor = [0.9375, 0.9375, 0.859375]
for state in states:
ax.add_geometries([state], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor='black', zorder=1)
# plot the storm track land points
xs, ys = zip(*land)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='s', c='green', alpha=0.5, zorder=2)
# plot the storm track sea points
xs, ys = zip(*sea)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='x', c='blue', alpha=0.5, zorder=2)
# plot the storm track
ax.add_geometries([track], ccrs.PlateCarree(),
facecolor='none', edgecolor='k')
plt.show()
The above example should generate the following plot, which highlights how to discriminate between land and sea points using shapely geometries.
HTH
"covers" does not seem to be documented anywhere. If it is, could you point me towards it or explain what it does? (cf intersects, etc)
– Rob
Nov 21 '18 at 12:50
Nice one Rob. Yeah, if you're going to iterate over many geometries, then take the prepared geometry option everytime... as you discovered it makes a massive optimisation for runtime. The only downside with prepared geometries are that the functionality isn't as rich, but clearly that's okay for your use case. BTW here the link for prepared and covers, see shapely.readthedocs.io/en/stable/…
– bjlittle
Nov 24 '18 at 19:39
Thanks. All that link says though is "Prepared geometries instances have the following methods: contains, contains_properly, covers, and intersects. All have exactly the same arguments and usage as their counterparts in non-prepared geometric objects." It doesn't seem to define what covers, etc actually are (for either prepared or non-prepared).
– Rob
Nov 24 '18 at 21:34
add a comment |
To highlight one approach that you could take, I've based my answer on the excellent Cartopy Hurricane Katrina (2005) gallery example, which plots the storm track of Katrina as it sweeps across the Gulf of Mexico, and up over the United States.
By essentially using a simple mixture of the cartopy.io.shapereader and some shapely predicates, we can get the job done. My example is a little bit verbose, but don't be put off... it's not that scary:
import matplotlib.pyplot as plt
import shapely.geometry as sgeom
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
def sample_data():
"""
Return a list of latitudes and a list of longitudes (lons, lats)
for Hurricane Katrina (2005).
The data was originally sourced from the HURDAT2 dataset from AOML/NOAA:
http://www.aoml.noaa.gov/hrd/hurdat/newhurdat-all.html on 14th Dec 2012.
"""
lons = [-75.1, -75.7, -76.2, -76.5, -76.9, -77.7, -78.4, -79.0,
-79.6, -80.1, -80.3, -81.3, -82.0, -82.6, -83.3, -84.0,
-84.7, -85.3, -85.9, -86.7, -87.7, -88.6, -89.2, -89.6,
-89.6, -89.6, -89.6, -89.6, -89.1, -88.6, -88.0, -87.0,
-85.3, -82.9]
lats = [23.1, 23.4, 23.8, 24.5, 25.4, 26.0, 26.1, 26.2, 26.2, 26.0,
25.9, 25.4, 25.1, 24.9, 24.6, 24.4, 24.4, 24.5, 24.8, 25.2,
25.7, 26.3, 27.2, 28.2, 29.3, 29.5, 30.2, 31.1, 32.6, 34.1,
35.6, 37.0, 38.6, 40.1]
return lons, lats
# create the figure and geoaxes
fig = plt.figure(figsize=(14, 12))
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
# load the shapefile geometries
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
states = list(shpreader.Reader(states_shp).geometries())
# get the storm track lons and lats
lons, lats = sample_data()
# to get the effect of having just the states without a map "background"
# turn off the outline and background patches
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
# turn the lons and lats into a shapely LineString
track = sgeom.LineString(zip(lons, lats))
# turn the lons and lats into shapely Points
points = [sgeom.Point(point) for point in zip(lons, lats)]
# determine the storm track points that fall on land
land =
for state in states:
land.extend([tuple(point.coords)[0] for point in filter(state.covers, points)])
# determine the storm track points that fall on sea
sea = set([tuple(point.coords)[0] for point in points]) - set(land)
# plot the state polygons
facecolor = [0.9375, 0.9375, 0.859375]
for state in states:
ax.add_geometries([state], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor='black', zorder=1)
# plot the storm track land points
xs, ys = zip(*land)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='s', c='green', alpha=0.5, zorder=2)
# plot the storm track sea points
xs, ys = zip(*sea)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='x', c='blue', alpha=0.5, zorder=2)
# plot the storm track
ax.add_geometries([track], ccrs.PlateCarree(),
facecolor='none', edgecolor='k')
plt.show()
The above example should generate the following plot, which highlights how to discriminate between land and sea points using shapely geometries.
HTH
"covers" does not seem to be documented anywhere. If it is, could you point me towards it or explain what it does? (cf intersects, etc)
– Rob
Nov 21 '18 at 12:50
Nice one Rob. Yeah, if you're going to iterate over many geometries, then take the prepared geometry option everytime... as you discovered it makes a massive optimisation for runtime. The only downside with prepared geometries are that the functionality isn't as rich, but clearly that's okay for your use case. BTW here the link for prepared and covers, see shapely.readthedocs.io/en/stable/…
– bjlittle
Nov 24 '18 at 19:39
Thanks. All that link says though is "Prepared geometries instances have the following methods: contains, contains_properly, covers, and intersects. All have exactly the same arguments and usage as their counterparts in non-prepared geometric objects." It doesn't seem to define what covers, etc actually are (for either prepared or non-prepared).
– Rob
Nov 24 '18 at 21:34
add a comment |
To highlight one approach that you could take, I've based my answer on the excellent Cartopy Hurricane Katrina (2005) gallery example, which plots the storm track of Katrina as it sweeps across the Gulf of Mexico, and up over the United States.
By essentially using a simple mixture of the cartopy.io.shapereader and some shapely predicates, we can get the job done. My example is a little bit verbose, but don't be put off... it's not that scary:
import matplotlib.pyplot as plt
import shapely.geometry as sgeom
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
def sample_data():
"""
Return a list of latitudes and a list of longitudes (lons, lats)
for Hurricane Katrina (2005).
The data was originally sourced from the HURDAT2 dataset from AOML/NOAA:
http://www.aoml.noaa.gov/hrd/hurdat/newhurdat-all.html on 14th Dec 2012.
"""
lons = [-75.1, -75.7, -76.2, -76.5, -76.9, -77.7, -78.4, -79.0,
-79.6, -80.1, -80.3, -81.3, -82.0, -82.6, -83.3, -84.0,
-84.7, -85.3, -85.9, -86.7, -87.7, -88.6, -89.2, -89.6,
-89.6, -89.6, -89.6, -89.6, -89.1, -88.6, -88.0, -87.0,
-85.3, -82.9]
lats = [23.1, 23.4, 23.8, 24.5, 25.4, 26.0, 26.1, 26.2, 26.2, 26.0,
25.9, 25.4, 25.1, 24.9, 24.6, 24.4, 24.4, 24.5, 24.8, 25.2,
25.7, 26.3, 27.2, 28.2, 29.3, 29.5, 30.2, 31.1, 32.6, 34.1,
35.6, 37.0, 38.6, 40.1]
return lons, lats
# create the figure and geoaxes
fig = plt.figure(figsize=(14, 12))
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
# load the shapefile geometries
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
states = list(shpreader.Reader(states_shp).geometries())
# get the storm track lons and lats
lons, lats = sample_data()
# to get the effect of having just the states without a map "background"
# turn off the outline and background patches
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
# turn the lons and lats into a shapely LineString
track = sgeom.LineString(zip(lons, lats))
# turn the lons and lats into shapely Points
points = [sgeom.Point(point) for point in zip(lons, lats)]
# determine the storm track points that fall on land
land =
for state in states:
land.extend([tuple(point.coords)[0] for point in filter(state.covers, points)])
# determine the storm track points that fall on sea
sea = set([tuple(point.coords)[0] for point in points]) - set(land)
# plot the state polygons
facecolor = [0.9375, 0.9375, 0.859375]
for state in states:
ax.add_geometries([state], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor='black', zorder=1)
# plot the storm track land points
xs, ys = zip(*land)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='s', c='green', alpha=0.5, zorder=2)
# plot the storm track sea points
xs, ys = zip(*sea)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='x', c='blue', alpha=0.5, zorder=2)
# plot the storm track
ax.add_geometries([track], ccrs.PlateCarree(),
facecolor='none', edgecolor='k')
plt.show()
The above example should generate the following plot, which highlights how to discriminate between land and sea points using shapely geometries.
HTH
To highlight one approach that you could take, I've based my answer on the excellent Cartopy Hurricane Katrina (2005) gallery example, which plots the storm track of Katrina as it sweeps across the Gulf of Mexico, and up over the United States.
By essentially using a simple mixture of the cartopy.io.shapereader and some shapely predicates, we can get the job done. My example is a little bit verbose, but don't be put off... it's not that scary:
import matplotlib.pyplot as plt
import shapely.geometry as sgeom
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
def sample_data():
"""
Return a list of latitudes and a list of longitudes (lons, lats)
for Hurricane Katrina (2005).
The data was originally sourced from the HURDAT2 dataset from AOML/NOAA:
http://www.aoml.noaa.gov/hrd/hurdat/newhurdat-all.html on 14th Dec 2012.
"""
lons = [-75.1, -75.7, -76.2, -76.5, -76.9, -77.7, -78.4, -79.0,
-79.6, -80.1, -80.3, -81.3, -82.0, -82.6, -83.3, -84.0,
-84.7, -85.3, -85.9, -86.7, -87.7, -88.6, -89.2, -89.6,
-89.6, -89.6, -89.6, -89.6, -89.1, -88.6, -88.0, -87.0,
-85.3, -82.9]
lats = [23.1, 23.4, 23.8, 24.5, 25.4, 26.0, 26.1, 26.2, 26.2, 26.0,
25.9, 25.4, 25.1, 24.9, 24.6, 24.4, 24.4, 24.5, 24.8, 25.2,
25.7, 26.3, 27.2, 28.2, 29.3, 29.5, 30.2, 31.1, 32.6, 34.1,
35.6, 37.0, 38.6, 40.1]
return lons, lats
# create the figure and geoaxes
fig = plt.figure(figsize=(14, 12))
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
# load the shapefile geometries
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
states = list(shpreader.Reader(states_shp).geometries())
# get the storm track lons and lats
lons, lats = sample_data()
# to get the effect of having just the states without a map "background"
# turn off the outline and background patches
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
# turn the lons and lats into a shapely LineString
track = sgeom.LineString(zip(lons, lats))
# turn the lons and lats into shapely Points
points = [sgeom.Point(point) for point in zip(lons, lats)]
# determine the storm track points that fall on land
land =
for state in states:
land.extend([tuple(point.coords)[0] for point in filter(state.covers, points)])
# determine the storm track points that fall on sea
sea = set([tuple(point.coords)[0] for point in points]) - set(land)
# plot the state polygons
facecolor = [0.9375, 0.9375, 0.859375]
for state in states:
ax.add_geometries([state], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor='black', zorder=1)
# plot the storm track land points
xs, ys = zip(*land)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='s', c='green', alpha=0.5, zorder=2)
# plot the storm track sea points
xs, ys = zip(*sea)
ax.scatter(xs, ys, transform=ccrs.PlateCarree(),
s=100, marker='x', c='blue', alpha=0.5, zorder=2)
# plot the storm track
ax.add_geometries([track], ccrs.PlateCarree(),
facecolor='none', edgecolor='k')
plt.show()
The above example should generate the following plot, which highlights how to discriminate between land and sea points using shapely geometries.
HTH
edited Nov 15 '18 at 23:14
answered Nov 15 '18 at 23:08
bjlittlebjlittle
5616
5616
"covers" does not seem to be documented anywhere. If it is, could you point me towards it or explain what it does? (cf intersects, etc)
– Rob
Nov 21 '18 at 12:50
Nice one Rob. Yeah, if you're going to iterate over many geometries, then take the prepared geometry option everytime... as you discovered it makes a massive optimisation for runtime. The only downside with prepared geometries are that the functionality isn't as rich, but clearly that's okay for your use case. BTW here the link for prepared and covers, see shapely.readthedocs.io/en/stable/…
– bjlittle
Nov 24 '18 at 19:39
Thanks. All that link says though is "Prepared geometries instances have the following methods: contains, contains_properly, covers, and intersects. All have exactly the same arguments and usage as their counterparts in non-prepared geometric objects." It doesn't seem to define what covers, etc actually are (for either prepared or non-prepared).
– Rob
Nov 24 '18 at 21:34
add a comment |
"covers" does not seem to be documented anywhere. If it is, could you point me towards it or explain what it does? (cf intersects, etc)
– Rob
Nov 21 '18 at 12:50
Nice one Rob. Yeah, if you're going to iterate over many geometries, then take the prepared geometry option everytime... as you discovered it makes a massive optimisation for runtime. The only downside with prepared geometries are that the functionality isn't as rich, but clearly that's okay for your use case. BTW here the link for prepared and covers, see shapely.readthedocs.io/en/stable/…
– bjlittle
Nov 24 '18 at 19:39
Thanks. All that link says though is "Prepared geometries instances have the following methods: contains, contains_properly, covers, and intersects. All have exactly the same arguments and usage as their counterparts in non-prepared geometric objects." It doesn't seem to define what covers, etc actually are (for either prepared or non-prepared).
– Rob
Nov 24 '18 at 21:34
"covers" does not seem to be documented anywhere. If it is, could you point me towards it or explain what it does? (cf intersects, etc)
– Rob
Nov 21 '18 at 12:50
"covers" does not seem to be documented anywhere. If it is, could you point me towards it or explain what it does? (cf intersects, etc)
– Rob
Nov 21 '18 at 12:50
Nice one Rob. Yeah, if you're going to iterate over many geometries, then take the prepared geometry option everytime... as you discovered it makes a massive optimisation for runtime. The only downside with prepared geometries are that the functionality isn't as rich, but clearly that's okay for your use case. BTW here the link for prepared and covers, see shapely.readthedocs.io/en/stable/…
– bjlittle
Nov 24 '18 at 19:39
Nice one Rob. Yeah, if you're going to iterate over many geometries, then take the prepared geometry option everytime... as you discovered it makes a massive optimisation for runtime. The only downside with prepared geometries are that the functionality isn't as rich, but clearly that's okay for your use case. BTW here the link for prepared and covers, see shapely.readthedocs.io/en/stable/…
– bjlittle
Nov 24 '18 at 19:39
Thanks. All that link says though is "Prepared geometries instances have the following methods: contains, contains_properly, covers, and intersects. All have exactly the same arguments and usage as their counterparts in non-prepared geometric objects." It doesn't seem to define what covers, etc actually are (for either prepared or non-prepared).
– Rob
Nov 24 '18 at 21:34
Thanks. All that link says though is "Prepared geometries instances have the following methods: contains, contains_properly, covers, and intersects. All have exactly the same arguments and usage as their counterparts in non-prepared geometric objects." It doesn't seem to define what covers, etc actually are (for either prepared or non-prepared).
– Rob
Nov 24 '18 at 21:34
add a comment |
Here's a code example using the solutions above that solves this problem:
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
from shapely.prepared import prep
import seaborn as sns
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_polygons_prep = [prep(land_polygon) for land_polygon in land_polygons]
land =
for land_polygon in land_polygons_prep:
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)
add a comment |
Here's a code example using the solutions above that solves this problem:
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
from shapely.prepared import prep
import seaborn as sns
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_polygons_prep = [prep(land_polygon) for land_polygon in land_polygons]
land =
for land_polygon in land_polygons_prep:
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)
add a comment |
Here's a code example using the solutions above that solves this problem:
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
from shapely.prepared import prep
import seaborn as sns
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_polygons_prep = [prep(land_polygon) for land_polygon in land_polygons]
land =
for land_polygon in land_polygons_prep:
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)
Here's a code example using the solutions above that solves this problem:
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
from shapely.prepared import prep
import seaborn as sns
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_polygons_prep = [prep(land_polygon) for land_polygon in land_polygons]
land =
for land_polygon in land_polygons_prep:
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)
answered Nov 21 '18 at 13:23
RobRob
1571114
1571114
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53322952%2fcreating-a-land-ocean-mask-in-cartopy%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown