
1. 从一个让人抓狂的坐标偏移说起如果你处理过遥感影像、无人机正射图或者任何带地理坐标的栅格数据大概率遇到过这种场景同一块区域的两份GeoTIFF用GIS软件打开后叠在一起一份严丝合缝另一份却整体偏移了几十米甚至几百米。你检查了像素分辨率没问题检查了影像范围也对得上最后打开元数据一看投影坐标系Projected CS的字段里写着一串看不懂的数字代码而另一份写的是完整的WKT字符串。问题就出在这里——投影类型代码Projected CS Type Codes / CoordRefSysCode的解析出了岔子。这篇内容就是围绕GeoTIFF里这个看似不起眼、实则决定数据生死的字段展开的。我会把投影坐标系代码的来龙去脉、常见取值、在GeoTIFF文件里的存储位置、不同软件读取时的行为差异以及实际项目中怎么排查和修复相关问题全部拆开讲清楚。适合做GIS开发、遥感数据处理、测绘内业、空间数据入库的朋友参考也适合刚接触地理空间数据、被坐标问题折磨过的新手。读完你至少能做到两件事看到一串投影代码能快速判断它代表什么坐标系以及遇到坐标偏移时知道从哪个字段下手排查。先说清楚一个基本概念。GeoTIFF本质上还是TIFF只是在一堆标准标签之外塞进了地理相关的私有标签。其中跟坐标系相关的核心标签是GeoKeyDirectoryTag它内部是一系列键值对每个键有一个编号KeyID值可能是短整型、双精度或者ASCII字符串。投影坐标系类型对应的键是ProjectedCSTypeGeoKey编号3072。这个键存储的值就是所谓的Projected CS Type Code也就是投影坐标系类型代码。它通常是一个EPSG代码比如32650代表WGS 84 / UTM zone 50N4326代表WGS 84地理坐标系虽然4326是地理坐标系但在某些实现里也会出现在这个位置这是坑之一。为什么这个代码这么重要因为它是GIS软件判断“这份数据在哪个投影下”的第一手依据。代码对了软件就能正确重建坐标系做投影转换、面积量算、图层叠加都不会出问题。代码错了或者缺失软件要么报错要么用默认值兜底要么直接按像素坐标处理结果就是偏移、变形、无法配准。更麻烦的是不同软件对同一个代码的解析策略还不一样有的严格按EPSG库查有的有自己的映射表有的遇到未知代码就静默忽略。这就导致同一份文件在不同平台打开表现可能完全不同。我见过太多案例数据生产方用某款软件导出GeoTIFF代码写的是自定义值自己用没问题交给下游单位后对方用另一款软件打开就偏了。排查半天最后发现就是Projected CS Type Code这个字段在作祟。所以这个知识点不是学术问题是实打实的生产问题。2. GeoTIFF里坐标系代码到底存在哪2.1 GeoKeyDirectoryTag的结构拆解要理解投影类型代码得先搞清楚它在文件里的物理位置。GeoTIFF规范定义了一个叫GeoKeyDirectoryTag的标签编号34735类型是SHORT数组。这个数组不是随便排的它有严格的结构前四个SHORT是头部信息分别是版本号、修订号、次要修订号、键的数量从第五个SHORT开始每四个SHORT组成一个键条目依次是键ID、值存储位置TIFFTagLocation、值数量Count、值本身或值偏移Value_Offset。举个例子假设我们要写入ProjectedCSTypeGeoKey键ID是3072。如果值直接存在这个条目里TIFFTagLocation就是0Count是1Value_Offset就是那个EPSG代码比如32650。如果值存在别的标签里比如存成ASCII字符串TIFFTagLocation就会指向那个标签的编号Value_Offset变成偏移量。这种设计很灵活但也给解析带来了复杂度。实际解析时你需要先读GeoKeyDirectoryTag拿到键的数量然后逐个遍历键条目。遇到3072这个键再根据TIFFTagLocation决定去哪里取值。如果TIFFTagLocation是0直接取Value_Offset如果不是0就要跳到对应标签的对应偏移位置去读。很多简易解析库就是在这里偷懒只处理TIFFTagLocation为0的情况遇到复杂存储就返回空值导致坐标系丢失。注意GeoKeyDirectoryTag里的值都是SHORT类型也就是16位无符号整数范围0到65535。EPSG代码超过65535的比如某些自定义坐标系就不能直接存在这里必须走TIFFTagLocation跳转的方式。这是很多自定义投影数据出问题的根源。2.2 ProjectedCSTypeGeoKey与其他相关键的关系ProjectedCSTypeGeoKey不是孤立的它和另外几个键配合工作。最相关的是GeographicTypeGeoKey编号2048它定义地理坐标系还有ProjectionGeoKey编号3074、ProjCoordTransGeoKey编号3075、ProjLinearUnitsGeoKey编号3076等。当ProjectedCSTypeGeoKey的值是一个标准EPSG代码时其他这些键通常可以省略因为EPSG代码本身已经包含了完整的投影定义。但当ProjectedCSTypeGeoKey的值是32767用户自定义时就必须靠其他键来拼出完整的投影参数。这里有个常见的误解很多人以为只要ProjectedCSTypeGeoKey写对了坐标系就没问题。实际上如果同时存在ProjectedCSTypeGeoKey和其他投影参数键不同软件的优先级策略不同。有的软件优先用ProjectedCSTypeGeoKey有的优先用显式参数有的会做一致性校验不一致就报错。我遇到过一份数据ProjectedCSTypeGeoKey写的是32650但ProjLinearUnitsGeoKey写的是英尺单位结果软件按米处理面积算出来差了十倍多。所以这几个键要一起看不能只盯一个。另外GeographicTypeGeoKey和ProjectedCSTypeGeoKey之间也有约束关系。如果ProjectedCSTypeGeoKey是32650WGS 84 / UTM 50N那么GeographicTypeGeoKey应该是4326WGS 84。如果写成了别的比如4214Beijing 1954那就矛盾了。有些软件会以ProjectedCSTypeGeoKey为准忽略GeographicTypeGeoKey有些会报错有些会按GeographicTypeGeoKey重建地理坐标系再用投影参数投影结果可能完全不同。这种不一致在数据交换中非常常见尤其是跨单位、跨软件协作时。2.3 代码在文件中的实际存储示例为了更直观我用一个简化的十六进制视角来看。假设GeoKeyDirectoryTag的内容如下每个SHORT用两个字节表示这里用十进制示意[1, 1, 0, 3, 3072, 0, 1, 32650, 2048, 0, 1, 4326, 3076, 0, 1, 9001]解读一下头部是版本1、修订1、次要修订0、共3个键。第一个键ID 3072TIFFTagLocation 0Count 1值32650即ProjectedCSTypeGeoKey为32650。第二个键ID 2048值4326即GeographicTypeGeoKey为4326。第三个键ID 3076值9001即ProjLinearUnitsGeoKey为9001米。这是一个非常标准、干净的配置任何GIS软件都能正确解析。再看一个有问题的情况[1, 1, 0, 2, 3072, 0, 1, 32767, 3074, 0, 1, 16050]这里ProjectedCSTypeGeoKey是32767表示用户自定义。然后靠ProjectionGeoKey3074的值16050来定义投影方法。但16050这个代码是否在软件的EPSG库里存在如果不存在软件就不知道具体投影参数只能报错或忽略。更糟的是如果连ProjCoordTransGeoKey、ProjLinearUnitsGeoKey都没写那这个坐标系就是残缺的无法使用。我实际处理过一批数据生产方用某开源库导出ProjectedCSTypeGeoKey全部写成32767然后只写了ProjectionGeoKey没写其他参数。结果在专业GIS软件里打开坐标系显示为“未知”但影像位置看起来又大致对——因为软件用了文件里的仿射变换参数ModelPixelScaleTag和ModelTiepointTag来定位没有做投影转换。这种“看起来对”最危险一旦做面积量算或与其他投影数据叠加问题就暴露了。3. 常见投影类型代码速查与含义解析3.1 EPSG代码的编码规律Projected CS Type Code绝大多数情况下就是EPSG代码。EPSG代码不是随便编的它有一定的分段规律。了解这些规律能帮你快速判断一个代码大概代表什么坐标系不用每次都查数据库。地理坐标系Geographic CRS主要集中在4000到4999段比如4326是WGS 844490是CGCS20004214是Beijing 1954。投影坐标系Projected CRS则分布在2000到3999、以及32600到32700等段。其中UTM系列非常规整32601到32660是WGS 84 / UTM zone 1N到60N32701到32760是WGS 84 / UTM zone 1S到60S。所以看到32650立刻能反应出是北半球UTM第50带中央经线是117度因为zone 50的中央经线是50*6-183117。看到32750就是南半球第50带。国内常用的投影坐标系代码也有规律。CGCS2000的高斯-克吕格投影3度带和6度带都有对应的EPSG代码段。比如CGCS2000 / 3-degree Gauss-Kruger zone 39EPSG代码是4547CGCS2000 / Gauss-Kruger zone 20代码是4499。这些代码在数据交换中经常出现尤其是测绘系统内部。如果你拿到一份数据ProjectedCSTypeGeoKey是4547就应该知道这是CGCS2000的3度带投影中央经线117度带号39。提示EPSG代码不是一成不变的官方会新增、废弃、修改代码。比如某些旧代码被标记为deprecated新代码替代。如果你的软件EPSG库版本较老可能不认识新代码就会报未知。所以保持GIS软件和投影库更新是必要的但也要注意更新后旧代码的兼容性。3.2 国内常用投影代码对照表下面这张表整理了国内项目中高频出现的Projected CS Type Code方便快速查阅。注意这些代码对应的坐标系名称和适用范围实际使用时还要结合数据的具体情况。EPSG代码坐标系名称投影类型适用场景4326WGS 84地理坐标系全球定位、GPS数据4490CGCS2000地理坐标系国家大地坐标系4214Beijing 1954地理坐标系历史数据32650WGS 84 / UTM zone 50N横轴墨卡托东经114-120度32750WGS 84 / UTM zone 50S横轴墨卡托南半球对应区域4547CGCS2000 / 3-degree Gauss-Kruger zone 39高斯-克吕格中央经线117度4499CGCS2000 / Gauss-Kruger zone 20高斯-克吕格6度带带号203857WGS 84 / Pseudo-Mercator伪墨卡托Web地图切片3395WGS 84 / World Mercator墨卡托航海、全球图这张表里3857值得单独说。它是Web地图的事实标准很多在线地图切片用的就是它。但3857不是严格意义上的投影坐标系它是基于球体的伪墨卡托和椭球体的墨卡托有细微差别。有些GeoTIFF的ProjectedCSTypeGeoKey写3857但实际数据是用椭球体参数算的叠加时会有米级偏差。这种问题在大比例尺下不明显小比例尺下可能就露馅了。3.3 自定义代码与用户定义投影的处理当ProjectedCSTypeGeoKey的值为32767时表示用户自定义投影。这时候必须靠其他GeoKey来补全定义。具体需要哪些键取决于投影类型。以横轴墨卡托为例至少需要ProjectionGeoKey投影方法代码、ProjCoordTransGeoKey坐标转换代码、ProjLinearUnitsGeoKey线性单位、ProjStdParallel1GeoKey标准纬线1、ProjNatOriginLongGeoKey自然原点经度、ProjNatOriginLatGeoKey自然原点纬度、ProjFalseEastingGeoKey假东、ProjFalseNorthingGeoKey假北等。这些键的编号和取值都有规范写错一个投影就偏了。我见过一个案例自定义横轴墨卡托投影其他参数都对就是ProjFalseEastingGeoKey写成了50000000多了一个零结果整个影像向东偏移了450公里。排查时逐项核对参数才发现这个低级错误。所以自定义投影的数据一定要逐键核对最好用专业工具导出WKT对比。另外有些软件在导出自定义投影时会把ProjectedCSTypeGeoKey写成32767但同时在GeoAsciiParamsTag里写一个WKT字符串。这种情况下不同软件的行为差异很大有的优先读WKT有的优先读GeoKey有的两者都读然后做一致性校验。如果WKT和GeoKey不一致就会出问题。我的建议是如果要用自定义投影尽量保证GeoKey和WKT表达的是同一个坐标系并且用多种软件交叉验证。4. 代码解析错误的典型表现与排查方法4.1 坐标偏移的几种典型模式Projected CS Type Code解析错误导致的坐标偏移不是随机的它有几种典型模式。识别这些模式能帮你快速定位问题。第一种是整体平移。影像所有像素向同一个方向偏移相同的距离。这通常是因为投影参数中的假东、假北或者中央经线错了。比如把3度带的中央经线按6度带算偏移量就是固定的。这种偏移最容易发现因为形状没变只是位置不对。第二种是缩放变形。影像看起来位置大致对但尺寸不对面积算出来偏大或偏小。这通常是线性单位错了比如把米当成英尺或者把英尺当成米。1米约等于3.28英尺如果单位搞反面积会差10倍以上。这种问题在只做可视化时不容易发现一做量算就暴露。第三种是旋转或剪切。影像看起来是斜的或者形状发生了扭曲。这通常是投影方法错了比如把横轴墨卡托当成兰勃特等角圆锥或者把投影参数张冠李戴。这种问题最严重因为数据已经不可用了必须重新投影。第四种是无偏移但无法叠加。影像自身位置看起来对但和别的图层叠不上或者叠加后错位。这通常是地理坐标系不一致比如一个是WGS 84一个是CGCS2000两者差异虽然只有几十厘米到几米但在高精度场景下就是问题。这种最隐蔽因为单看一份数据看不出问题。实操心得遇到坐标问题先不要急着改数据。第一步是导出两份数据的完整坐标系信息用WKT格式对比。WKT里会明确写出基准面、椭球体、投影方法、各项参数。逐项对比差异点就是问题所在。很多GIS软件都支持复制WKT这个操作比看EPSG代码更直接。4.2 用命令行工具快速检查GeoKey排查Projected CS Type Code最直接的方法是读取GeoTIFF的GeoKeyDirectoryTag。GDAL提供了命令行工具可以快速查看。比如用gdalinfo命令gdalinfo -json your_file.tif在输出的JSON里找到coordinateSystem部分里面会包含wkt和dataAxisToSRSAxisMapping等信息。WKT里会明确写出PROJCRS或GEOGCRS以及对应的EPSG代码如果有的话。如果WKT里显示的是unnamed或者没有EPSG标识说明ProjectedCSTypeGeoKey可能缺失或无效。更底层的方法是直接用listgeo工具来自libgeotifflistgeo -d your_file.tif这个命令会打印出所有GeoKey的键值对包括ProjectedCSTypeGeoKey。你可以直接看到3072这个键的值是多少。如果值是32767就要继续看其他投影参数键。如果值是0或者不存在说明坐标系未定义。还有一个Python的方法用rasterio库import rasterio with rasterio.open(your_file.tif) as src: print(src.crs) print(src.crs.to_epsg())src.crs会返回一个CRS对象to_epsg()会尝试提取EPSG代码。如果返回None说明没有对应的EPSG代码可能是自定义投影或者坐标系缺失。这个方法适合批量检查写个循环就能扫一遍目录下所有文件。4.3 不同软件解析行为的差异对比同一个Projected CS Type Code在不同软件里的解析结果可能不同。这不是软件bug而是因为各家的EPSG库版本、解析策略、容错机制不一样。下面这张表对比了几种常见软件的行为供参考。软件/库解析策略遇到未知代码遇到32767优先级GDAL严格按EPSG库报错或返回空读其他GeoKeyGeoKey优先ArcGIS自有投影引擎尝试模糊匹配读WKT或参数WKT优先QGIS基于GDAL同GDAL同GDAL同GDALENVI自有映射表用默认值读参数参数优先某国产GIS自有映射表静默忽略读参数参数优先这张表里最需要注意的是“遇到未知代码”这一列。GDAL会报错或返回空这是好事至少你知道有问题。但有些软件会静默忽略用默认坐标系兜底结果就是数据看起来能打开但坐标系是错的。这种“沉默的失败”最危险因为用户可能一直不知道有问题直到做分析时才发现。还有一个差异是优先级。当ProjectedCSTypeGeoKey和其他参数键同时存在且不一致时GDAL优先用GeoKeyArcGIS优先用WKTENVI优先用参数。这意味着同一份数据在GDAL里是一个坐标系在ArcGIS里可能是另一个。解决方法是保证所有表达方式一致或者只保留一种表达方式避免歧义。注意如果你需要跨软件交换数据最稳妥的做法是同时提供正确的ProjectedCSTypeGeoKey和完整的WKT并确保两者一致。如果做不到至少保证ProjectedCSTypeGeoKey是标准EPSG代码不要用32767加自定义参数因为后者在不同软件里的解析差异太大。5. 修复与写入投影代码的实操方案5.1 用GDAL重写ProjectedCSTypeGeoKey如果你发现一份GeoTIFF的ProjectedCSTypeGeoKey错了或者缺失最直接的修复方法是用GDAL重新写入。假设你知道正确的EPSG代码是32650可以用gdal_translate命令gdal_translate -a_srs EPSG:32650 input.tif output.tif这个命令会读取input.tif把坐标系强制设为EPSG:32650然后写入output.tif。注意-a_srs是覆盖式写入不管原来是什么都改成你指定的。如果原来有正确的仿射变换参数会保留如果原来没有可能需要额外指定。如果你只想改GeoKey不想动像素数据可以用Python的rasterioimport rasterio from rasterio.crs import CRS with rasterio.open(input.tif) as src: profile src.profile data src.read() transform src.transform profile.update(crsCRS.from_epsg(32650)) with rasterio.open(output.tif, w, **profile) as dst: dst.write(data)这段代码会读取原数据更新CRS然后写入新文件。像素值和仿射变换都保留只改坐标系。这是最干净的修复方式适合批量处理。但要注意如果原数据的仿射变换参数本身就是错的比如按错误的投影算出来的光改ProjectedCSTypeGeoKey是不够的还需要重新计算仿射变换。这种情况就需要先确定正确的投影然后用控制点做几何校正再写入正确的坐标系。这是另一个话题这里不展开。5.2 自定义投影的GeoKey写入方法如果你需要用自定义投影不能简单用EPSG代码就要手动构造GeoKeyDirectoryTag。GDAL支持通过WKT来设置自定义投影gdal_translate -a_srs projutm zone50 datumWGS84 unitsm no_defs input.tif output.tif这里的a_srs参数用的是PROJ字符串。GDAL会解析这个字符串生成对应的GeoKey和WKT写入文件。但要注意PROJ字符串转GeoKey时如果参数不在EPSG库里ProjectedCSTypeGeoKey会被写成32767其他参数会写入对应的GeoKey。这样写出来的文件在GDAL体系里没问题但在其他软件里可能解析不了。更可控的方法是用Python的pyproj和rasterio配合from pyproj import CRS import rasterio crs CRS.from_proj4(projtmerc lat_00 lon_0117 k1 x_0500000 y_00 ellpsGRS80 unitsm no_defs) with rasterio.open(input.tif) as src: profile src.profile data src.read() profile.update(crscrs) with rasterio.open(output.tif, w, **profile) as dst: dst.write(data)这段代码用pyproj构造了一个自定义的横轴墨卡托投影中央经线117度椭球GRS80单位米。rasterio会把这个CRS写入GeoTIFF包括ProjectedCSTypeGeoKey可能是32767和其他参数键。写完后建议用listgeo检查一下确认所有键都写对了。实操心得自定义投影写入后一定要用至少两种软件打开验证。我习惯用GDAL和QGIS各开一次对比坐标系信息是否一致。如果QGIS显示的坐标系和GDAL不一致说明GeoKey和WKT有冲突需要检查写入逻辑。另外如果数据要给别人用最好附上一份说明文档写明坐标系定义避免对方解析错误。5.3 批量检查与修复的脚本方案实际项目中往往不是一份文件的问题而是一批文件。手动一个个改不现实需要脚本批量处理。下面这个Python脚本可以扫描目录下所有GeoTIFF检查ProjectedCSTypeGeoKey并输出报告import os import rasterio from rasterio.crs import CRS def check_crs(directory): results [] for root, dirs, files in os.walk(directory): for f in files: if f.lower().endswith((.tif, .tiff)): path os.path.join(root, f) try: with rasterio.open(path) as src: crs src.crs epsg crs.to_epsg() if crs else None results.append({ file: path, crs: str(crs) if crs else None, epsg: epsg }) except Exception as e: results.append({ file: path, crs: ERROR, epsg: str(e) }) return results for r in check_crs(/path/to/data): print(r)这个脚本会遍历目录打开每个GeoTIFF提取CRS和EPSG代码。如果EPSG是None说明没有标准代码需要进一步检查。如果打开报错说明文件可能损坏或格式不支持。输出结果可以导出成CSV方便筛选。修复脚本类似只是在读取后更新CRS再写入。但要注意批量修复前一定要备份原始数据因为坐标系修改是不可逆的改错了很难恢复。我一般会先在小样本上测试确认无误后再全量处理。6. 几个容易踩的坑和我的应对经验6.1 代码正确但数据偏移的诡异情况有时候你检查了ProjectedCSTypeGeoKey发现代码完全正确但数据还是偏移。这种情况最让人头疼因为问题不在代码本身而在别的地方。我遇到过几种原因列出来供参考。第一种是仿射变换参数错误。GeoTIFF的像素定位靠的是ModelPixelScaleTag和ModelTiepointTag它们定义了像素尺寸和左上角坐标。如果这两个标签的值是按错误投影算的即使ProjectedCSTypeGeoKey正确数据也会偏。这种情况需要重新计算仿射变换通常要用控制点做几何校正。第二种是基准面转换参数缺失。比如数据是Beijing 1954但ProjectedCSTypeGeoKey写的是CGCS2000的代码两者基准面不同直接叠加会偏。虽然代码看起来“正确”但和实际数据不符。这种情况需要做基准面转换或者修正代码。第三种是软件缓存或配置问题。有些软件会缓存坐标系信息改了文件后不重新读取还用旧缓存。或者软件的EPSG库版本太老不认识新代码用了错误的替代。这种情况重启软件或更新库就能解决。第四种是数据本身经过裁剪或重采样但坐标系没更新。比如从一个大图中裁了一小块裁剪工具没有正确传递坐标系导致新文件的ProjectedCSTypeGeoKey是默认值。这种情况需要手动补上正确的代码。提示排查这类问题我习惯用“三步法”第一步用gdalinfo看坐标系和仿射变换第二步用QGIS加载数据叠加一个已知正确的底图看偏移方向和距离第三步根据偏移模式反推可能的原因。三步下来基本能定位问题。6.2 跨平台数据交换的兼容性建议如果你的数据要给别的单位或别的软件用ProjectedCSTypeGeoKey的写法就很重要。我的经验是尽量用标准EPSG代码不要用32767加自定义参数。标准代码通用性最好几乎所有软件都能识别。如果必须用自定义投影那就同时提供WKT和GeoKey并确保两者一致。另外导出数据时建议同时导出坐标系说明文件比如一个txt或xml写明EPSG代码、投影名称、基准面、中央经线、带号等信息。这样即使对方软件解析有问题也能手动设置。我见过太多因为坐标系信息丢失导致的数据无法使用其实只要多写一个说明文件就能避免。还有一点不同软件对GeoTIFF的写入策略不同。有的软件默认不写ProjectedCSTypeGeoKey只写WKT有的只写GeoKey不写WKT。交换数据前最好用gdalinfo检查一下确认关键信息都在。如果缺失用gdal_translate补上。6.3 版本升级带来的代码变更风险EPSG代码库不是静态的官方会定期更新。有些代码会被废弃有些会被重新定义。比如某些旧代码在新版本里被标记为deprecated推荐用新代码替代。如果你的软件升级了EPSG库旧代码可能不再被识别或者被映射到新的坐标系导致数据表现变化。我经历过一次某软件升级后一批老数据的ProjectedCSTypeGeoKey被重新解析原本正确的坐标系变成了另一个导致叠加偏移。排查后发现是EPSG库升级导致的代码映射变化。解决办法是锁定EPSG库版本或者在升级前备份数据并测试。所以对于长期保存的数据建议在元数据里同时记录EPSG代码和完整的WKT。WKT是自包含的不依赖外部代码库即使代码被废弃WKT仍然能准确描述坐标系。这是最保险的做法。6.4 常见问题速查表最后整理一张速查表把Projected CS Type Code相关的常见问题和解决方法列出来方便快速查阅。问题现象可能原因排查方法解决方式数据整体偏移投影参数错误对比WKT参数修正GeoKey或重投影面积算出来偏大/偏小线性单位错误检查ProjLinearUnitsGeoKey改为正确单位软件报未知坐标系代码不在EPSG库用gdalinfo查看代码改用标准代码或补WKT不同软件显示不同坐标系解析优先级不同对比各软件WKT统一表达方式数据无法叠加基准面不一致检查GeographicTypeGeoKey做基准面转换代码正确但数据偏仿射变换错误检查ModelPixelScaleTag重新计算仿射变换升级后坐标系变了EPSG库版本变化对比升级前后WKT锁定库版本或补WKT这张表里的每一行都是我实际踩过的坑。尤其是最后一行版本升级导致坐标系变化很多人不会想到。我的建议是对于重要数据永远在元数据里保留WKT不要只依赖EPSG代码。WKT虽然长但它是自包含的不依赖外部库是最可靠的坐标系描述方式。另外如果你在做数据生产导出GeoTIFF时最好同时写入ProjectedCSTypeGeoKey和WKT并确保两者一致。这样无论对方用什么软件都能正确解析。如果只能写一个优先写WKT因为它的信息更完整兼容性更好。但要注意有些老软件只认GeoKey不认WKT所以两者都写是最稳妥的。我在实际项目中的体会是坐标系问题从来不是技术难题而是规范问题。只要生产方和消费方都遵循同一套规范问题就能避免。但现实是不同软件、不同单位、不同时期的规范不一致导致问题层出不穷。所以与其事后排查不如在数据生产环节就把坐标系写清楚、写完整、写一致。这是最省事的做法。