你有没有被一堆文件后缀搞到崩溃的瞬间?处理过空间数据的人,基本都经历过这个阶段:客户丢来一个文件夹,里面是.shp、.dbf、.prj、.cpg,你打开发现属性表乱码;同事给了一个GeoJSON,你用ArcGIS打开发现只有一堆线稿;还有人让你把几百GB的影像转成“通用的格式”,可到底哪种才算通用?我入行前几年,就是在这种“空间数据格式”的坑里爬出来的。今天这篇就把它们一次性讲清楚,尽量覆盖矢量、栅格、点云三大类,把格式背后的设计逻辑、适用场景、常见坑和实操转换路径都过一遍。
1. 空间数据格式的底层逻辑:从数据本质到存储目的
1.1 矢量、栅格、点云:三种数据“方言”
空间数据格式本质上解决一个问题:把现实世界的地理对象变成计算机能读、能算、能画的东西。现实世界太复杂,计算机只能“简化”。目前主流简化方式就三种:矢量、栅格、点云。
矢量的核心是几何对象加属性。比如一栋房子,在矢量世界里就是一组坐标点围成的面,再加上“楼号、层数、用途”这些表格字段。它的优点是精度高、占存储小、支持拓扑关系,适合做规划分析、地籍管理、路网建模。你平常用的Shapefile、GeoJSON、GeoPackage,基本都是这一类。
栅格的核心是像元,也就是像素。遥感影像、全球DEM、土地利用分类图,本质都是一张张格网,每个格子里填一个数值。你可以把栅格理解成照片,只不过每个像素带的是“海拔”“温度”“反射率”这类物理量,而不是颜色值。栅格的优点是数据结构简单、非常适合连续表面的分析和图像处理,缺点也是明显的——文件往往很大,放大到一定比例尺会发虚。
点云就比较特殊了,它是一大群三维坐标点,每个点还带有强度、颜色、分类等信息。用激光雷达扫一遍街区,得到的不是面也不是格子,而是几千万甚至上亿个点。点云的好处是能精确表达三维结构和细节,坏处是数据量极大,必须依赖压缩和高效索引才能正常操作。常见后缀.las、.laz就属于这一类。
为什么开头先强调这三种?因为几乎所有空间数据格式,都能归到这三类之一。但很多人搞不清格式,是因为先搞不清数据类型。拿着一个GeoTIFF去问“为什么不能像Shapefile那样用”,本质是拿栅格格式去套矢量需求,那当然不行。先想清楚你的数据是点线面、是格网、还是三维点云,再去选格式,方向就不会偏。
1.2 文件、数据库、网络服务:格式设计的三重目标
空间数据格式并不仅仅是文件后缀,它背后有三种完全不同的设计目标。
第一种是文件交换和分发。比如一个城市的地块数据要交给合作伙伴,最自然的形式就是打包成一个Shapefile或GeoPackage发过去。这类格式追求的是“别人能直接打开”,兼容性最重要,什么拓扑、版本管理都得往后靠。
第二种是数据库存储。当数据量达到百万级要素、需要多人并发编辑时,把数据丢进一个文件就不合适了。PostGIS就是PostgreSQL的“空间版”,它把几何信息做成专门的二进制类型,配合空间索引,能实现秒级查询和并发事务。你会接触到的空间数据格式,其实也包括这种“数据库内部格式”,比如PostGIS的geometry类型,以及Esri FileGDB。
第三种是网络服务。浏览器里刷在线地图,背后是瓦片、矢量瓦片,还有WMS、WFS、WMTS这类OGC标准接口。这些格式设计出来就不是给你直接拷贝的,而是为了在网络上高效传输和实时渲染。最常见的例子就是GeoJSON,前端拿到一个请求就能画出一片地图。
理解了这个分类,你就明白为什么老工程师总爱强调“按场景选格式”这句话——没有最好的格式,只有最合适的格式。这句话不是套话,因为不同格式根本不是一个维度上的东西。你要做在线可视化,你偏要下个Shapefile再导入,绕一大圈;你要做长期归档,你偏用在线瓦片服务,那也不行。
1.3 从专有格式到开放标准:历史如何影响选型
空间数据格式的历史,其实就是一条从“厂商锁定”到“开放标准”的演进线。
早年ArcGIS影响太大,Shapefile、Coverage、FileGDB都是Esri定义的,你离开Esri生态就很难受。后来Google Earth普及,KML被推了一把;再后来Web GIS兴起,GeoJSON、TopoJSON这类格式因为JavaScript天然亲和,成了前端事实标准。现在OGC(开放地理空间联盟)推出GeoPackage,希望用一个SQLite文件统一矢量、栅格和瓦片,不少国家的基础测绘已经把GeoPackage定为分发标准。
这条历史线对实际选型有一个重要启发:优先拥抱开放、有标准背书的格式,尽量别碰没文档的私有格式。不是说私有格式不能用,而是数据要活几十年,格式的生命力远比你的项目周期长。我见过不少老项目,数据用ArcGIS打包好,版本一换,打不开了,数据全部干瞪眼。开放格式至少给了你一种“逃命”的可能——哪怕原软件没了,开源社区还有工具链能把它捞出来。
2. 矢量格式深度拆解:Shapefile、GeoJSON、GeoPackage及冷门格式
2.1 Shapefile:一个文件不算文件,暗坑一大堆
说Shapefile是空间数据的“祖宗”一点不过分。1990年代Esri设计了它,直到今天它仍然是整个行业默认的交换格式之一。但绝大多数新手不知道:Shapefile根本不是单个文件,而是至少三个文件的集合——.shp存几何,.shx存索引,.dbf存属性,.prj存坐标系,.cpg存编码。你只拷一个.shp给别人,对方大概率打不开,纯粹就是少了“兄弟文件”。
这还不是最坑的。Shapefile有几个硬伤,我整理一下:
- 字段名长度限制10个字符,中文或者长英文直接被截断,属性类型很弱,日期经常被当成字符串。
- .dbf默认编码是ANSI,在不同环境下打开中文乱码是家常便饭。
- 单个文件上限2GB,对大矢量数据不友好。
- 一个文件只能存一种几何类型,点线面不能混装。
- 不自带空间索引,规模一大,范围查询速度就让人着急。
既然这么麻烦,为什么还在用?因为兼容性强到夸张,几乎所有GIS软件都能打开Shapefile,哪怕是20年前的老古董。所以我的建议是:Shapefile可以作为“交换格式”,但别拿它当“工作格式”。真要长期干活,尤其涉及大数据量,我一般直接进PostGIS或GeoPackage。
如果你必须用Shapefile交付,记得打包成zip,里面完整带上.prj和.cpg。.cpg里写UTF-8,能避免相当一部分中文乱码。还有一个经验是,交付之前用QGIS重新打开一次,确认能正常显示再发出去。这一步看着笨,但能拦住80%的返工。
2.2 GeoJSON:Web地图的“通用语”,但不是万能药
GeoJSON是JSON格式的地理数据规范,2016年更新为RFC 7946。它最大的好处是文本化、人类可读、JavaScript天然能解析。Leaflet、Mapbox、OpenLayers这些前端库,喂给它们的矢量数据有一半以上是GeoJSON。
但正因为它是文本,所以有两个明显的短板:文件体积大——几何是双精度浮点,十万个点存成文本,几十MB轻轻松松;没有空间拓扑——同一个点重复出现多次,数据冗余严重,修改一个公共边界要改动很多地方。
GeoJSON的坐标顺序也值得单独说:规范要求是“经度,纬度”,也就是先x后y,对应EPSG:4326。很多人习惯写“纬度,经度”,尤其是在科学计算里,翻车概率极高。我遇到过前端画出来的线直接跑到海上去了,最后发现是坐标对调。
如果你的GeoJSON要用来长期存储或大数据分析,建议把它当“中间格式”就好——在脚本流程里转来转去用一下,最终落库还是转成GeoPackage或者PostGIS。临时使用没问题,永久归档别选它。
2.3 GeoPackage:我目前最推荐的新一代容器
如果你只能记住一个新格式,那我希望是GeoPackage。它是OGC在2014年发布的标准,底层是一个SQLite数据库文件,后缀.gpkg。这意味着你所有的矢量图层、栅格瓦片、属性表都能装进这一个文件里,用标准SQL去查。
相比Shapefile,GeoPackage没有字段名10字符限制,没有2GB硬限制(理论支持非常大的数据量,实际受文件系统管理),支持空间索引(RTree),支持矢量栅格混装,也支持分块瓦片。我实测过同样一份全国路网数据,GeoPackage比Shapefile体积小大概两到三成,查询却快了很多。
更重要的是,GeoPackage是纯开放标准,有OGC背书,GDAL、QGIS、GeoPandas等一堆开源库都原生支持。交给别人的时候只有一个文件,不会出现“少了.prj”这种破事。我现在做项目分发,第一选择就是GeoPackage。
劣势也得讲清楚:SQLite的并发写性能一般,不适合多人同时在线编辑;如果数据特别巨大,比如几十GB以上,单文件管理反而吃亏,不如直接进PostGIS。选型没有银弹,但GeoPackage在绝大多数中小型项目的确是那个“最不后悔”的选项。
2.4 KML/KMZ、FileGDB、WKT/WKB快速扫盲
KML是Google Earth的“母语”,本质是XML,KMZ则是压缩打包过的KML。它在三维地表展示里很好用,Google Earth和Google Maps都支持,嵌套样式、气泡弹窗是它的独有能力。但要正经做空间分析,KML就太笨重了,解析慢,坐标还是经纬度无投影,不适合大数据处理。
FileGDB是Esri的文件地理数据库,表面看是.gdb文件夹,里面一堆文件。它支持拓扑、网络、注记、版本管理这些复杂结构,在ArcGIS生态里功能很强。但开源软件读写FileGDB受限,GDAL目前只能读,写文件地理数据库基本要依赖Esri官方SDK或商用许可。你的工具链如果偏开源,碰FileGDB会很痛苦。
WKT/WKB是几何对象的文本和二进制表示方式,不属于文件格式,却无处不在。PostGIS入库、坐标输出、API返回结果,都可能遇到WKT或WKB。WKT长这样:POINT (116.39 39.9);WKB是二进制形式,紧凑且高效。这个知识点建议牢记,因为你会不断在数据库日志和报错信息里看到它们。
3. 栅格与点云格式深度拆解:GeoTIFF、NetCDF、LAS/LAZ
3.1 GeoTIFF与COG:遥感影像的主流与进化
GeoTIFF本质上是TIFF图像加上了地理参考信息。它可以是一个波段,也可以是多波段,比如红绿蓝加近红外。每个像素是一个数值,数值的含义完全取决于你的数据类型。
举个例子:高程模型DEM,像素值就是海拔,单位米;遥感影像,像素值可能是DN值(数字量化值),需要定标公式转成反射率。很多人做遥感时把像素值当“颜色”直接看图,只能看个大概,真正做分析必须理解像素背后的物理含义。
GeoTIFF之所以是事实标准,是因为几乎所有遥感软件和几乎所有数据处理库都原生支持,包括GDAL、Rasterio、ENVI、ArcGIS、QGIS。它支持内部压缩,也支持金字塔概览和分块存储。从实践角度看,GeoTIFF就是栅格界的“Shapefile”——兼容性極高,但本身也存在一些性能问题。
这里要重点讲一个变体:COG,Cloud Optimized GeoTIFF,也就是云优化GeoTIFF。COG的核心不是压缩算法,而是把数据组织成“可随机访问”的结构。想象一本厚书,普通TIFF你要从头翻到尾才能找到某一页,而COG就像带详细目录和页边索引的书,你翻到哪一页都很快。配合HTTP Range请求,服务器不需要加载整个文件,就能把你要的那块影像发给客户端,做在线预览流畅得多。
生成COG用GDAL一行命令就够:
gdal_translate input.tif output_cog.tif -of COG -co COMPRESS=DEFLATE -co BLOCKSIZE=512BLOCKSIZE=512很关键,它决定分块大小。分块太小,HTTP请求次数太多;太大则浪费带宽。512是社区实践下来比较稳妥的折中。
3.2 NetCDF:自描述的多维科学数据
NetCDF通常出现在气候、海洋、大气分析等科学领域,文件后缀.nc。它和GeoTIFF最大的区别是:NetCDF能存多维数组。比如全球气温数据,维度是时间、纬度、经度、高度,四个维度叠在一起,你随时可以沿任意维度切片。而GeoTIFF只能表达二维平面,时间序列你得一个时相一个文件地堆。
NetCDF还是自描述的,文件内部就带变量名、单位、坐标轴、属性。哪怕没有外部说明文档,你也能读出来:“这个变量是温度,单位是开尔文,时间轴是每月”。这一点在很多科学数据集里非常救命——最怕给一堆二进制数据,没有解释文档,谁也不知道里面是什么。
处理NetCDF,Python生态最常用的是xarray,一句xr.open_dataset("data.nc")就能打开。GDAL其实也能读,但它看到的往往只是其中一个时间片,用起来不方便。如果你要做气候预测、洋流模拟这类科学计算,建议直接学xarray,配合维度重排和插值,效率比在GIS软件里硬啃高得多。
3.3 LAS/LAZ:点云压缩存储与处理
点云格式最常遇到的是LAS,由ASPRS组织维护,后缀.las。LAS文件里每个点记录坐标X、Y、Z,还有强度、颜色、分类等。分类值是整数,例如2是地面点,5是植被,6是建筑,具体分类号含义有标准规定。
LAS是二进制,读取效率高,但完全没有压缩,动辄几个GB。后来出现了LAZ,也就是对LAS做无损压缩,压缩率通常能达到3到5倍。3GB的LAS压缩成LAZ后可能只有700MB。做点云处理和传输,如果没有特殊原因,我都建议用LAZ。
点云不像栅格那样方便直接“看图”,需要依赖专门的索引结构。PDAL是点云处理的经典工具,支持过滤、分类、切片、格式转换。把LAS转成LAZ:
pdal translate input.las output.laz --writers.las.compression=laszip我接过一个机载LiDAR项目,原始点云185GB,全部转成LAZ,配合PDAL流式处理和切片,整个工作流从“磁盘不够”变成了“半小时跑完”。压缩格式在这里帮了大忙。
4. 实操环节:用Python打通格式转换全流程
4.1 环境安装:别再一个个pip了
要玩转空间数据格式,绕不开开源地理计算库。核心是GDAL,几乎所有矢量栅格读写都靠它。Python生态里,Fiona负责矢量,Rasterio负责栅格,GeoPandas把矢量数据直接变成DataFrame。
有些朋友习惯pip install,但GDAL其实是有C++底座的,pip装很容易踩版本坑。我强烈建议用conda统一装:
conda create -n geo python=3.11 conda activate geo conda install -c conda-forge geopandas rasterio pyproj shapely fionaconda-forge上的预编译包非常成熟,装完就能用,基本不会碰到“找不到libgdal”这类问题。装好之后验证:
python -c "from osgeo import gdal; print(gdal.VersionInfo())"能打印出版本号,说明GDAL已经就绪。
4.2 矢量转换演练:Shapefile转GeoJSON和GeoPackage
先用GeoPandas把Shapefile读进来,再转成GeoJSON:
import geopandas as gpd gdf = gpd.read_file(r"D:\data\building.shp") print(gdf.head()) gdf.to_file(r"D:\data\building.geojson", driver="GeoJSON")这段代码看起来很轻松,但有几个细节值得注意。第一,read_file会自动读取.prj里的坐标系信息,但如果原始Shapefile没有.prj,GeoPandas默认会把数据当成EPSG:4326,坐标很可能错得离谱。第二,写GeoPackage只需要换driver:
gdf.to_file(r"D:\data\building.gpkg", layer="building", driver="GPKG")GeoPackage支持分层,一个文件里可以放多个图层。我给别人的工作文件里,经常一个.gpkg同时放建筑、道路、边界三个图层,接收方在QGIS里一目了然,不用再管理一堆散文件。
4.3 栅格转换演练:GeoTIFF转COG
栅格转COG,用Rasterio和命令行GDAL都行。Rasterio版本大致这样:
import rasterio with rasterio.open("input.tif") as src: profile = src.profile.copy() profile.update(driver="COG", compress="DEFLATE") with rasterio.open("output_cog.tif", "w", **profile) as dst: dst.write(src.read())命令行GDAL更简单直观:
gdal_translate input.tif output_cog.tif -of COG -co COMPRESS=DEFLATE -co BLOCKSIZE=512转完COG之后,我建议再试一下gdalinfo output_cog.tif,检查输出的Metadata里有没有Layout=COG字样。如果没显示,多半是版本不支持COG驱动,需要升级GDAL到3.1以上。还有一个容易被忽略的点:COG应该保留原始坐标系,如果原影像坐标系就已经“飞”了,转COG只是换容器,并不会修复坐标问题。
4.4 坐标系与数据校验:动手前必须做的两件事
空间数据格式另一个容易翻车的点是坐标系。先明确:经纬度数据一般对应EPSG:4326;Web Mercator投影是EPSG:3857;国内常见坐标系包括CGCS2000以及一些投影分带坐标系。不同坐标系之间必须做转换,否则图层叠加时位置全部偏移。用GeoPandas转坐标系:
gdf_wgs84 = gdf.to_crs(epsg=4326) gdf_web = gdf_wgs84.to_crs(epsg=3857)转换本身不复杂,复杂的是“你根本不知道原数据是哪个坐标系”。.prj文件丢失,但坐标值看起来像投影坐标,这种情况一定别硬猜。建议用已知控制点做交叉验证,或者让数据来源方补充坐标系信息。坐标系的脏活,在没有可靠说明的情况下,宁可停下来问清楚,也不要继续处理。
还有一个老生常谈:某些国内互联网地图平台使用的坐标基准和WGS84不完全一样,存在非线性偏移。这类坐标必须通过对应技术文档给出的方法处理,不能单纯用一个固定差值平移。如果项目里能不用这类坐标,就尽量不用;如果必须接入,一定要在文档里确认它的坐标基准,否则数据进了GIS就是“满天飞”。
提示:做完任何格式转换,先输出一遍数据的边界范围(total_bounds)。如果范围数值非常离谱,比如经度跑到几百万,那几乎可以断定坐标系搞错或坐标顺序反了,回到源头排查。
5. 常见问题与排查技巧实录
5.1 中文乱码与路径问题
先说最简单的坑:中文路径。Windows下GDAL对中文路径的历史支持一直不太好。最省心的方案是:项目目录尽量用英文和数字。实在避免不了,在Python里处理路径时,也要明确告诉GDAL文件路径的编码方式,尽量减少乱码和“无法识别”的问题。
然后是属性表乱码。Shapefile的.dbf属性默认编码不是UTF-8,而是本地系统代码页。如果打开中文乱码,先看文件夹里有没有.cpg文件。有.cpg的内容一般是UTF-8或GBK,没有则用QGIS手动指定编码再读。你可能会问,为什么前面反复强调弃用Shapefile?因为转成GeoPackage或GeoJSON之后,编码问题基本消失,.cpg、.dbf这类历史包袱统统不用管了。
5.2 几何无效与坐标漂移
前阵子处理一份全国社区边界数据,用GeoPandas计算面积时频繁报错,最后排查发现里面全是无效多边形。矢量数据的多边形可能自相交、有重复节点、形成空洞,这跟坐标系无关,是数据本身“脏”。
检查几何有效性用一条语句:
print(gdf.geometry.is_valid.sum(), len(gdf))出现无效几何,经典修复手段是buffer(0):
gdf["geometry"] = gdf.geometry.buffer(0)buffer(0)的原理是给几何做一个宽度为0的缓冲,利用图形修复规则把自相交等异常清理掉。但要注意:对拓扑关系极复杂的数据,buffer(0)也可能让边界形状轻微变化,所以修复前一定要备份原始数据。
坐标漂移则是另一类高频问题。明明底图是CGCS2000坐标,你把WGS84数据叠上去,发现位置偏移十几米甚至几百米。这不是“格式”问题,是“坐标系”问题。不管建筑、道路还是POI,跨应用前都要先确认坐标系,再做精确转换,别用附近地区的近似参数。
5.3 大数据量性能优化
空间数据动辄GB级,格式选对之后,还得会优化性能。
对矢量数据,第一建议是建空间索引。Shapefile需要重建.shx索引,GeoPackage会自动维护RTree索引,PostGIS要手动CREATE INDEX ON table USING GIST (geom);。索引建好之后,范围查询速度能提升好几个数量级。没有索引的空间数据,就跟没有目录的书一样,找一页要翻遍全书。
对栅格数据,COG的“分块+概览”是最核心的优化手段。生成COG时,GDAL会自动创建概览金字塔,前端显示小比例尺时直接引用低分辨率层,不用解析高分辨率层,速度自然快。如果数据是上千个零散GeoTIFF,更聪明的做法是先建VRT虚拟栅格:
gdalbuildvrt mosaic.vrt *.tifVRT只保存元数据和路径,不复制数据,既能统一读取,又省空间。确认无误之后再决定要不要嵌合成一个大COG。
5.4 项目选型速查:按场景对号入座
| 使用场景 | 优先选择 | 原因 | 避免 |
|---|---|---|---|
| Web地图可视化 | GeoJSON / MVT | 前端解析方便,流式加载 | Shapefile |
| 桌面GIS分析 | GeoPackage / Shapefile | 兼容性广,QGIS/ArcGIS都能开 | 无 |
| 科学数据处理 | NetCDF / GeoTIFF | 支持多维数组和自描述 | Shapefile |
| 点云存储与交换 | LAZ | 压缩率高,无损,工具链成熟 | 未压缩LAS |
| 大型企业级生产库 | PostGIS | 并发、权限、索引、事务 | 单个GeoPackage |
| 长期归档 | GeoPackage / GeoTIFF / LAZ | 开放标准,生命周期长 | FileGDB等私有格式 |
这张表不是金科玉律,但它覆盖了我这些年遇到的大多数场景。真正选型的时候,再结合团队工具链、甲方交付要求、数据更新频率去调整,会更稳妥。
踩过这么多坑之后,我现在接到任何涉及空间数据的项目,第一件事永远是:问清数据来源、坐标系、精度、更新频率,然后统一归档成GeoPackage加COG这套组合。不是因为它们最“新”,而是因为它们都是开放、稳定、有完整工具链支撑的格式,十年后大概率还活着,数据也还能打开。空间数据格式这件事,说白了就是“用稳定的方式描述复杂世界”。希望这篇梳理能帮你少走弯路;下次再有人丢给你一堆文件后缀,你能一眼看出该在哪里下功夫。