基于PyQt5和Cartopy的离线可分发应用打包后Shapefile加载失败问题求助
解决Cartopy单文件打包后离线无法加载Shapefile的问题
我之前也踩过一模一样的坑!问题根源有两个:一是你打包时可能没包含Shapefile的所有关联文件,二是直接给add_feature传文件路径的方式不对——Cartopy并不会把这个路径当成本地文件读取,反而会触发它默认的在线下载逻辑。下面是具体的解决方案:
1. 正确打包所有Shapefile关联文件
Shapefile不是单一文件,它由.shp、.shx、.dbf、.prj等多个配套文件组成,打包时必须把这些文件全部包含进去。使用PyInstaller的--add-data参数时,要用通配符匹配所有相关文件:
Windows环境打包命令:
pyinstaller --onefile --add-data "data/ne_50m_coastline.*;data/" your_app.py
Linux/macOS环境打包命令:
pyinstaller --onefile --add-data "data/ne_50m_coastline.*:data/" your_app.py
这里的data/是打包后文件会被解压到的相对目录,和你代码中读取的路径保持一致即可。
2. 修改代码,手动加载本地Shapefile
不要直接把文件路径传给add_feature,因为Cartopy会把字符串参数识别为内置Feature的名称(比如'coastline'),进而尝试在线下载官方资源。正确的做法是用cartopy.io.shapereader.Reader读取本地文件,再转成ShapelyFeature添加到地图:
import sys import os import cartopy.crs as ccrs import cartopy.io.shapereader as shpreader from cartopy.feature import ShapelyFeature # 封装资源路径获取函数,兼容开发和打包后环境 def get_resource_path(relative_path): try: # 打包后运行时,资源会被解压到sys._MEIPASS临时目录 base_path = sys._MEIPASS except Exception: # 开发环境下,用当前脚本所在目录 base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) # 获取打包后的Shapefile完整路径 coastline_shp_path = get_resource_path("data/ne_50m_coastline.shp") # 读取Shapefile并创建Feature对象 shape_reader = shpreader.Reader(coastline_shp_path) coastline_feature = ShapelyFeature( shape_reader.geometries(), ccrs.PlateCarree(), # 这里要和你的地图投影保持一致 edgecolor="black", facecolor="none", zorder=3 ) # 将Feature添加到地图上 map.add_feature(coastline_feature)
3. 额外注意事项
- 不要用
os.chdir(sys._MEIPASS)改变全局工作目录,这可能导致其他资源(比如你之前的CSV文件)加载路径混乱,用上面的get_resource_path函数单独获取每个资源的路径更稳妥。 - 确保你本地的Shapefile是完整的,从Natural Earth下载对应分辨率的文件后,把所有关联文件都放到
data/目录下再打包。 - 测试时可以先在离线环境下运行开发版代码,确认本地Shapefile能正常加载后再打包,排除文件本身的问题。
这样修改后,打包后的单文件程序就能在离线环境下直接读取内置的Shapefile,不会再触发在线下载了。
内容的提问来源于stack exchange,提问作者TeenySceney
相关产品推荐
相关产品推荐

