简体   繁体   English

使用 Cartopy 和 Geopandas 遮罩形状文件之外的区域

[英]Mask area outside of a shape file with Cartopy and Geopandas

I have a set of data with (lon, lat, temperature) that I have plotted with Cartopy.我有一组用 Cartopy 绘制的(经度、纬度、温度)数据。 The minimum example that I can give is the code below (with only 30 data points)我可以给出的最小示例是下面的代码(只有 30 个数据点)

import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import matplotlib.colors as clr
import pandas as pd
import numpy as np

from metpy.interpolate import interpolate_to_grid, remove_nan_observations
from cartopy.io.shapereader import Reader
from cartopy.feature import ShapelyFeature

canada_east = -95
canada_west = -101.8
canada_north = 52.8
canada_south = 48.85

central_lon = (canada_east + canada_west)/2
central_lat = (canada_north + canada_south)/2

crs = ccrs.LambertConformal(central_longitude = central_lon, central_latitude = central_lat)
lat = np.array([49.8134 50.904  50.698  49.095  49.436  49.9607 49.9601 49.356  50.116
49.402  52.3472 50.411  49.24   49.876  49.591  49.905  49.498  49.088
49.118  50.5947 49.3776 49.148  49.1631 51.358  49.826  50.4324 49.96
49.68   49.875  50.829  51.572])
lon = np.array([-100.3721  -97.273   -99.068   -97.528  -100.308   -98.9054  -98.6367
-99.248   -96.434  -100.93   -101.1099 -100.893  -100.055   -99.909
-97.518   -99.354   -98.03    -99.325   -99.054   -98.0035 -100.5387
-100.491   -97.1454 -100.361   -96.776   -99.4392  -97.7463  -97.984
-95.92    -98.111  -100.488])
tem = np.array([-8.45   -4.026  -5.993  -3.68   -7.35   -7.421  -6.477  -8.03   -3.834
-13.04   -4.057  -8.79   -6.619 -10.89   -4.465  -8.41   -4.861  -9.93
-7.125  -4.424 -11.95   -9.56   -3.86   -7.17   -4.193  -7.653  -4.883
-5.631  -3.004  -4.738  -8.81])

xp, yp, _ = crs.transform_points(ccrs.PlateCarree(), lon, lat ).T
xp, yp, tem = remove_nan_observations(xp, yp, tem)

alt_x, alt_y, data = interpolate_to_grid( xp, yp, tem, minimum_neighbors=2, search_radius=240000, interp_type = 'barnes', hres = 1000)

# Create the figure and grid for subplots
fig = plt.figure(figsize=(17, 12))

# Main ax
ax = plt.subplot(111, projection=crs)
ax.set_extent([canada_west, canada_east, canada_south, canada_north], ccrs.PlateCarree())

# Ading province borders and country borders
provinces_bdr = cfeature.NaturalEarthFeature(category = 'cultural',
                                                name = 'admin_1_states_provinces_lines',
                                                scale = '50m',
                                                linewidth = 0.6,
                                                facecolor='none',
                                                )  # variable to add provinces border

country_bdr = cfeature.NaturalEarthFeature(category= 'cultural',
                                            name = 'admin_0_boundary_lines_land', 
                                            scale = '50m', 
                                            linewidth = 1.5,
                                            facecolor = 'none',
                                            edgecolor = 'k')
                                                
ax.add_feature(provinces_bdr, linestyle='--')
ax.add_feature(country_bdr, linestyle='--')

ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.BORDERS)

cf = ax.pcolormesh(alt_x, alt_y, data, cmap=plt.cm.rainbow)

# Read the shape file and add it

shape_feature = ShapelyFeature(Reader('MB_AGregion_Perim_South.shp').geometries(), ccrs.epsg(26914), linewidth = 1, facecolor = (1, 1, 1, 0), edgecolor = (0.5, 0.5, 0.5, 1))

ax.add_feature(shape_feature)
plt.show()

which gives this result:这给出了这个结果:

这个结果

