You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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坐标系不统一,跨坐标系做空间交集、包含判断时结果为空
解决步骤
  1. 先加校验代码定位空数据环节,每一步筛选后打印数据集规模,确认哪一步产出了空结果:
    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)
    
  2. 统一所有地理数据集的坐标系,避免空间计算错位:
    # 全部转WGS84坐标系
    africa_rivers = africa_rivers.to_crs(epsg=4326)
    lakes = lakes.to_crs(epsg=4326)
    country_shapefiles = country_shapefiles.to_crs(epsg=4326)
    
  3. 适配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')
    
  4. 根据第一步打印的湖泊名实际值,调整维多利亚湖的筛选规则,比如名称带后缀的话用模糊匹配:
    lake_victoria = africa_lakes.loc[africa_lakes['name'].str.contains('Lake Victoria', na=False)]
    
  5. 兜底规避matplotlib边界计算报错:手动给坐标轴设置固定绘图范围,避免自动计算拿不到边界:
    # 非洲区域大致经纬度范围
    ax.set_xlim(-20, 55)
    ax.set_ylim(-35, 38)
    

内容的提问来源于stack exchange,提问作者Exquisite_Poupon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 10:33:17