Shapefile 简介与使用指南
📖 什么是 Shapefile
Shapefile 是由 ESRI 公司开发的一种矢量数据存储格式,是地理信息系统(GIS)中最常用的空间数据格式之一。它能够存储地理要素的位置、形状和属性信息。
核心特点
- 开放标准 - 公开的文件格式规范
- 广泛支持 - 几乎所有 GIS 软件都支持
- 矢量数据 - 存储点、线、面等几何图形
- 属性数据 - 可关联丰富的属性信息
- 跨平台 - 支持多种操作系统
📁 文件结构
Shapefile 实际上是一个文件集合,包含多个相关文件:
必需文件
.shp
- 存储几何图形数据.shx
- 存储几何图形索引.dbf
- 存储属性数据表
可选文件
.prj
- 存储坐标系统信息.sbn/.sbx
- 空间索引文件.cpg
- 字符编码信息.xml
- 元数据信息
example_shapefile/
├── roads.shp # 几何数据
├── roads.shx # 索引文件
├── roads.dbf # 属性表
├── roads.prj # 投影信息
└── roads.cpg # 编码信息
🛠️ 常用工具与库
桌面软件
- QGIS - 免费开源 GIS 软件
- ArcGIS - 商业 GIS 软件
- MapInfo - 专业地图软件
JavaScript 库
1. shapefile.js - 基础读取
javascript
// 使用 shapefile.js
import * as shapefile from "shapefile";
// 基本读取
shapefile.open("path/to/shapefile.shp")
.then(source => source.read()
.then(function log(result) {
if (result.done) return;
console.log(result.value);
return source.read().then(log);
}));
// 读取所有要素
async function readAllFeatures(shpPath) {
const source = await shapefile.open(shpPath);
const features = [];
let result = await source.read();
while (!result.done) {
features.push(result.value);
result = await source.read();
}
return features;
}
// 转换为 GeoJSON
shapefile.read("data.shp", "data.dbf")
.then(geojson => {
console.log(geojson);
});
💻 Web 开发中的使用
前端处理
1. 使用 Leaflet
javascript
// 需要先转换为 GeoJSON
L.geoJSON(geojsonData, {
style: function (feature) {
return {color: 'blue'};
}
}).addTo(map);
2. 使用 OpenLayers
是的!VectorSource 的 url 可以直接放 ZIP 文件!
javascript
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import {Shapefile} from 'ol/format';
// 方式1:直接加载 ZIP 格式的 shapefile
const vectorLayer = new VectorLayer({
source: new VectorSource({
url: 'data/shapefile.zip', // ✅ 支持 ZIP 格式
format: new Shapefile()
}),
style: {
'fill-color': 'rgba(255, 255, 255, 0.6)',
'stroke-color': '#319FD3',
'stroke-width': 1
}
});
// 方式2:加载远程 ZIP 文件
const remoteLayer = new VectorLayer({
source: new VectorSource({
url: 'https://example.com/data/cities.zip',
format: new Shapefile()
})
});
// 方式3:带参数配置
const configuredLayer = new VectorLayer({
source: new VectorSource({
url: 'data/complex_shapefile.zip',
format: new Shapefile({
// 可选配置
})
})
});
ZIP 文件要求:
- 包含完整的 shapefile 文件集(.shp, .shx, .dbf 等)
- 文件名必须一致
- 建议包含 .prj 文件以确保坐标系正确
优势:
- 减少 HTTP 请求次数
- 文件传输更高效
- 保持文件完整性
完整示例:
javascript
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import OSM from 'ol/source/OSM';
import {Shapefile} from 'ol/format';
// 创建地图
const map = new Map({
target: 'map',
layers: [
// 底图
new TileLayer({
source: new OSM()
}),
// Shapefile 图层(ZIP 格式)
new VectorLayer({
source: new VectorSource({
url: 'data/boundaries.zip', // ZIP 文件路径
format: new Shapefile()
}),
style: {
'stroke-color': '#ff0000',
'stroke-width': 2,
'fill-color': 'rgba(255, 0, 0, 0.1)'
}
})
],
view: new View({
center: [0, 0],
zoom: 2
})
});
// 监听图层加载
const shapefileLayer = map.getLayers().getArray()[1];
shapefileLayer.getSource().on('featuresloadend', function() {
console.log('Shapefile 加载完成');
// 缩放到图层范围
const extent = shapefileLayer.getSource().getExtent();
map.getView().fit(extent, {padding: [20, 20, 20, 20]});
});
注意事项:
- ZIP 文件中的所有 shapefile 组件必须在根目录
- 确保服务器正确设置 MIME 类型:
application/zip
- 大文件建议启用 gzip 压缩传输
🔄 JavaScript 格式转换
Shapefile 转 GeoJSON
javascript
import * as shapefile from "shapefile";
import fs from 'fs';
// 转换单个文件
async function shapefileToGeoJSON(inputPath, outputPath) {
const geojson = await shapefile.read(inputPath);
fs.writeFileSync(outputPath, JSON.stringify(geojson, null, 2));
console.log('转换完成');
}
// 使用示例
shapefileToGeoJSON('input.shp', 'output.geojson');
// 批量转换
async function batchConvert(files) {
for (const file of files) {
const outputName = file.replace('.shp', '.geojson');
await shapefileToGeoJSON(file, outputName);
}
}
在线转换工具
📊 JavaScript 数据分析
空间查询与统计
javascript
import * as turf from '@turf/turf';
import * as shapefile from 'shapefile';
// 读取并分析数据
async function analyzeShapefile(path) {
const geojson = await shapefile.read(path);
// 基本统计
const featureCount = geojson.features.length;
console.log(`要素总数: ${featureCount}`);
// 计算面积(对于面要素)
const areas = geojson.features.map(feature => {
if (feature.geometry.type === 'Polygon') {
return turf.area(feature);
}
return 0;
});
const totalArea = areas.reduce((sum, area) => sum + area, 0);
console.log(`总面积: ${totalArea} 平方米`);
return { featureCount, totalArea, areas };
}
// 空间查询
async function spatialQuery(shapefilePath, queryPoint, distance) {
const geojson = await shapefile.read(shapefilePath);
const point = turf.point(queryPoint);
// 查找指定距离内的要素
const nearby = geojson.features.filter(feature => {
const dist = turf.distance(point, turf.centroid(feature));
return dist <= distance;
});
return {
type: 'FeatureCollection',
features: nearby
};
}
// 属性统计
function attributeStats(geojson, propertyName) {
const values = geojson.features
.map(f => f.properties[propertyName])
.filter(v => v !== null && v !== undefined);
const counts = {};
values.forEach(val => {
counts[val] = (counts[val] || 0) + 1;
});
return {
total: values.length,
unique: Object.keys(counts).length,
distribution: counts
};
}
JavaScript 编码处理
javascript
// 处理中文编码问题
import iconv from 'iconv-lite';
async function readShapefileWithEncoding(path, encoding = 'utf8') {
try {
const geojson = await shapefile.read(path, undefined, {
encoding: encoding
});
return geojson;
} catch (error) {
// 尝试其他编码
console.log('尝试 GBK 编码...');
return await shapefile.read(path, undefined, {
encoding: 'gbk'
});
}
}
性能优化
- 大文件使用空间索引
- 适当简化几何图形
- 考虑分块处理
数据质量
- 检查几何有效性
- 处理空值和异常值
- 验证坐标系统
📚 学习资源
官方文档
教程推荐
示例数据
- Natural Earth - 免费地理数据
- OpenStreetMap - 开源地图数据