Plot only on continent in matplotlib

前端 未结 5 743
清歌不尽
清歌不尽 2020-11-27 07:13

I am drawing a map using basemap from matplotlib. The data are spreaded all over the world, but I just want to retain all the data on the continent and drop those on the oce

相关标签:
5条回答
  • 2020-11-27 07:51

    The HYRY's answer won't work on new versions of matplotlib (nxutils is deprecated). I've made a new version that works:

    from mpl_toolkits.basemap import Basemap
    import matplotlib.pyplot as plt
    from matplotlib.path import Path
    import numpy as np
    
    map = Basemap(projection='cyl', resolution='c')
    
    lons = [0., 0., 16., 76.]
    lats = [0., 41., 19., 51.]
    
    x, y = map(lons, lats)
    
    locations = np.c_[x, y]
    
    polygons = [Path(p.boundary) for p in map.landpolygons]
    
    result = np.zeros(len(locations), dtype=bool) 
    
    for polygon in polygons:
    
        result += np.array(polygon.contains_points(locations))
    
    print result
    
    0 讨论(0)
  • 2020-11-27 07:54

    I was answering this question, when I was told that it would be better to post my answer over here. Basically, my solution extracts the polygons that are used to draw the coastlines of the Basemap instance and combines these polygons with the outline of the map to produce a matplotlib.PathPatch that overlays the ocean areas of the map.

    This especially useful if the data is coarse and interpolation of the data is not wanted. In this case using maskoceans produces a very grainy outline of the coastlines, which does not look very good.

    Here is the same example I posted as answer for the other question:

    from matplotlib import pyplot as plt
    from mpl_toolkits import basemap as bm
    from matplotlib import colors
    import numpy as np
    import numpy.ma as ma
    from matplotlib.patches import Path, PathPatch
    
    fig, ax = plt.subplots()
    
    lon_0 = 319
    lat_0 = 72
    
    ##some fake data
    lons = np.linspace(lon_0-60,lon_0+60,10)
    lats = np.linspace(lat_0-15,lat_0+15,5)
    lon, lat = np.meshgrid(lons,lats)
    TOPO = np.sin(np.pi*lon/180)*np.exp(lat/90)
    
    m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=lat_0, lon_0=lon_0, ax = ax)
    m.drawcoastlines(linewidth=0.5)
    
    x,y = m(lon,lat)
    pcol = ax.pcolormesh(x,y,TOPO)
    
    ##getting the limits of the map:
    x0,x1 = ax.get_xlim()
    y0,y1 = ax.get_ylim()
    map_edges = np.array([[x0,y0],[x1,y0],[x1,y1],[x0,y1]])
    
    ##getting all polygons used to draw the coastlines of the map
    polys = [p.boundary for p in m.landpolygons]
    
    ##combining with map edges
    polys = [map_edges]+polys[:]
    
    ##creating a PathPatch
    codes = [
        [Path.MOVETO] + [Path.LINETO for p in p[1:]]
        for p in polys
    ]
    polys_lin = [v for p in polys for v in p]
    codes_lin = [c for cs in codes for c in cs]
    path = Path(polys_lin, codes_lin)
    patch = PathPatch(path,facecolor='white', lw=0)
    
    ##masking the data:
    ax.add_patch(patch)
    
    plt.show()
    

    This produces the following plot:

    Hope this is helpful to someone :)

    0 讨论(0)
  • 2020-11-27 07:54

    The simplest way is to use basemap's maskoceans.

    If for each lat, lon you have a data and you want to use contours: After meshgrid and interpolation:

    from scipy.interpolate import griddata as gd
    from mpl_toolkits.basemap import Basemap, cm, maskoceans
    xi, yi = np.meshgrid(xi, yi)
    zi = gd((mlon, mlat),
                scores,
                (xi, yi),
                method=grid_interpolation_method)
    #mask points on ocean
    data = maskoceans(xi, yi, zi)
    con = m.contourf(xi, yi, data, cmap=cm.GMT_red2green)
    #note instead of zi we have data now.
    

    Update (much faster than in_land or in_polygon solutions):

    If for each lat, lon you don't have any data, and you just want to scatter the points only over land:

    x, y = m(lons, lats)
    samples = len(lons)
    ocean = maskoceans(lons, lats, datain=np.arange(samples),
                       resolution='i')
    ocean_samples = np.ma.count_masked(ocean)
    print('{0} of {1} points in ocean'.format(ocean_samples, samples))
    m.scatter(x[~ocean.mask], y[~ocean.mask], marker='.', color=colors[~ocean.mask], s=1)
    m.drawcountries()
    m.drawcoastlines(linewidth=0.7)
    plt.savefig('a.png')
    
    0 讨论(0)
  • 2020-11-27 07:56

    There's method in matplotlib.basemap: is_land(xpt, ypt)

    It returns True if the given x,y point (in projection coordinates) is over land, False otherwise. The definition of land is based upon the GSHHS coastline polygons associated with the class instance. Points over lakes inside land regions are not counted as land points.

    For more information, see here.

    0 讨论(0)
  • 2020-11-27 08:00

    is_land() will loop all the polygons to check whether it's land or not. For large data size, it's very slow. You can use points_inside_poly() from matplotlib to check an array of points quickly. Here is the code. It doesn't check lakepolygons, if you want remove points in lakes, you can add your self.

    It took 2.7 seconds to check 100000 points on my PC. If you want more speed, you can convert the polygons into a bitmap, but it's a little difficult to do this. Please tell me if the following code is not fast enought for your dataset.

    from mpl_toolkits.basemap import Basemap
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.nxutils as nx
    
    def points_in_polys(points, polys):
        result = []
        for poly in polys:
            mask = nx.points_inside_poly(points, poly)
            result.extend(points[mask])
            points = points[~mask]
        return np.array(result)
    
    points = np.random.randint(0, 90, size=(100000, 2))
    m = Basemap(projection='moll',lon_0=0,resolution='c')
    m.drawcoastlines()
    m.fillcontinents(color='coral',lake_color='aqua')
    x, y = m(points[:,0], points[:,1])
    loc = np.c_[x, y]
    polys = [p.boundary for p in m.landpolygons]
    land_loc = points_in_polys(loc, polys)
    m.plot(land_loc[:, 0], land_loc[:, 1],'ro')
    plt.show()
    
    0 讨论(0)
提交回复
热议问题