简体   繁体   English

仅在matplotlib中的大陆上绘图

[英]Plot only on continent in matplotlib

I am drawing a map using basemap from matplotlib. 我正在使用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 ocean. 数据散布在世界各地,但我只想保留大陆上的所有数据,然后将其丢到海洋上。 Is there a way that I can filter the data, or is there a way to draw the ocean again to cover the data? 有没有办法可以过滤数据,还是有办法再次抽空覆盖数据?

There's method in matplotlib.basemap: is_land(xpt, ypt) matplotlib.basemap中有一个方法: is_land(xpt, ypt)

It returns True if the given x,y point (in projection coordinates) is over land, False otherwise. 如果给定的x,y点(在投影坐标中)在陆地上,则返回True否则返回False The definition of land is based upon the GSHHS coastline polygons associated with the class instance. 土地的定义基于与类实例关联的GSHHS海岸线多边形。 Points over lakes inside land regions are not counted as land points. 陆地区域内的湖泊上的点数不算作陆地点。

For more information, see here . 有关更多信息,请参见此处

is_land() will loop all the polygons to check whether it's land or not. is_land()将循环所有多边形以检查其是否为陆地。 For large data size, it's very slow. 对于大数据量,它非常慢。 You can use points_inside_poly() from matplotlib to check an array of points quickly. 您可以使用matplotlib中的points_inside_poly()快速检查点数组。 Here is the code. 这是代码。 It doesn't check lakepolygons , if you want remove points in lakes, you can add your self. 它不检查lakepolygons ,如果要删除湖泊中的点,则可以添加自己。

It took 2.7 seconds to check 100000 points on my PC. 在我的PC上花了2.7秒检查100000点。 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()

The HYRY's answer won't work on new versions of matplotlib (nxutils is deprecated). HYRY的答案不适用于新版本的matplotlib(不建议使用nxutils)。 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

The simplest way is to use basemap's maskoceans . 最简单的方法是使用底图的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): 更新(比in_land或in_polygon解决方案快得多):

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

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. 基本上,我的解决方案提取了用于绘制Basemap实例的海岸线的多边形,并将这些多边形与地图的轮廓相结合,以生成一个matplotlib.PathPatch ,它覆盖了地图的海洋区域。

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. 在这种情况下,使用maskoceans会产生非常粗糙的海岸线轮廓,看起来不太好。

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 :) 希望这对某人有帮助:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM