npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sphinx_hq/shapefile-parser

v0.0.6

Published

纯前端 Shapefile 解析与导出工具库,无需后端服务,支持 GeoJSON 互转、坐标系自动识别、多种编码检测

Readme

@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

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

联系方式