如何硬解析 .shp 文件中的几何体,拯救 .dbf、.shx 等文件缺失的 ESRI Shapefile 格式文件

最近遇到一个问题就是 ESRI Shapefile 格式文件中 .dbf 文件损坏,导致 .shp 无法打开,又不能放弃这个数据,所以只能做修复,网上搜了很多方法,结果都失败了,所以用 deepseek 查询应解析的方法,亲测可行(简单多边形),现将方法分享:

ESRI Shapefile 文件组成

1 核心文件

扩展名 必选 用途
.shp 存储几何图形(点、线、面等)
.shx 存储几何索引(加速查询)
.dbf 存储属性数据(类似表格)
.prj 坐标系统定义(如 WGS84)
.cpg 字符编码声明(如 UTF-8)
  • 必须同时存在 .shp.shx.dbf 文件才能正常使用。
  • 缺失 .prj 可能导致坐标系统未知。

2 .shp 文件结构(二进制格式)

文件头(Header)

偏移量 长度 类型 描述
0 4B int32 文件标识码(固定 9994
24 4B int32 版本号(1000
28 4B int32 几何类型(见下表)
36-68 32B double 边界框(xmin, ymin, xmax, ymax)

几何记录(Record)

  • 记录头 (8B):
    • int32 记录编号(从 1 开始)
    • int32 记录长度(以 16-bit 字为单位)
  • 记录内容
    • int32 几何类型
    • 几何数据(坐标点等)

Shapefile 几何类型

类型值 名称 描述
0 Null Shape 空几何
1 Point 二维点 [x, y]
3 PolyLine 多段线
5 Polygon 多边形(首尾闭合)
8 MultiPoint 多点集合
11 PointZ 三维点 [x, y, z, m]

多边形规则

  • 外环 :顶点按 逆时针 排列
  • 内环 (孔洞):顶点按 顺时针 排列

Python 硬解析只有 .shp 文件的已损坏的 ESRI Shapefile 格式数据(Polygon)

环境和数据准备

安装 gma:pip install gma
本文基于:gma 2.1.7,Python 3.12
本文用到数据:

链接: https://pan.baidu.com/s/1kMlji9xqSsYIVn1cygIewA?pwd=9nc1

提取码: 9nc1

代码

python 复制代码
import struct

def parse_polygon_shp(shp_path):
    polygons = []
    iii = 0
    with open(shp_path, "rb") as f:
        # 跳过文件头(100字节)
        f.read(100)
        
        while True:
            # 读取记录头(8字节:记录号+长度)
            record_header = f.read(8)
            if not record_header:
                break
            
            # 解析记录头(大端序)
            record_num = struct.unpack(">i", record_header[:4])[0]
            record_len = struct.unpack(">i", record_header[4:8])[0]  # 单位:16-bit words
            
            # 读取记录内容(长度:record_len * 2 字节)
            try:
                record_data = f.read(record_len * 2)
            except:
                break
                
            if len(record_data) < 44:  # 多边形至少需要44字节
                continue
            
            # 解析几何类型(小端序,从记录内容开始)
            shape_type = struct.unpack("<i", record_data[0:4])[0]
            if shape_type != 5:  # 仅处理多边形
                continue

            # 解析边界框(4个double)
            bbox = struct.unpack("<4d", record_data[4:36])
            
            # 解析环数和点数
            num_parts = struct.unpack("<i", record_data[36:40])[0]
            num_points = struct.unpack("<i", record_data[40:44])[0]
            
            # 解析环的起始索引(num_parts个int32)
            parts = []
            offset = 44
            parts_data = record_data[offset : offset + 4 * num_parts]
            parts = list(struct.unpack(f"<{num_parts}i", parts_data))
            offset += 4 * num_parts
            
            # 解析所有点(num_points个xy对)
            points = []
            points_data = record_data[offset : offset + 16 * num_points]
            for i in range(num_points):
                x = struct.unpack("<d", points_data[16*i : 16*i + 8])[0]
                y = struct.unpack("<d", points_data[16*i + 8 : 16*i + 16])[0]
                points.append((x, y))
            # 保存多边形数据
            polygons.append({
                "shape_type": shape_type,
                "bbox": bbox,
                "num_parts": num_parts,
                "num_points": num_points,
                "parts": parts,
                "points": points
            })
    
    return polygons

shp_path = "青海省.shp"
res = parse_polygon_shp(shp_path)

import pandas as pd
from gma import io

fts = []
for data in res:
    pt = data['points']
    ft = io.CreateFeature(pt, GeomType = 'Polygon', Projection = 'WGS84')
    fts.append(ft)
    
ly = io.CreateLayer(fts)
ly.SaveAs("青海省_New.shp")

注意:此方法暂不适合复杂的多边形。如果是复杂多边形需要进一步优化。

相关推荐
运维小文9 分钟前
初探贪心算法 -- 使用最少纸币组成指定金额
c++·python·算法·贪心算法
Eiceblue10 分钟前
PDF转Markdown - Python 实现方案与代码
开发语言·vscode·python·pdf
Bruce_Liuxiaowei10 分钟前
Python实现PDF按页分割:灵活拆分文档的技术指南
windows·python·pdf
烛九幽-子麟13 分钟前
精通Python PDF裁剪:从入门到专业的三重境界
开发语言·python·pdf
go&Python1 小时前
scikit-learn 包
python·机器学习·scikit-learn
sssammmm1 小时前
AI入门学习-Python 最主流的机器学习库Scikit-learn
人工智能·python·机器学习
AI视觉网奇1 小时前
docker compose 安装使用笔记
开发语言·python
27669582922 小时前
拼多多小程序 anti_content 分析
java·javascript·python·node·拼多多·anti-content·anti_content