Skip to content

SphinxHQ/shapefile-parser

Repository files navigation

@sphinx_hq/shapefile-parser

npm version npm downloads MIT License TypeScript

纯前端 Shapefile 解析与导出工具库,无需后端服务,支持离线使用。

特性

  • 读取 Shapefile:支持 .shp.dbf.prj.cpg 文件及 ZIP 压缩包
  • 多文件同时读取:支持一次读取多个 Shapefile,自动按文件名分组
  • 导出 Shapefile:支持导出为 ZIP 压缩包,可选浏览器直接下载
  • GeoJSON 互转:读取返回标准 GeoJSON FeatureCollection,写入接受 GeoJSON 输入
  • 完整形状类型支持:支持所有 Shapefile 形状类型(Point, MultiPoint, PolyLine, Polygon, PointZ/M, MultiPatch 等)
  • MultiPatch 转换:自动将 MultiPatch 转换为 Polygon/MultiPolygon
  • 编码自动检测:通过 LDID 和 .cpg 文件自动识别 DBF 编码(UTF-8 / GBK / Big5 等)
  • 字段名处理策略:支持自动转换、截断、严格模式三种策略处理非法字段名
  • 坐标系支持:内置 243 个常用 EPSG 坐标系,支持自定义注册
  • 非标准 PRJ 修正:自动识别非标准 PRJ 内容,通过参数比对匹配正确的 EPSG 代码
  • TypeScript:完整的类型定义
  • 无后端依赖:无需后端服务,纯浏览器/Node.js 环境

安装

npm install @sphinx_hq/shapefile-parser

快速开始

读取 Shapefile

import { ShapefileParser } from '@sphinx_hq/shapefile-parser';

const parser = new ShapefileParser();

// 读取单个 .shp 文件(仅几何,无属性)
const result = await parser.read(shpArrayBuffer);
// 返回: Record<string, GeoJSONFeatureCollection>
// 例如: { "file": FeatureCollection }

// 读取完整 Shapefile(包含属性和坐标系)
const result = await parser.read({
  shp: shpArrayBuffer,
  dbf: dbfArrayBuffer,
  prj: prjArrayBuffer,
});

// 读取 ZIP 压缩包(可能包含多套 Shapefile)
const result = await parser.read(zipArrayBuffer);
// 返回: { "layer1": FeatureCollection, "layer2": FeatureCollection }

// 读取 File 对象(浏览器环境)
const result = await parser.read(file); // 支持 .shp 或 .zip
const result = await parser.read([shpFile, dbfFile, prjFile]);

// 获取第一个 FeatureCollection
const firstKey = Object.keys(result)[0];
const geojson = result[firstKey];

导出 Shapefile

import { ShapefileParser } from '@sphinx_hq/shapefile-parser';

const parser = new ShapefileParser();

// 导出为 ZIP(返回 ArrayBuffer)
const result = await parser.write(geojson, {
  filename: 'my-shapefile',
});

// 直接触发浏览器下载
const result = await parser.write(geojson, {
  filename: 'my-shapefile',
  download: true, // 自动下载 my-shapefile.zip
});

// 指定坐标系导出
const result = await parser.write(geojson, {
  filename: 'my-shapefile',
  epsgCode: 4490, // CGCS2000
  download: true,
});

API 文档

ShapefileParser

主类,提供 Shapefile 读写功能。所有方法均为 async 异步函数。

构造函数

new ShapefileParser(options?: ShapefileParserOptions)
参数 类型 默认值 说明
encoding string 'utf-8' DBF 文件默认编码
defaultOutputFormat 'shp' | 'zip' 'zip' 默认输出格式
defaultFilename string 'shapefile' 默认文件名
debug boolean false 调试模式

read(input, options?)

异步读取 Shapefile 数据,返回 Record<string, GeoJSONFeatureCollection>

输入类型

类型 说明
ArrayBuffer .shp 或 .zip 文件
File 单个文件(浏览器)
File[] | FileList 多文件(浏览器)
{ shp, dbf?, prj?, cpg? } 对象形式,各属性为 ArrayBuffer 或 File

读取选项

参数 类型 默认值 说明
encoding string 自动检测 DBF 编码(覆盖自动检测)
parsePrj boolean true 是否解析 .prj 文件
includeNullShapes boolean false 是否包含空形状记录
preserveZM boolean false 是否保留 Z/M 值

返回值

// 返回类型:Record<string, GeoJSONFeatureCollection>
{
  "文件名1": {
    type: 'FeatureCollection';
    features: GeoJSONFeature[];
    bbox?: [number, number, number, number];
    crs?: string | CrsInfo; // 如 'EPSG:4490'
  },
  "文件名2": { ... }
}

write(geojson, options?)

异步将 GeoJSON 导出为 Shapefile。

写入选项

参数 类型 默认值 说明
output 'zip' 'zip' 输出格式
filename string 'shapefile' 文件名(不含扩展名)
shapeType ShapeType 自动推断 强制指定形状类型
encoding string 'utf-8' DBF 编码
epsgCode number | string - EPSG 代码,如 4490 或 'EPSG:4490'
crsWkt string - 自定义 WKT 字符串
generateShx boolean true 是否生成 .shx 索引文件
download boolean false 触发浏览器下载
fieldNameStrategy FieldNameStrategy 'auto' 字段名处理策略

返回值

interface WriteResult {
  format: 'zip';
  zipBuffer: ArrayBuffer;
  info: {
    featureCount: number;
    shapeType: ShapeType;
    bbox: [number, number, number, number];
    fieldMappings: FieldMapping[]; // 字段名映射信息
  };
}

interface FieldMapping {
  original: string;   // 原始字段名
  processed: string;  // 处理后的字段名
  reason?: 'non_ascii' | 'too_long' | 'invalid_start' | 'invalid_char' | 'duplicate';
}

字段名处理策略

dBASE 格式对字段名有严格限制:

  • 最多 10 个 ASCII 字符
  • 只允许字母 (A-Z, a-z)、数字 (0-9)、下划线 (_)
  • 必须以字母或下划线开头

通过 fieldNameStrategy 选项控制处理方式:

'auto'(默认)

自动转换非法字段名,并在控制台输出警告:

const result = await parser.write(geojson, {
  fieldNameStrategy: 'auto',
});

// 控制台输出:
// [shapefile-parser] Field names were modified to comply with dBASE format:
//   "名称" -> "FLD001" (contains non-ASCII characters)
//   "very_long_field_name" -> "very_long_" (exceeds 10 characters)

'strict'

遇到非法字段名直接抛出错误:

try {
  const result = await parser.write(geojson, {
    fieldNameStrategy: 'strict',
  });
} catch (error) {
  // Error: Field name "名称" contains non-ASCII characters.
  // Use 'fieldNameStrategy: "auto"' to auto-convert.
}

'truncate'

仅截断超长字段名,非 ASCII 字符仍会报错:

const result = await parser.write(geojson, {
  fieldNameStrategy: 'truncate',
});
// "very_long_field_name" -> "very_long_"
// "名称" -> 抛出错误(非 ASCII 字符)

Z/M 值保留

默认情况下,读取 Shapefile 时会丢弃 Z/M 值,只返回二维坐标 [x, y]。如果需要保留原始的 Z/M 值,可以使用 preserveZM 选项:

// 读取时保留 Z/M 值
const result = await parser.read(zipBuffer, { preserveZM: true });

// 对于 Z 类型(如 PointZ),坐标为 [x, y, z, m]
const coords = result['file'].features[0].geometry.coordinates;
// [116.4, 39.9, 100, 0] - [经度, 纬度, Z值, M值]

注意

  • Z 类型(PointZ、PolyLineZ、PolygonZ 等)同时包含 Z 和 M 值
  • M 类型(PointM、PolyLineM、PolygonM 等)只包含 M 值
  • 未定义的 M 值默认为 0

