Skip to content

Shapefile 简介与使用指南

上次更新 2025年8月24日星期日 13:22:33 字数 0 字 时长 0 分钟

📖 什么是 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'
    });
  }
}

性能优化

  • 大文件使用空间索引
  • 适当简化几何图形
  • 考虑分块处理

数据质量

  • 检查几何有效性
  • 处理空值和异常值
  • 验证坐标系统

📚 学习资源

官方文档

教程推荐

示例数据

关注公众号