Creating a land/ocean mask in Cartopy?












1















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()


Land mask



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 :-)










share|improve this question





























    1















    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()


    Land mask



    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 :-)










    share|improve this question



























      1












      1








      1








      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()


      Land mask



      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 :-)










      share|improve this question
















      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()


      Land mask



      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 21 '18 at 13:17







      Rob

















      asked Nov 15 '18 at 15:40









      RobRob

      1621114




      1621114
























          2 Answers
          2






          active

          oldest

          votes


















          2














          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






          share|improve this answer


























          • "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



















          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)





          share|improve this answer























            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
            });


            }
            });














            draft saved

            draft discarded


















            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









            2














            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






            share|improve this answer


























            • "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
















            2














            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






            share|improve this answer


























            • "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














            2












            2








            2







            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






            share|improve this answer















            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







            share|improve this answer














            share|improve this answer



            share|improve this answer








            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



















            • "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













            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)





            share|improve this answer




























              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)





              share|improve this answer


























                2












                2








                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)





                share|improve this answer













                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)






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 21 '18 at 13:23









                RobRob

                1621114




                1621114






























                    draft saved

                    draft discarded




















































                    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.




                    draft saved


                    draft discarded














                    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





















































                    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







                    Popular posts from this blog

                    Florida Star v. B. J. F.

                    Danny Elfman

                    Retrieve a Users Dashboard in Tumblr with R and TumblR. Oauth Issues