where the gray line inside is produced by the shape file.其中里面的灰线是由shape文件产生的。 Now I want to limit the coloring to be only inside the shape file (so area that's outside of the gray line should not be colored by pcolormesh) but I can not find a way that work.现在我想将着色限制为仅在形状文件内(因此灰线之外的区域不应由 pcolormesh 着色)但我找不到可行的方法。 I have read this example and this example but I cannot understand both of them.我已经阅读了这个例子这个例子,但我无法理解它们。 Is there a simple way to do this using geopandas and/or cartopy alone?有没有一种简单的方法可以单独使用 geopandas 和/或 cartopy?

Sorry I cannot upload the shape file here, this is the best minimal example I could have done.抱歉,我无法在此处上传形状文件,这是我可以做的最好的最小示例。 If there are any improvements I should have done please tell me.如果有任何我应该做的改进,请告诉我。 I'm new to stack overflow and I'm open to critiques.我是堆栈溢出的新手,我愿意接受批评。

Edit1: To clarify, the shape file I want the color to be limited to is the 'MB_AGregion_Perim_South.shp' that I read with ShapelyFeature (the last 4 lines of my code), and it draw the grey line that bounds most part of my coloring. Edit1:澄清一下,我希望将颜色限制为的形状文件是我用 ShapelyFeature 读取的“MB_AGregion_Perim_South.shp”(我的代码的最后 4 行),它绘制了我的大部分内容的灰色线染色。

Edit 2: As @Michael Delgado suggested, I have added this lines of code:编辑 2:正如@Michael Delgado 建议的那样,我添加了这行代码:

cat_gdf = geopandas.read_file('MB_AGregion_Perim_South.shp')
cat_gdf = cat_gdf.to_crs(epsg = 4326)
mask = shapely.vectorized.contains(cat_gdf.dissolve().geometry.item(), alt_x, alt_y)

where alt_x and alt_y is the interpolated result (please look at my example above).其中 alt_x 和 alt_y 是插值结果(请看我上面的例子)。 The shape file has epsg = 26914 originally, so I transform it into 4326. shape文件原来有epsg = 26914,所以我把它转换成4326。

The problem is that the mask contains all false values (which means it mask everything).问题是掩码包含所有错误值(这意味着它会掩盖所有内容)。 I doubted that it's because alt_x and alt_y are coordinates that has been transformed with crs.transform_points(ccrs.PlateCarree(), lon, lat ).T (as my code showed above).我怀疑这是因为 alt_x 和 alt_y 是已经用 crs.transform_points(ccrs.PlateCarree(), lon, lat ).T 转换的坐标(如我上面的代码所示)。 I have search around and try to get the shape file into different epsg values but it doesn't work.我四处搜索并尝试将形状文件转换为不同的 epsg 值,但它不起作用。 Also, cat_gdf.geometry is a multi polygons.此外, cat_gdf.geometry 是一个多多边形。 Could it be the cause here?会不会是这里的原因?

For anyone who's struggling with this in the future, here is a detailed explanation of the solution对于将来遇到此问题的任何人, 这里是解决方案的详细说明

Quick MRE:快速 MRE:

import numpy as np, pandas as pd, geopandas as gpd
import matplotlib.pyplot as plt

x = np.arange(-126, -105, 0.1)
y = np.arange(25, 46, 0.1)
xx, yy = np.meshgrid(x, y)
xnorm = (xx - xx.min()) / (xx.max() - xx.min())
ynorm = (yy - yy.min()) / (yy.max() - yy.min())
v = np.cos((xnorm * 2 - 1) * np.pi) + np.sin((ynorm * 2 - 1) * np.pi)

gdf = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))

fig, ax = plt.subplots()
ax.pcolormesh(xx, yy, v)
xlim, ylim = ax.get_xlim(), ax.get_ylim()
gdf.plot(ax=ax, color='none', edgecolor='k')
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)

带有 shapefile 覆盖的网格数据的 pcolormesh

You can use shapely.vectorized to mask a set of x, y points using a shapely.geometry object:您可以使用 shapely.vectorized 使用 shapely.geometry 对象屏蔽一组 x、y 点:

import shapely.vectorized
mask = shapely.vectorized.contains(gdf.dissolve().geometry.item(), xx, yy)

fig, ax = plt.subplots()
ax.pcolormesh(xx, yy, np.where(mask, v, np.nan))
xlim, ylim = ax.get_xlim(), ax.get_ylim()
gdf.plot(ax=ax, color='none', edgecolor='k')
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)

网格数据的 pcolormesh 图,掩蔽为多边形

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

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