字段名映射

当使用 fieldNameStrategy: 'auto' 写入时,如果字段名被修改,可以通过 WriteResult.info.fieldMappings 获取映射信息:

const result = await parser.write(geojson, {
  fieldNameStrategy: 'auto',
});

// 检查字段名映射
console.log(result.info.fieldMappings);
// [
//   { original: "名称", processed: "FLD001", reason: "non_ascii" },
//   { original: "very_long_field_name", processed: "very_long_", reason: "too_long" },
//   { original: "name", processed: "name" } // 未修改,无 reason
// ]

// 使用映射在应用层进行数据关联
for (const mapping of result.info.fieldMappings) {
  if (mapping.reason) {
    console.warn(`字段 "${mapping.original}" 被转换为 "${mapping.processed}"`);
  }
}

形状类型支持

类型 读取 写入 说明
Null 0 空形状
Point 1 二维点
PolyLine 3 多段线
Polygon 5 多边形
MultiPoint 8 多点
PointZ 11 带 Z 值的点
PolyLineZ 13 带 Z 值的多段线
PolygonZ 15 带 Z 值的多边形
MultiPointZ 18 带 Z 值的多点
PointM 21 带 M 值的点
PolyLineM 23 带 M 值的多段线
PolygonM 25 带 M 值的多边形
MultiPointM 28 带 M 值的多点
MultiPatch 31 - 自动转换为 Polygon/MultiPolygon

MultiPatch 处理

MultiPatch 是一种复杂的 3D 几何类型,包含多种部分类型(TriangleStrip、TriangleFan、Ring 等)。读取时会自动转换为 Polygon/MultiPolygon:

  • 保留:OuterRing、InnerRing、FirstRing、Ring
  • 忽略:TriangleStrip、TriangleFan(3D 渲染结构,无法转为多边形)

编码检测

detectDbfEncoding(buffer, cpgContent?)

检测 DBF 文件编码。

import { detectDbfEncoding } from '@sphinx_hq/shapefile-parser';

// 通过 LDID 自动检测
const encoding = detectDbfEncoding(dbfBuffer);

// 使用 .cpg 文件内容
const encoding = detectDbfEncoding(dbfBuffer, 'GBK');

检测优先级:用户指定 > .cpg 文件 > LDID(偏移29) > 默认 UTF-8

支持的编码

LDID 编码 说明
0x4D gbk 简体中文 GBK(ArcGIS 常用)
0x78 gbk 简体中文 GBK
0x79 gb2312 简体中文 GB2312
0x7A big5 繁体中文 Big5
0x7B gb18030 简体中文 GB18030
0xC8 utf-8 dBASE Level 7
0x00 windows-1252 ANSI

坐标系工具

import {
  // 查询
  getWktFromEpsg,
  identifyEpsgFromWkt,
  getAllEpsgCodes,
  hasEpsgCode,
  getEpsgEntry,
  // 解析与构建
  parseWkt,
  buildWkt,
  parseCrsInfo,
  resolveWktFromCrs,
  // 自定义注册
  registerEpsgParams,
  registerEpsgWkt,
} from '@sphinx_hq/shapefile-parser';

getWktFromEpsg(code)

根据 EPSG 代码获取 ESRI WKT 字符串。

const wkt = getWktFromEpsg(4490);
// GEOGCS["GCS_China_Geodetic_Coordinate_System_2000",...]

identifyEpsgFromWkt(wkt)

从 WKT 字符串识别 EPSG 代码。通过数值参数比对,忽略名称差异。

const code = identifyEpsgFromWkt(wktString);
// 4490

registerEpsgWkt(code, wkt, friendlyName?)

注册自定义坐标系(WKT 字符串形式)。

registerEpsgWkt(999001, customWkt, '自定义坐标系');
const wkt = getWktFromEpsg(999001);

非标准 PRJ 文件处理

