我正在努力使用 cartopy (和 matplotlib)在底图顶部绘制点。我认为问题是我无法设法获取数据和底图以匹配轴的投影/crs,因为我的数据结构与我发现的大多数示例不同。不幸的是,其中一些(我的代码的顶部)无法更改,因为这意味着重写许多其他代码和函数:/
首先我尝试使用 matplotlib 绘制点并使用 cartopy 添加底图(我的代码中的版本 1)。根据我为
ax
设置投影的位置,底图根本不会绘制,或者在没有所需扩展的情况下绘制在所有内容之上(-> 显示整个地球)。如果我按照建议添加transform=
参数here,则根本不会绘制任何内容(至少在视口内不会绘制)。
在 geopandas.org 上,我找到了一个使用 cartopy 函数 (
add_geometries
) 添加点的示例(版本 2)。这会引发错误Source CRS must be an instance of CRS or one of its subclasses, or None.
,我不知道它来自哪里。
这是我使用的代码。不幸的是,它相当冗长,但我不知道在不丢失必要信息的情况下我还能省略什么。
import pandas as pd #2.1.4
import geopandas as gpd #0.12.2
import matplotlib.pyplot as plt #3.8.0
from shapely.geometry import Point, Polygon #1.8.5
import cartopy.crs as ccrs #0.22.0
import cartopy.io.img_tiles as cimgt
#------------------------------------------
#-----------Creating data-environment------
#------------------------------------------
#------Part 1: has to stay--------
gdf_test1 = gpd.GeoDataFrame(
{
"geometry": [Point(13.10157, 52.36640), Point(13.10130, 52.36656), Point(13.10254, 52.36582), Point(13.10309, 52.36549), Point(13.10332, 52.36535)]
},
crs = 'EPSG:4326'
)
gdf_test2 = gpd.GeoDataFrame(
{
"geometry": [Point(13.43562, 52.48038), Point(13.50074, 52.45651), Point(13.50061, 52.45657), Point(13.49998, 52.45688), Point(13.49984, 52.45695)]
},
crs = 'EPSG:4326'
)
outline_test1 = Polygon((Point(12.984381, 52.354036), Point(13.147663, 52.354036), Point(13.147663, 52.412748), Point(12.984381, 52.412748), Point(12.984381, 52.354036)))
outline_test2 = Polygon((Point(13.62631009242705, 52.517735730410585) , Point(13.625180130891495, 52.50452520302402) , Point(13.621934955922162, 52.49144577310617) , Point(13.616607639378886, 52.478623231164505) , Point(13.609251157772427, 52.46618078976451) , Point(13.599937818411506, 52.45423790766038) , Point(13.5887585076519, 52.44290915361389) , Point(13.57582177055312, 52.432303120286605) , Point(13.561252732467345, 52.42252139799845) , Point(13.545191874144964, 52.413657617485086) , Point(13.527793672849741, 52.40579657006711) , Point(13.509225122743361, 52.399013412873614) , Point(13.489664148436754, 52.39337296594993) , Point(13.469297926125792, 52.388929107231334) , Point(13.448321127145887, 52.38572427048734) , Point(13.426934099105035, 52.3837890504421) , Point(13.405341000000002, 52.38314191835826) , Point(13.383747900894965, 52.3837890504421) , Point(13.362360872854113, 52.38572427048734) , Point(13.341384073874208, 52.388929107231334) , Point(13.321017851563246, 52.39337296594993) , Point(13.301456877256639, 52.399013412873614) , Point(13.282888327150259, 52.40579657006711) , Point(13.265490125855036, 52.413657617485086) , Point(13.249429267532655, 52.42252139799845) , Point(13.23486022944688, 52.432303120286605) , Point(13.221923492348102, 52.44290915361389) , Point(13.210744181588494, 52.45423790766038) , Point(13.201430842227573, 52.46618078976451) , Point(13.194074360621114, 52.478623231164505) , Point(13.188747044077843, 52.49144577310617) , Point(13.185501869108505, 52.50452520302403) , Point(13.18437190757295, 52.517735730410585) , Point(13.185369938647879, 52.53095019147478) , Point(13.188488252991643, 52.5440412712636) , Point(13.193698653775746, 52.5568827315916) , Point(13.200952657395336, 52.569350632900345) , Point(13.210181894677728, 52.581324538061054) , Point(13.221298711298406, 52.59268868614871) , Point(13.234196963913654, 52.60333312435861) , Point(13.248753006259824, 52.61315478651281) , Point(13.264826857184044, 52.62205850701603) , Point(13.282263540302736, 52.62995795966922) , Point(13.300894582770653, 52.63677651143157) , Point(13.320539658531636, 52.6424479820343) , Point(13.341008359453616, 52.64691730128411) , Point(13.362102075970094, 52.65014105694213) , Point(13.383615967296576, 52.65208792721094) , Point(13.405341000000002, 52.652738993096214) , Point(13.427066032703426, 52.65208792721094) , Point(13.448579924029907, 52.65014105694215) , Point(13.469673640546384, 52.64691730128411) , Point(13.490142341468363, 52.642447982034305) , Point(13.509787417229353, 52.63677651143158) , Point(13.528418459697264, 52.62995795966922) , Point(13.54585514281595, 52.62205850701603) , Point(13.56192899374018, 52.61315478651281) , Point(13.57648503608635, 52.60333312435861) , Point(13.589383288701594, 52.592688686148705) , Point(13.600500105322267, 52.581324538061054) , Point(13.609729342604664, 52.56935063290036) , Point(13.616983346224249, 52.5568827315916) , Point(13.622193747008357, 52.5440412712636) , Point(13.625312061352123, 52.53095019147478) , Point(13.62631009242705, 52.517735730410585)))
liste_test = pd.DataFrame({
'order': [1, 2],
'bez': ['Region 1 (circ)', 'Region 2 (rect)'],
'gdf': [gdf_test1, gdf_test2],
'filter': [outline_test1, outline_test2]
})
#------Part 2: could be changed--------
gdf_lang_test = gpd.GeoDataFrame(columns=['bez', 'geometry'], geometry='geometry', crs='EPSG:4326')
for i, row in liste_test.iterrows():
for j, row2 in row['gdf'].iterrows():
ptX = row2['geometry'].x
ptY = row2['geometry'].y
gdf_this_test = gpd.GeoDataFrame([(row['bez'], Point(ptX, ptY))], columns=['bez', 'geometry'], geometry='geometry', crs='EPSG:4326') #jeder Messwert innerhalb eines gdf
gdf_lang_test = gpd.GeoDataFrame(pd.concat([gdf_lang_test, gdf_this_test], ignore_index=True)) #wird an neuen gdf angehängt
bland_test1 = gpd.read_file('https://raw.githubusercontent.com/isellsoap/deutschlandGeoJSON/main/2_bundeslaender/1_sehr_hoch.geo.json')
bland_test2 = gpd.read_file('https://raw.githubusercontent.com/isellsoap/deutschlandGeoJSON/main/4_kreise/1_sehr_hoch.geo.json')
args = [bland_test1, bland_test2, gdf_lang_test]
#-----------------------------------------------
#-------------Actual Code-----------------------
#-----------------------------------------------
fig, ax = plt.subplots(figsize=(15, 15))
bland_crs = args[0].crs #Returns: epsg:4326 (lower case!?)
data_crs = args[2].crs #Returns: EPSG:4326 (upper case!?)
ax = plt.axes(projection=ccrs.Mercator())
#-----PLOTTING DATA-----------------------------
#-Version 1----------
args[0].plot(ax=ax,facecolor="none", edgecolor='black', linewidths=1, alpha=1, figsize=(15,15)) #, transform=ccrs.PlateCarree())
args[1].plot(ax=ax,facecolor="none", edgecolor='grey', linewidths=0.5, alpha=1, figsize=(15,15)) #, transform=ccrs.PlateCarree()(
args[2].plot(ax=ax, zorder=2, markersize=6) #, transform=ccrs.PlateCarree())
#-Version 2----------
#ax.add_geometries(args[0]['geometry'], facecolor="none", edgecolor='black', crs=bland_crs)
#ax.add_geometries(args[0]['geometry'], facecolor="none", edgecolor='black', crs=bland_crs)
#ax.add_geometries(args[2]['geometry'], facecolor="none", edgecolor='black', crs=data_crs)
#-----PLOTTING Outlines-------------------------
n=0
for i, row in liste_test.iterrows():
n=n+1
polygon_temp = row['filter']
p = gpd.GeoSeries(polygon_temp, crs=data_crs)
args += (p,)
#-Version 1----
args[n+2].plot(ax=ax, facecolor="none") #,transform=ccrs.PlateCarree())
#-Version 2---
#ax.add_geometries(args[n+2], facecolor="none", edgecolor='black', crs=data_crs)
#----Calculate Extent---------------------------
xmin, ymin, xmax, ymax = args[2].total_bounds
xmin = xmin*0.998
xmax = xmax*1.002
ymin = ymin*0.998
ymax = ymax*1.002
xlim = ([xmin, xmax])
ylim = ([ymin, ymax])
ax.set_xlim(xlim)
ax.set_ylim(ylim)
#----PLOTTING Background image--------------
#ax = plt.axes(projection=ccrs.Mercator()) #if uncommented, the basemap is drawn on top of everything with max extent (whole earth)
url = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}.jpg'
ax.set_extent(ax.get_extent(), ccrs.Mercator())
image = cimgt.GoogleTiles(url=url)
ax.add_image(image, 1)
plt.show()
非常感谢您的帮助! :/
我找到了一种显示大部分数据的方法。这并不完全是我想要的,因为情节是在 PlateCarree 而不是 Mercator 中,但现在对我来说很好,所以我就这样吧。
我将绘图的投影设置移至其自身的初始化,并稍微更改了添加底图和数据的顺序。代码现在如下所示:
#-----------------------------------------------
#-------------Actual Code-----------------------
#-----------------------------------------------
fig, ax = plt.subplots(figsize=(15, 15), subplot_kw={'projection': ccrs.PlateCarree()})
bland_crs = args[0].crs #Returns: epsg:4326
data_crs = args[2].crs #Returns: EPSG:4326
#----Calculate Extent---------------------------
xmin, ymin, xmax, ymax = args[2].total_bounds
xmin = xmin*0.998
xmax = xmax*1.002
ymin = ymin*0.998
ymax = ymax*1.002
ax.set_extent([xmin, xmax, ymin, ymax], ccrs.PlateCarree())
#----PLOTTING Background image--------------
#ax = plt.axes(projection=ccrs.Mercator()) #if uncommented, the basemap is drawn on top of everything with max extent (whole earth)
url = 'https://server.arcgisonline.com/arcgis/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}.jpg'
image = cimgt.GoogleTiles(url=url)
ax.add_image(image, 13, interpolation='hanning', regrid_shape=3000)
#-----PLOTTING DATA-----------------------------
#-Version 1----------
#args[0].plot(ax=ax,facecolor="none", edgecolor='black', linewidths=1, alpha=1, figsize=(15,15)) #, transform=ccrs.PlateCarree())
#args[1].plot(ax=ax,facecolor="none", edgecolor='grey', linewidths=0.5, alpha=1, figsize=(15,15)) #, transform=ccrs.PlateCarree()(
args[2].plot(ax=ax, zorder=2, markersize=6) #, transform=ccrs.PlateCarree())
#-----PLOTTING Outlines-------------------------
n=0
for i, row in liste_test.iterrows():
n=n+1
polygon_temp = row['filter']
p = gpd.GeoSeries(polygon_temp, crs=data_crs)
args += (p,)
#-Version 1----
args[n+2].plot(ax=ax, facecolor="none") #,transform=ccrs.PlateCarree())
plt.show()
我还在
interpolation='hanning', regrid_shape=3000
中添加了 add_image
,以获得更好的图像质量,如此处推荐。