
项目名就叫Python-Proj光听名字就知道是个用Python折腾地图投影的小项目。可项目第一次跑起来第一个拦路虎就来了代码里明明写着CRS.from_epsg(4326)结果终端直接甩给我一句PROJ: proj_exception_create: unrecognized CRS: epsg:4326。这个报错我前前后后在五六台机器上踩过每一回都能感觉到血压在飙升。今天把这个问题的来龙去脉和最快解决路径彻底讲一遍该诊断的诊断该重装的重装保证你看完不用再瞎试。先解释清楚这是套什么东西免得新手一头雾水。Pyproj是PROJ地图投影库的Python绑定凡是做坐标系转换、地图投影、地理数据分析的Python项目底层十有八九靠它撑着GeoPandas、Cartopy、Folium这些耳熟能详的库全都挂着它。EPSG:4326就是WGS84经纬度坐标系是全世界地理数据最通用的基础坐标系。一个连EPSG:4326都认不出来的投影库约等于翻译软件突然不认识hello了手里所有坐标相关的活儿全得停摆。这篇文章适合所有用Python做地图、遥感、空间分析的人哪怕你只是刚照着教程装完Python准备跑第一个数据可视化脚本只要哪天撞上这个报错照下面的路子排查基本都能救回来。1. 先搞清楚为什么Python项目会找不到EPSG:43261.1 报错到底长什么样我先把最常见的三种现场摆出来你对号入座。第一种是用pyproj直接查坐标系。from pyproj import CRS crs CRS.from_epsg(4326)输出CRSError: Invalid projection: epsg:4326: (Internal Proj Error: proj_exception_create: unrecognized CRS / Request for file database failed)第二种是配合GeoPandas读数据看起来像是在shp文件读取阶段突然崩掉。import geopandas as gpd gdf gpd.read_file(some_file.shp)报错尾段常常长这样RasterioIOError: PROJ: proj_create: unrecognized CRS第三种是用转换工具做坐标转换时翻车。from pyproj import Transformer transformer Transformer.from_crs(EPSG:4326, EPSG:3857)报错核心还是那句unrecognized CRS有时候会多一行database file not found之类的提示。三种症状虽然表现不同本质上是同一个问题PROJ核心库的数据库文件proj.db缺失、损坏或者路径没对上。记住这句话后面所有排查思路都围着它转。看到这类报错别急着怀疑代码逻辑先往环境上想。1.2 proj.db是什么为什么它一掉链子全盘皆输老版本的PROJ6.0以前把坐标系的定义直接写死在源码里编译完成就自带全宇宙的坐标系知识根本不存在找不到的可能。但从6.0开始PROJ彻底换了设计思路把几千个坐标参考系的定义、基准面参数、单位换算信息全部塞进一个叫proj.db的SQLite数据库文件。pyproj在运行时动态读取这个数据库CRS.from_epsg(4326)这个操作本质上就是拿着4326这个编号去数据库里查一行记录。用生活类比来解释旧版PROJ是随身带了一本打印好的电话簿翻哪页都行新版PROJ变成了一个需要读卡的设备proj.db就是那张SIM卡。卡没插好哪怕你背得出4326这个号码设备也拨不出去。这解释了一个很多人困惑的现象——为什么连EPSG:4326这种最最基础的坐标系都找不到因为问题压根不在4326这个数字本身而在承载它的数据库文件没有正常工作。实际项目里proj.db出问题通常集中在几种情况用pip直接安装pyproj时数据文件没被正确放进包目录conda和pip混用导致版本错配conda环境里先装了pyproj后面又用pip升级或降级数据库版本对不上某些精简版或镜像源安装包把数据文件剔除了再就是迁移代码、同步环境时只拷贝了site-packages的一部分把proj数据目录落在旧机器上。弄懂这几条你就知道这不是代码逻辑的错误而是环境不完整。方向要是搞错跑去逐行改代码改到天亮也改不出来。1.3 从报错到定位一条快速诊断命令在动手解决之前先用一句话给环境做个诊断省得瞎忙活。python -c import pyproj; print(pyproj.__version__) python -c from pyproj.datadir import get_data_dir; print(get_data_dir())第一句看pyproj版本号第二句看它实际去哪个目录找数据文件。如果第二个路径打印出来之后你用ls检查目录发现里面没有proj.db或者这个路径本身压根不存在那问题基本实锤了。这一步花不到一分钟却能帮你省下后面换环境、重装依赖的大量时间。2. 三种解决方案怎么选conda、环境变量还是版本重装2.1 先看对比表再决定对症下药之前我先把能用得上的方案摆一张表方便你按自己的环境条件对号入座。方案适用场景优点缺点conda重装pyproj、proj用的是conda环境不排斥调整依赖一条命令解决数据文件自动配好需要下载网络包会改动环境手动设置PROJ_LIB环境变量pip环境装好了只是数据路径没对上最快不动依赖换机器要重新配只解决路径不解决版本pip按指定版本重装项目锁死版本或镜像源缺数据文件精准控制版本版本组合坑多容易踩雷我自己在实操中的选择标准很简单能用conda就优先conda。这是因为conda把PROJ核心库、proj.db和pyproj当作一套整体来管理能自动算好三者之间的匹配关系pip则是各管各的装出来有时候就缺胳膊少腿。但有时候项目限制了你没法用conda那后面两条路就是救命稻草。2.2 为什么conda重装是省心路线pyproj、proj、geopandas这一族库实际上涉及C扩展、数据文件和Python包三层依赖。conda-forge仓库里这几样东西是打包成一套的版本之间做过联动测试安装时会把proj可执行程序、proj.db、pyproj的编译产物一起装好目录结构也按约定配好。而pip安装pyproj时数据文件其实也会带问题在于很多环境下pip安装过程不会帮你配好PROJ_LIB或者因为缓存、镜像的原因导致文件不完整。conda则没有这个烦恼它有一套约定好的目录结构装完就能直接读。这也是为什么老手处理类似问题第一反应都是把相关包全部统一重装一遍而不是费劲去研究路径配置。2.3 什么情况才选另外两条路手动设置PROJ_LIB适合什么场景比如你手上有个正在跑的生产服务不想大动依赖或者pyproj明明装着只是当前用户的环境变量丢了路径。这时候直接指一条路过去最快一行export的事风险最小。指定版本重装适合什么场景项目锁定了pyproj版本比如老代码只兼容pyproj 2.x你不能顺手升到3.x或者你怀疑镜像源给了一个残缺的安装包。这时候清缓存、精确指定版本重装比整体换环境更安全。话虽如此版本矩阵的坑依然存在具体怎么躲我在第4章里单独讲。3. 手把手实操从诊断到恢复的完整流程3.1 动手前先做环境检查不管选哪条方案先把现场情况摸清楚。除了前面说的版本和目录检查还要分清操作系统——Windows、Linux、macOS三者的路径习惯差得挺多但排查思路完全一致。Linux环境下conda装出来的proj.db通常在/opt/conda/share/proj/这种位置系统级pip装的通常在/usr/share/proj/或/usr/local/share/proj/。Windows环境下常见位置是C:\Users\用户名\AppData\Local\Programs\Python\Python39\Lib\site-packages\pyproj\proj_dir\share\proj\proj.db。先确认文件在哪后面设置PROJ_LIB才有依据。这一步省不得因为很多重装完还是报错的案例其实就是proj.db装在A路径pyproj偏去B路径找两边各说各话。3.2 方案一实操conda统一重装进入目标conda环境执行conda install -c conda-forge pyproj3.4.2如果你用的是geopandas全家桶干脆一起重装conda install -c conda-forge pyproj proj geopandas这里有个操作细节很多人不知道如果conda提示依赖冲突别急着加--force-reinstall强行覆盖。先检查当前环境里proj核心库是什么版本再让conda自己解算。多数冲突都是因为proj和pyproj版本不匹配conda会自动挑一套正确的组合覆盖掉。等它跑完用conda list | grep -E pyproj|proj确认版本都在就能进入验证环节。我当时就是这样把一台Ubuntu服务器上乱七八糟的环境理顺的前后不超过五分钟。3.3 方案二实操手动指定PROJ_LIB如果没法动conda那就手动告诉PROJ数据文件在哪。先全域搜proj.dbfind / -name proj.db 2/dev/nullWindows上可以用where /r C:\ proj.db找到目录后临时设置环境变量export PROJ_LIB/path/to/proj_data然后重新打开Python跑一次验证成功后再把变量写进~/.bashrc永久生效。Windows的永久配置去系统属性 - 环境变量里新建一个变量名PROJ_LIB变量值填proj.db所在目录即可。这个方案的好处是快坏处是换一台机器就得重配一次所以适合应急不适合作为长期依赖。3.4 方案三实操pip清缓存重装前面反复强调pip装pyproj不等于数据文件就位。如果你是pip环境又怀疑是缓存或残缺包的问题清缓存重装pip cache purge pip install --force-reinstall --no-cache-dir pyproj3.4.2重装完如果还是不行再检查一次路径与proj.db是否存在。倘若文件存在但版本与pyproj要求的对不上就需要回到版本矩阵里仔细配对。pyproj官方文档有详细的版本兼容表别只看pyproj自己的版本号还要确认它编译链接的PROJ核心版本。这一步最容易让人抓狂因为报错长得一模一样实际原因可能差着十万八千里。3.5 验证是否恢复一段自检脚本环境改完不要直接跑业务代码先跑一段自检把问题边界划清楚from pyproj import CRS, Transformer crs CRS.from_epsg(4326) print(EPSG:4326 name:, crs.name) transformer Transformer.from_crs(EPSG:4326, EPSG:3857, always_xyTrue) x, y transformer.transform(116.4, 39.9) # 经度、纬度 print(Web墨卡托坐标:, x, y)输出正常说明环境OK问题不在PROJ。这步如果过不了再回头排查环境。这段自检脚本我至今保存在常用代码片段里每次新建环境都要跑一遍算是排障的第一道防线。4. 踩坑实录常见错误与排查技巧4.1 conda和pip混装的深水区我遇到最多的坑就是conda环境里混用pip装了pyproj两边版本对不上。比如conda的proj核心库是7.2.0然后pip装了个新版pyproj它编译时链接的是PROJ 8.x运行时就按8.x的数据库格式去找跟conda自带的7.x数据文件不匹配结果自然就是unrecognized CRS。这种情况在数据科学环境里特别常见因为conda装数据分析全家桶pip又单独装地图库两边互相不知道对方的存在。排查思路很直接把两边版本都打出来对照。projinfo --version python -c import pyproj; print(pyproj.proj_version_str)projinfo是PROJ核心库自带的命令行工具pyproj.proj_version_str是pyproj编译时链接的PROJ版本。两个版本差太多就该统一来源要么全conda要么全pip。最忌讳的就是哪个缺了装哪个这在PROJ的依赖体系里行不通。4.2 一个容易忽略的坑proj.db装上了但路径不对有时候proj.db其实是装了的但pyproj去另一个路径找。这种错位通常发生在多Python版本并存、虚拟环境套娃、或者用户级site-packages和系统级site-packages混用的情况下。这时候路径检查就是唯一的救命稻草。学诊断命令的价值就在这里——它直接告诉你pyproj的视线范围你只管把文件放到它看得见的地方。遇到这类错位我还有一个小技巧直接对比pyproj.datadir.get_data_dir()的输出和实际proj.db所在路径两者不一致就说明有东西在中间改变了环境变量。可以用以下命令单独抓一下当前环境里是否已有PROJ_LIB设置python -c import os; print(os.environ.get(PROJ_LIB))有输出的话看看这个指向是否存在且正确很多环境错位就是某个配置文件里残留了一个旧的路径。4.3 常见问题速查表症状可能原因快速处理Could not find EPSG:4326proj.db缺失或路径不对重装pyproj / 设置PROJ_LIBproj_exception_create: unrecognized CRSPROJ数据库版本不匹配conda统一重装pyproj和proj读shp时后台报proj错误GeoPandas底层proj配置坏了conda install geopandas 整体重装自定义坐标系找不到自定义CRS未写入proj.db用WKT或Proj4字符串传入换机器跑代码就挂环境没同步完整用conda env export导出yaml复现在这个速查表之外还有一个诊断技巧值得收藏当报错信息里出现Request for file database failed时基本可以停止怀疑代码直接进入环境排障流程。这个提示是PROJ数据库请求失败的标志性信息我看到它就知道该往哪查。4.4 两个容易带偏的细节第一个别自定义坐标系找不到时不必非得修改数据库。直接把WKT文本传给CRS.from_user_input()或者用CRS.from_proj4()传Proj4字符串都能绕开proj.db缺失的障碍。这个方法在数据文件缺得不多、又不想动环境的时候特别实用尤其是项目里只有一两个自定义坐标系的情况。提示绝对不要手动往proj.db里插记录。我见过有人在DB Browser里手插了一行4326定义结果SQLite表结构对不上整个pyproj初始化直接崩掉最后只能重装。数据库的结构和字段受版本控制手改就是自找麻烦。自定义坐标系用代码传WKT就好别去动数据库本身。第二同步环境时别只拷贝site-packages。很多团队喜欢把整个Python环境打包拷贝到另一台机器但proj数据文件有时不在site-packages的标准结构里拷贝一半就丢。稳妥做法是conda环境用conda env export environment.yml导出完整依赖pip环境用pip freeze requirements.txt并额外带上proj数据目录这样才会排除版本错位的隐患。最后分享一点个人体会。踩过这么多次坑之后我的原则现在很固定凡是涉到proj这一族的库要么全用conda管理要么全用pip管理绝不混搭。每次新建环境装完pyproj的第一件事就是跑一遍自检脚本确认proj.db能正常读到。另外别小看PROJ_LIB这个环境变量路径出问题时它能救命但也别过度依赖它它只能解决路径问题解决不了版本错配。真正根治的办法永远是先把依赖关系理干净。这套思路帮我在几台服务器和笔记本上快速治好了同样的病希望也能帮你少走一段弯路。