当读取 Shapefile 时,如果 .prj 文件包含非标准的坐标系名称,会通过数值参数比对自动识别正确的 EPSG 代码:

// 原始 PRJ 内容(非标准名称)
const nonStandardWkt = `GEOGCS["My_Custom_Name",DATUM["D_China_2000",SPHEROID["CGCS2000",6378137.0,298.257222101]],...]`;

// 自动识别为 EPSG:4490(CGCS2000)
const result = await parser.read({ shp, prj });
console.log(result[Object.keys(result)[0]].crs); // 'EPSG:4490'

比对原理:只比较数值参数(椭球体长半轴、扁率倒数、中央经线、偏移量等),忽略字符串名称差异。

内置坐标系

共 243 个 EPSG 代码,覆盖中国常用及全球常用坐标系。

全球常用

EPSG 名称 说明
4326 WGS84 GPS 标准坐标系
3857 Web Mercator Web 地图常用
32601-32660 UTM N 北半球 UTM 投影
32701-32760 UTM S 南半球 UTM 投影

CGCS2000(中国大地坐标系)

类型 EPSG 范围 说明
地理坐标系 4490 CGCS2000 地理坐标系
3度带(带号) 4513-4533 Zone 25-45,中央经线 75°E-135°E
3度带(CM) 4534-4554 中央经线 75°E-135°E,无带号偏移
6度带 4491-4501 Zone 13-23,中央经线 75°E-135°E

北京54

类型 EPSG 范围 说明
地理坐标系 4214 北京54 地理坐标系
3度带 2401-2453 Zone 25-45
6度带 21413-21423 Zone 13-23

西安80

类型 EPSG 范围 说明
地理坐标系 4610 西安80 地理坐标系
3度带 2349-2371 Zone 25-45
6度带 2327-2337 Zone 13-23

浏览器使用

ES Module

<script type="module">
  import { ShapefileParser } from 'https://unpkg.com/@sphinx_hq/shapefile-parser/dist/shapefile-parser.esm.js';
  
  const parser = new ShapefileParser();
  
  document.getElementById('fileInput').addEventListener('change', async (e) => {
    const files = e.target.files;
    const result = await parser.read(files);
    
    // 获取第一个图层
    const firstKey = Object.keys(result)[0];
    const geojson = result[firstKey];
    console.log(geojson);
    
    // 修改后导出下载
    const writeResult = await parser.write(geojson, {
      filename: 'modified',
      download: true,
    });
  });
</script>

UMD

<script src="https://unpkg.com/@sphinx_hq/shapefile-parser/dist/shapefile-parser.umd.js"></script>
<script>
  const parser = new ShapefileParser();
  // ...
</script>

Shapefile 文件说明

文件 必需性 说明
.shp 必需 几何数据
.dbf 可选 属性数据
.shx 可选 索引文件(读取时不需要,写入时可选生成)
.prj 可选 坐标系信息
.cpg 可选 DBF 编码声明

最小文件组合:仅需 .shp 文件即可读取(属性将为空对象)。

错误处理

import { ShapefileError, ErrorCode } from '@sphinx_hq/shapefile-parser';

try {
  const result = await parser.read(input);
} catch (error) {
  if (error instanceof ShapefileError) {
    switch (error.code) {
      case ErrorCode.INVALID_FIELD_NAME:
        console.error('字段名不合法:', error.message);
        break;
      case ErrorCode.MISSING_SHP:
        console.error('缺少 .shp 文件');
        break;
      case ErrorCode.DBF_PARSE_ERROR:
        console.error('DBF 解析失败,可能需要指定编码');
        break;
      default:
        console.error(error.message);
    }
  }
}

License

MIT © YuanYu

本软件完全开源,可自由使用、修改和分发,包括商业用途。唯一要求是保留原始版权声明。

联系方式

About

Pure frontend Shapefile parser and exporter library | 纯前端 Shapefile 解析与导出工具库

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors