readshp
v0.0.2
Published
High-performance Shapefile reader for Node.js, supporting ESM/CommonJS, GeoJSON output, stream processing, and geometry simplification
Downloads
14
Maintainers
Readme
readshp
高性能、生产级的 Shapefile 解析库,基于 TypeScript 开发,同时支持 ESM 和 CommonJS 模块系统,适配 Node.js 环境。专注于速度、稳定性和类型安全,可解析点、线、面、多点等 Shapefile 几何类型,输出标准 GeoJSON,提供流式处理、几何简化、多线程加速等高级功能,适用于 GIS 数据处理、空间分析等场景。
特性
- ✅ 双模块支持:原生兼容 ESM(
import)和 CommonJS(require),无需额外配置 - ✅ 类型安全:完整 TypeScript 类型定义,IDE 自动补全+类型校验,减少 runtime 错误
- ✅ 高性能:对象池复用、坐标缓存、多线程加速,处理 1000 万+ 要素内存稳定
- ✅ 流式处理:大文件分块解析,避免内存溢出(支持 10GB+ 超大型 Shapefile)
- ✅ 全面兼容:支持 UTF-8/GBK/ISO-8859 等编码,兼容所有标准 Shapefile 几何类型
- ✅ 生产级稳定:完善的错误处理、资源自动释放,无内存泄漏
- ✅ 跨平台:支持 Windows/macOS/Linux,Node.js 14.13+ 均可运行
安装
# npm
npm install readshp
# yarn
yarn add readshp
# pnpm
pnpm add readshp依赖要求:Node.js 14.13.0+(ESM 支持需 Node.js 14.13+,CommonJS 支持 Node.js 12+,推荐 16+ 稳定版)
快速开始
根据项目使用的模块系统,选择对应的引入方式:
1. ESM 模块(推荐,import 语法)
适用于 package.json 中配置 "type": "module" 的项目,或使用 .mjs 后缀的文件。
1.1 基础解析(全量读取)
// ESM 基础解析示例
import readshp, { createShapefileStream } from 'readshp';
async function esmBasicParse() {
try {
// 解析 Shapefile(传入基础路径,无需扩展名)
const result = await readshp('./data/cities', {
encoding: 'utf-8', // 处理中文属性(如 GBK 编码需指定 'gbk')
simplify: 0.001, // 几何简化精度(0-1,值越大简化越明显)
logLevel: 'info', // 日志级别:silent/error/warn/info/debug
enableCache: true // 启用坐标缓存,减少重复坐标内存占用
});
// 输出结果(标准 GeoJSON FeatureCollection)
console.log(`解析完成:共 ${result.features.length} 个要素`);
console.log('数据边界框:', result.bbox);
console.log('第一个要素属性:', result.features[0].properties);
} catch (err) {
// 精细化错误处理
if (err.name === 'MissingFileError') {
console.error('错误:缺少 .shp/.shx/.dbf 必要文件', err.message);
} else if (err.name === 'EncodingError') {
console.error('错误:编码不匹配,尝试添加 encoding: "gbk" 配置', err.message);
} else {
console.error('解析失败:', err.message);
}
}
}
esmBasicParse();1.2 流式解析(大文件处理)
// ESM 流式解析示例(处理 100 万+ 要素)
import { createShapefileStream } from 'readshp';
async function esmStreamParse() {
const stream = createShapefileStream('./data/national-roads', {
highWaterMark: 256 * 1024, // 缓冲区大小(256KB,大文件建议增大)
workerCount: 4, // 多线程数量(默认 CPU 核心数/2,最大不超过 8)
filter: (feature) => { // 过滤:仅保留长度 > 1000m 的道路
return feature.properties.length > 1000;
}
});
let processedCount = 0;
// 逐要素处理(for await...of 适配流式迭代)
for await (const feature of stream) {
processedCount++;
// 业务逻辑:如写入数据库、转换为其他格式等
console.log(`处理道路 ${processedCount}:${feature.properties.name}`);
}
console.log(`流式解析完成:共处理 ${processedCount} 条道路数据`);
}
esmStreamParse().catch(console.error);2. CommonJS 模块(require 语法)
适用于传统 Node.js 项目(package.json 无 "type": "module",或使用 .cjs 后缀的文件)。
2.1 基础解析(全量读取)
// CommonJS 基础解析示例
const readshp = require('readshp');
async function cjsBasicParse() {
try {
const result = await readshp('./data/cities', {
encoding: 'gbk', // 处理 GBK 编码的中文属性
simplify: 0.005, // 中等简化精度,平衡精度和性能
logLevel: 'warn' // 仅输出警告和错误日志
});
console.log(`解析完成:${result.features.length} 个城市`);
} catch (err) {
console.error('CommonJS 解析错误:', err.message);
}
}
cjsBasicParse();2.2 流式解析(大文件处理)
// CommonJS 流式解析示例
const { createShapefileStream } = require('readshp');
function cjsStreamParse() {
const stream = createShapefileStream('./data/large-polygons', {
workerCount: 2,
highWaterMark: 128 * 1024
});
let count = 0;
// 监听 data 事件处理要素
stream.on('data', (feature) => {
count++;
console.log(`处理要素 ${count}:${feature.properties.id}`);
});
// 监听结束事件
stream.on('end', () => {
console.log(`CommonJS 流式解析完成:共 ${count} 个要素`);
});
// 监听错误事件
stream.on('error', (err) => {
console.error('流式解析错误:', err.message);
});
}
cjsStreamParse();模块支持说明
readshp 通过以下配置实现双模块兼容,确保 ESM 和 CommonJS 项目均可无缝使用:
1. 底层配置(package.json)
{
"type": "module", // 优先 ESM
"main": "./dist/cjs/index.js", // CommonJS 入口
"module": "./dist/esm/index.js", // ESM 入口
"types": "./dist/types/index.d.ts", // 类型定义入口(双模块共享)
"exports": {
".": {
"import": "./dist/esm/index.js", // ESM 引入路径
"require": "./dist/cjs/index.js", // CommonJS 引入路径
"types": "./dist/types/index.d.ts" // 类型定义
}
}
}2. 兼容性注意事项
- ESM 要求:Node.js 14.13.0+(建议 16+),项目需配置
"type": "module"或使用.mjs文件 - CommonJS 要求:Node.js 12.0.0+,无需额外配置,直接用
require引入 - TypeScript 项目:无论使用 ESM 还是 CommonJS,只需确保
tsconfig.json中module配置与项目一致(如ESNext对应 ESM,CommonJS对应 CommonJS)
API 文档
1. 核心函数
readshp<T>(path: string, options?: ParseOptions<T>): Promise<FeatureCollection<T>>
全量解析 Shapefile,返回完整的 FeatureCollection(标准 GeoJSON)。
| 参数 | 类型 | 说明 |
|------------|-------------------------------|----------------------------------------------------------------------|
| path | string | Shapefile 基础路径(如 ./data/cities,需存在 cities.shp/cities.shx/cities.dbf) |
| options | ParseOptions<T> | 解析配置(见下文) |
| 返回值 | Promise<FeatureCollection<T>>| 包含所有要素、边界框的 GeoJSON 对象 |
createShapefileStream<T>(path: string, options?: ParseOptions<T>): Readable
创建流式解析器,返回 Node.js 可读流(objectMode: true),逐要素输出 Feature<T>。
| 参数 | 类型 | 说明 |
|------------|-------------------------------|----------------------------------------------------------------------|
| path | string | 同 readshp 函数 |
| options | ParseOptions<T> | 同 readshp 函数 |
| 返回值 | Readable | 可读流,支持 for await...of 或 on('data') 处理要素 |
2. 解析配置(ParseOptions<T>)
| 选项名 | 类型 | 默认值 | 说明 |
|-----------------|-------------------------------|---------------------------------|----------------------------------------------------------------------|
| encoding | string | 'utf-8' | DBF 属性编码(支持 utf-8/gbk/gb2312/ISO-8859-1/CP1252 等) |
| simplify | number | 0 | 几何简化精度(0 = 不简化,建议范围 0.0001 ~ 0.1,值越大顶点越少) |
| filter | (feature: Feature<T>) => boolean | () => true | 要素过滤函数(返回 true 保留,false 过滤) |
| workerCount | number | Math.max(1, CPU核心数 / 2) | 多线程数量(用于几何简化,最大值建议 ≤ 8,避免线程调度开销) |
| highWaterMark | number | 64 * 1024(64KB) | 读取缓冲区大小(大文件建议设为 128*1024 或 256*1024) |
| enableCache | boolean | false | 启用坐标缓存(重复坐标多的数据集(如建筑轮廓)可减少 40%-60% 内存) |
| logLevel | 'silent'/'error'/'warn'/'info'/'debug' | 'info' | 日志级别(silent 关闭所有日志,debug 输出解析细节) |
3. 错误类型
解析过程中抛出的错误可通过 err.name 区分,便于精细化处理:
| 错误名称 | 场景说明 | 解决方案 |
|------------------------|-------------------------------------------|-------------------------------------------|
| MissingFileError | 缺少 .shp/.shx/.dbf 任一必要文件 | 检查文件路径和完整性,确保三文件齐全 |
| InvalidFormatError | Shapefile 格式损坏(文件头错误、数据截断) | 验证文件有效性(可通过 QGIS 打开测试) |
| EncodingError | DBF 编码转换失败(中文乱码、特殊字符报错) | 指定正确编码(如 encoding: 'gbk') |
| ResourceReleaseError | 资源释放失败(文件句柄关闭异常) | 避免解析中强制终止程序,检查系统权限 |
| ShapefileError | 其他通用错误 | 查看错误详情,提交 GitHub Issue 反馈 |
高级功能示例
1. 几何简化(平衡精度与性能)
// ESM 示例:简化高精度面数据(如省级行政边界)
import readshp from 'readshp';
async function simplifyExample() {
const result = await readshp('./data/provinces', {
simplify: 0.002, // 保留核心轮廓,减少 70% 顶点数量
enableCache: true
});
console.log(`简化前顶点数:${result.features[0].geometry.coordinates[0].length}`);
console.log(`简化后顶点数:${result.features[0].geometry.coordinates[0].length}`);
}2. 多线程加速(复杂几何处理)
// CommonJS 示例:4 线程处理 500 万线要素
const { createShapefileStream } = require('readshp');
async function multiThreadExample() {
const stream = createShapefileStream('./data/railways', {
workerCount: 4, // 4 线程并行简化
simplify: 0.001, // 线要素简化
highWaterMark: 256 * 1024
});
let count = 0;
for await (const feature of stream) {
count++;
}
console.log(`多线程处理完成:${count} 条铁路数据`);
}测试与调试
1. 本地测试
# 安装开发依赖
npm install -D
# 运行单元测试(覆盖双模块引入逻辑)
npm test
# 运行集成测试(解析真实 Shapefile 数据)
npm run test:integrate
# 查看测试覆盖率
npm run test:coverage2. 调试建议
- ESM 项目:使用
node --inspect index.mjs启动调试 - CommonJS 项目:使用
node --inspect index.js启动调试 - 日志调试:将
logLevel设为'debug',查看解析过程中的详细日志(如文件读取进度、要素数量)
