4. Shapefile格式详解

把地质钻孔CSV转成Shapefile,很多人以为“导出成功”就结束了,结果在ArcGIS或QGIS里打开发现属性表乱码、中文缺失、坐标飞到海里,或者明明有几百个孔位却只显示一部分。这些问题绝大多数不是软件故障,而是对Shapefile的组成结构和字段约束理解不到位。Shapefile并不是一个单一文件,而是一组同名、同目录、扩展名不同的文件协同工作,缺一不可。本节把它的内部结构拆开讲清楚,为后续的转换与投影校验打好基础。

一、Shapefile是一组文件,不是一个文件

一个可用的Shapefile至少由三个文件构成,通常还会带一个投影文件:

  • .shp:主文件,存储几何信息。它按记录顺序保存每个要素的几何类型、坐标串(点、线、面),以及该要素在文件中的位置索引。几何类型在文件头中一次性声明,因此一个Shapefile只能存放一种几何类型(全点、全线或全面),点与面不能混存。
  • .shx:索引文件,存储每个要素在.shp中的字节偏移量和记录长度。它让软件能快速定位第N个要素,而不必从头扫描。.shx损坏或与.shp不匹配时,常表现为“能打开但图形错位”或“记录数不对”。
  • .dbf:属性表文件,dBASE格式,存储每个要素的属性字段。它与.shp按记录序号一一对应:.shp中第1个几何对应.dbf中第1行属性。这个隐式关联是Shapefile的核心约定,也是转换时最容易出错的地方。
  • .prj:投影文件,纯文本,用WKT描述坐标系。它不是必需文件,但缺失时软件无法得知坐标含义,只能当作“未知坐标系”处理,这正是钻孔点位“飞到海里”的常见原因。

此外还可能出现 .sbn/.sbx(空间索引)、.cpg(字符编码说明)、.xml(元数据)等辅助文件。其中.cpg非常关键:它用一行文本声明.dbf的字符编码(如 UTF-8 或 GBK),没有它,中文属性在跨软件打开时极易变成乱码。

Shapefile 文件组:几何、索引、属性、投影的协同关系 .shp 主文件 几何类型 + 坐标串 记录1: 点(102.3, 30.1) 记录2: 点(102.4, 30.2) … .shx 索引文件 偏移量 + 记录长度 快速定位第 N 个要素 .dbf 属性表 孔号 | 岩性 | 孔深 ZK01 | 砂岩 | 120.5 ZK02 | 泥岩 | 98.0 … 字段名≤10字符,字符型≤254 按记录序号一一对应 .shp 第1个几何 ↕ .dbf 第1行属性 顺序错位 → 属性张冠李戴 记录数不等 → 数据丢失 .shx 必须与 .shp 同步 重建索引可修复错位 .prj 声明坐标系 缺失 → 未知坐标系 .cpg 声明编码 几何类型(三选一) Point:钻孔孔位 Polyline:剖面线 Polygon:地层区 字段约束(易踩坑) • 字段名 ≤ 10 字符 • 字符型长度 ≤ 254 • 数值型总宽 ≤ 19 位 • 不支持日期时间混合 • 中文需 .cpg 声明编码 • 单文件上限 2GB • 字段名不可重复

二、几何类型与钻孔数据的对应

Shapefile的几何类型在文件头固定,常见有Point、Polyline、Polygon三类。对地质钻孔而言,孔位天然对应Point:每个钻孔一个点,坐标取孔口X、Y(投影坐标)或经度、纬度。如果要把钻孔连成勘探线剖面,则用Polyline;若要表达地层分布或矿体边界,则用Polygon。需要特别提醒:一个Shapefile不能同时存点和面,所以“孔位+地层”必须拆成两个Shapefile,通过属性字段(如地层代号)建立逻辑关联,而不是塞进同一个文件。

点要素的坐标顺序也要注意:Shapefile内部按X、Y存储,X对应东坐标/经度,Y对应北坐标/纬度。若CSV中列名是“纬度、经度”,转换时必须显式指定映射,否则点位会整体旋转90度,表现为“所有孔排成一条竖线”。

三、.dbf字段类型与长度限制

.dbf源自dBASE,字段约束比现代数据库严格得多,转换前必须做字段设计。常用类型包括:

类型代号含义典型用途限制
C字符型孔号、岩性名称长度≤254,按字节计
N数值型孔深、标高、坐标总宽≤19,含小数点和负号
F浮点型品位、含量精度有限,慎用于大数
D日期型开孔日期固定8位YYYYMMDD
L逻辑型是否见矿仅T/F

两个高频坑:其一,字段名最长10个字符,中文按字节算更紧张,“钻孔编号”这类名字容易被截断成“钻孔编”,转换工具通常静默截断而不报错,导致后续按字段名取值失败;建议统一用英文短名如 HOLE_ID、LITHO、DEPTH。其二,字符型长度按字节而非字符计,UTF-8下一个汉字占3字节,若把“岩性描述”设为长度20,实际只能存6个汉字,超出部分被截掉。设计时应按“最长可能值×字节数”留余量。

数值型字段的“总宽”包含小数点、负号和小数位。例如 N,10,3 表示总宽10位、小数3位,整数部分最多6位。若孔深写成 1234.5678,会被截断或报错。转换前先统计各数值列的最大整数位与小数位,再定宽度。

四、属性表与几何的关联机制

.shp与.dbf之间没有显式主键,靠记录序号隐式绑定:第i个几何对应第i行属性。这意味着转换时CSV的行顺序必须与写入几何的顺序严格一致。实践中常见两种破坏方式:一是先按某列排序再写几何、却按原顺序写属性;二是过滤掉坐标为空的行时只删了几何没删属性,造成后续所有记录整体错位一位,表现为“ZK01的坐标配上了ZK02的岩性”。

排查这类问题有一个简单办法:在.dbf中保留一个与几何同源的字段(如孔号),在QGIS中用“按位置选择”或属性表逐行核对前几条记录,看孔号与点位是否吻合。若发现整体错位,可用GDAL的 ogrinfo 查看记录数是否与CSV有效行数一致,再用 ogr2ogr 重新生成索引与属性,而不是手工修补。

五、转换前的字段设计示例

假设钻孔CSV含列:孔号、经度、纬度、孔深、岩性、开孔日期。转换前建议先落一张字段映射表,明确类型、宽度与来源列,避免工具自动推断出错:

目标字段名   类型   宽度/小数   来源CSV列    说明
HOLE_ID      C      12         孔号         英文短名,避免截断
LON          N      12,6       经度         保留6位小数
LAT          N      12,6       纬度
DEPTH        N      8,2        孔深         整数最多5位
LITHO        C      30         岩性         UTF-8下约10个汉字
OPEN_DATE    D      8          开孔日期     转YYYYMMDD

同时生成配套的 .prj(如CGCS2000地理坐标或对应投影带)和 .cpg(写 UTF-8),并在转换后用 ogrinfo -al 检查几何类型、记录数、字段定义与坐标系是否与设计一致。只有这四项都对上,才算真正“转换成功”,而不是仅仅“导出了文件”。

记住一条判断链:几何类型对不对 → 记录数等不等 → 字段名与宽度够不够 → .prj有没有、坐标系对不对 → .cpg编码声明了没有。五步都过,Shapefile才能在ArcGIS、QGIS、MapGIS之间稳定流转。