geopandas绘图报'NoneType' object has no attribute 'extents'错
问题描述
我参考Python河流地图绘制教程编写绘图代码,已和教程给出的代码逐行核对完全一致,代码如下:
import geopandas as gpd import matplotlib.pyplot as plt africa_rivers = gpd.read_file('~/Python/Projects/River Maps/africa/afrivs.shp') lakes = gpd.read_file('~/Python/Projects/River Maps/ne_10m_lakes/ne_10m_lakes.shp') country_shapefiles = gpd.read_file('~/Python/Projects/River Maps/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp') africa = country_shapefiles.loc[country_shapefiles['CONTINENT'] == 'Africa'] africa_lakes = gpd.sjoin(lakes, africa, op = 'intersects') africa_lakes = lakes.loc[lakes.index.isin(africa_lakes.index.tolist())] excluded_rivers = gpd.sjoin(africa_rivers, africa_lakes, op = 'within') africa_rivers = africa_rivers.loc[~africa_rivers.index.isin(excluded_rivers.index.tolist())] lake_victoria = africa_lakes.loc[africa_lakes['name'] == 'Lake Victoria'] funny_features = gpd.sjoin(lake_victoria, africa_rivers, op = 'within') fig, ax = plt.subplots(facecolor='#FCF6F5FF') fig.set_size_inches(5,7) lake_victoria.plot(ax = ax, color = 'black', alpha = 0.1) funny_features.plot(ax = ax, color = 'blue', lw = 0.4, alpha = 1) ax.axis('off') plt.show()
运行代码时触发报错,首先弹出UserWarning提示待绘制的GeoDataFrame为空,无内容可展示,随后抛出AttributeError异常,错误栈最终定位到matplotlib坐标轴紧边界计算逻辑,提示'NoneType' object has no attribute 'extents',完整错误栈如下:
~/opt/anaconda3/envs/PopulationDensity/lib/python3.7/site-packages/geopandas/plotting.py:681: UserWarning: The GeoDataFrame you are attempting to plot is empty. Nothing has been displayed. UserWarning, AttributeError Traceback (most recent call last) ~/opt/anaconda3/envs/PopulationDensity/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj) 339 pass 340 else: --> 341 return printer(obj) 342 # Finally look for special method names 343 method = get_real_method(obj, self.print_method) ~/opt/anaconda3/envs/PopulationDensity/lib/python3.7/site-packages/IPython/core/pylabtools.py in print_figure(fig, fmt, bbox_inches, base64, **kwargs) 149 FigureCanvasBase(fig) 150 --> 151 fig.canvas.print_figure(bytes_io, **kw) 152 data = bytes_io.getvalue() 153 if fmt == 'svg': ~/opt/anaconda3/envs/PopulationDensity/lib/python3.7/site-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs) 2298 if bbox_inches == "tight": 2299 bbox_inches = self.figure.get_tightbbox( -> 2300 renderer, bbox_extra_artists=bbox_extra_artists) 2301 if pad_inches is None: 2302 pad_inches = rcParams['savefig.pad_inches'] ~/opt/anaconda3/envs/PopulationDensity/lib/python3.7/site-packages/matplotlib/figure.py in get_tightbbox(self, renderer, bbox_extra_artists) 1630 1631 for a in artists: -> 1632 bbox = a.get_tightbbox(renderer) 1633 if bbox is not None and (bbox.width != 0 or bbox.height != 0): 1634 bb.append(bbox) ~/opt/anaconda3/envs/PopulationDensity/lib/python3.7/site-packages/matplotlib/axes/_base.py in get_tightbbox(self, renderer, call_axes_locator, bbox_extra_artists, for_layout_only) 4660 clip_extent = mtransforms.Bbox.intersection( 4661 clip_extent, axbbox) -> 4662 if np.all(clip_extent.extents == axbbox.extents): 4663 # clip extent is inside the Axes bbox so don't check 4664 # this artist AttributeError: 'NoneType' object has no attribute 'extents'
报错原因
- 核心触发点是筛选得到的
funny_features甚至lake_victoriaGeoDataFrame为空,没有任何可绘制的几何要素 - 空GeoDataFrame调用
.plot()方法时不会在坐标轴上添加有效绘图元素,坐标轴没有明确的边界范围,后续matplotlib执行tight bbox自动计算边界时拿不到有效的边界对象,就会触发访问NoneType的extents属性的错误 - 空数据集的常见诱因:
- 使用的Natural Earth湖泊、行政边界数据集版本和教程不一致,存储湖泊名称的字段名不是
name,或者维多利亚湖的名称字段值带后缀、大小写不匹配,导致按name == 'Lake Victoria'筛选不到对应要素 - geopandas版本差异:0.9.0之后的版本中,空间关联
sjoin方法的空间谓词参数名从op改为predicate,旧参数写法会导致空间关联结果异常 - 读入的多个shapefile坐标系不统一,跨坐标系做空间交集、包含判断时结果为空
- 使用的Natural Earth湖泊、行政边界数据集版本和教程不一致,存储湖泊名称的字段名不是
解决步骤
- 先加校验代码定位空数据环节,每一步筛选后打印数据集规模,确认哪一步产出了空结果:
print("非洲湖泊数据集规模:", africa_lakes.shape) # 打印字段名和前20个湖泊名,确认名称字段和维多利亚湖的实际值 print("湖泊数据集字段:", africa_lakes.columns.tolist()) print("湖泊名称样例:", africa_lakes['name'].drop_duplicates().head(20).tolist()) print("维多利亚湖筛选结果规模:", lake_victoria.shape) print("湖内河流要素筛选结果规模:", funny_features.shape) - 统一所有地理数据集的坐标系,避免空间计算错位:
# 全部转WGS84坐标系 africa_rivers = africa_rivers.to_crs(epsg=4326) lakes = lakes.to_crs(epsg=4326) country_shapefiles = country_shapefiles.to_crs(epsg=4326) - 适配geopandas版本,把所有
sjoin方法里的op参数替换为predicate:africa_lakes = gpd.sjoin(lakes, africa, predicate = 'intersects') excluded_rivers = gpd.sjoin(africa_rivers, africa_lakes, predicate = 'within') funny_features = gpd.sjoin(lake_victoria, africa_rivers, predicate = 'within') - 根据第一步打印的湖泊名实际值,调整维多利亚湖的筛选规则,比如名称带后缀的话用模糊匹配:
lake_victoria = africa_lakes.loc[africa_lakes['name'].str.contains('Lake Victoria', na=False)] - 兜底规避matplotlib边界计算报错:手动给坐标轴设置固定绘图范围,避免自动计算拿不到边界:
# 非洲区域大致经纬度范围 ax.set_xlim(-20, 55) ax.set_ylim(-35, 38)
内容的提问来源于stack exchange,提问作者Exquisite_Poupon
相关产品推荐
相关产品推荐

