- GIS
- CLI
- 数据可视化
【免费下载链接】mapshaper
Tools for editing Shapefile, GeoJSON, TopoJSON and CSV files
GeoParquet 是一种将矢量几何与表格属性一并存储的紧凑列式格式,已成为云原生地理数据交换的主流载体。本文以 Mapshaper 官方文档《GeoParquet》为骨架,结合仓库中 导入实现 与 导出实现 的源码细节,系统讲解 Mapshaper 读取与写出.parquet文件的行为规则、compression=/level=/rowgroup=等输出选项的取值与语义,以及大文件场景下的内存约束与规避策略,帮助读者在 CLI 与 Web 端正确、高效地使用 GeoParquet。
为什么选择 GeoParquet:格式定位与适用场景
GeoParquet 是一种紧凑的列式(columnar)二进制矢量格式,把矢量几何与表格属性存放在同一文件中。它目前已成为云原生矢量数据的常见交换格式:Overture Maps Foundation 的全球数据发布即采用 GeoParquet,DuckDB(spatial 扩展)、GeoPandas、BigQuery、Athena、Synapse 等工具都能直接查询 GeoParquet 数据集。
在 Mapshaper 中,GeoParquet 的格式能力概览如下(与 格式总览 中的对照表一致):
| 项目 | 说明 |
|---|---|
| 文件扩展名 | .parquet、.geoparquet |
| 读取 | 支持 |
| 写入 | 支持 |
| 图层数量 | 不支持多图层,一个文件对应一个图层 |
| 几何编码 | 读取时支持 WKB;写出时统一为 WKB |
需要说明的是,与 Shapefile、DBF、CSV 等格式不同,GeoParquet 的文件编码固定为 UTF-8,不受encoding=选项影响(参见 格式总览)。
快速上手:三条 CLI 命令
官方文档给出了一组最小可用的 CLI 示例,覆盖"读取信息、GeoParquet 转 GeoJSON、GeoJSON 转 GeoParquet"三个典型场景:
# 查看 GeoParquet 文件的基本信息(图层、几何类型、字段、CRS 等) mapshaper roads.parquet -info # 把 GeoParquet 转换为 GeoJSON mapshaper roads.parquet -o roads.geojson # 把 GeoJSON 转换为 GeoParquet mapshaper roads.geojson -o roads.parquetMapshaper 会根据文件扩展名自动推断输入输出格式,因此通常无需显式声明format=;如需强制指定,可在-i或-o上使用format=geoparquet(导出时format=parquet亦可,作为别名,这一点由 导出测试 验证)。导出文件名默认取图层名加.parquet扩展名;由于 GeoParquet 不支持多图层,一个数据集内每个图层会各自写成一个文件。
读取 GeoParquet:输入行为与底层实现
官方文档明确指出:没有 GeoParquet 专属的-i输入选项。所有行为都来自导入器的默认逻辑,可以从 mapshaper-geoparquet-import.mjs 的importGeoParquet()调用链中还原:
- 动态加载
hyparquet库,读取文件字节; - 通过
parquetMetadataAsync()解析 Parquet footer 元数据,再调用parseGeoParquetMetadata()从 key-value 元数据中提取键为geo的 GeoParquet 元数据 JSON; - 通过
parquetReadObjects()以rowFormat: 'object'逐行读出所有记录; - 调用
convertGeoParquetRows()把每一行转换为 GeoJSON Feature 并交给 GeoJSON 解析器,最终构建出 Mapshaper 内部数据集。
以下是导入时需要注意的几个行为细节。
每个文件只导入一个几何列
Mapshaper 每个文件只导入一个几何列:当文件带有 GeoParquet 元数据时,优先使用primary_column指定的列;若该列不存在或不是 WKB 编码,则回退到第一个 WKB 编码的几何列;完全没有元数据时,则查找名为geometry的列,或通过"对象带有type字符串"的启发式规则识别 GeoJSON 式几何。文件中额外的几何列目前会被忽略,不会报错,也不会进入属性表。
仅支持 WKB 编码:原生编码降级为纯属性导入
几何列必须使用WKB(Well-Known Binary)编码。若文件中只有原生(native)编码而没有 WKB 列,导入器会发出一次性警告:
Unable to import GeoParquet geometry: native encodings are not supported (expected WKB). Importing attribute data only.随后仅导入属性数据(图层几何类型为 null)。这一点由 导入测试 用data-point-encoding_native.parquet固定样本验证:4 条记录全部导入,但geometry_type为 null。仓库的 测试数据目录 中同时准备了 point / linestring / polygon / multipoint / multilinestring / multipolygon 的 native 与 wkb 两种编码样本,可直接用于对比验证。
属性值规范化:BigInt 与精度警告
Parquet 的整数可能以 JavaScript 无法精确表示的 BigInt 形式读回。导入器会对属性值做递归规范化(normalizeGeoParquetValue):安全范围内的 BigInt 直接转为 Number;超出Number.MAX_SAFE_INTEGER或超出双精度范围的值会发出"precision will be lost"警告后截断转换。测试用例分别覆盖了"42n 无警告转换"与"9007199254740993n 丢失精度并警告"两种情形。
CRS:从元数据解析坐标参考系
导入器会把geo元数据中几何列的crs解析为 Mapshaper 内部可用的crs_string(写入dataset.info.crs_string),并同时保留dataset.info.geoparquet_geo与dataset.info.geoparquet_crs供后续使用。解析策略(getGeoParquetCrsStrings)是:
- 若 CRS 带有 EPSG 或 ESRI authority 标识,优先构造
epsg:XXXX/esri:XXXX字符串; - 否则尝试用
mproj库把 PROJJSON 转换为 Proj4 字符串; - 全部失败时发出 "Unable to import CRS from GeoParquet metadata" 警告。
一个关键实现细节:不会把base_crs当作投影 CRS 的标识——base_crs是投影所基于的地理 CRS,用它替换缺失的顶层id会把投影坐标误读为经纬度(源码注释 与 导入测试 均明确说明)。此外,导入测试还验证了 EPSG 查询失败时自动回退到 PROJJSON→Proj4 的容错路径。
写出 GeoParquet:输出行为与底层实现
导出的核心实现在 mapshaper-geoparquet-export.mjs 的exportGeoParquet()中:逐图层构建列式数据,把每行的 GeoJSON 几何交给hyparquet-writer编码为 WKB,以行组(row group)为单位分块写出。
统一的 WKB 几何列与类型推断
- 写出时固定使用一个名为
geometry的 WKB 几何列; - 属性列的类型由
inferColumnType()逐行采样推断:布尔→BOOLEAN,安全范围内的整数→INT32,其余有限数→DOUBLE,Date→TIMESTAMP,字节缓冲→BYTE_ARRAY,数组/对象→JSON,其余→STRING;同一列出现 INT32 与 DOUBLE 混用时提升为DOUBLE,出现不可调和的分歧时降级为STRING。测试断言了导出 schema 为geometry: BYTE_ARRAY, name: BYTE_ARRAY, count: INT32, ratio: DOUBLE, flag: BOOLEAN(导出测试)。 - 图层无几何时,若仍有属性则照常导出为纯 Parquet 表(伴随 "writing attribute data only" 警告);几何与属性皆无(或零记录)时会直接报错
GeoParquet export requires at least one record/GeoParquet export requires geometry or attribute data。
CRS 双重写入:geo 元数据 + GEOMETRY 逻辑类型
若图层带有 CRS,导出器会把它同时写入两处(buildSchema):
- GeoParquet 的
geo元数据(版本1.1.0,primary_column: "geometry",encoding: "WKB",含geometry_types与crs); - Parquet 2.11 引入的
GEOMETRY逻辑类型(logical_type: {type: 'GEOMETRY', crs: ...})。
原因在于:支持 GEOMETRY 逻辑类型的读者会忽略geo元数据,而逻辑类型不带 CRS 时规范默认按OGC:CRS84处理,会把投影数据误报为 WGS 84。双写是 GDAL 的通行做法,可让 GeoParquet 1.x 读者继续正常工作(导出测试 验证了两处 CRS 一致)。CRS 来源优先取导入时保留下来的geoparquet_crs,否则通过mproj把crs_string或 WKT 转换为 PROJJSON。导入测试也验证了 Vermont UTM(epsg:32618)样本可以无损往返。
行组规划:按字节而非按行数分块
行组大小由字节预算而非行数决定(planRowGroups):点几何一行仅几十字节,而高细节多边形一行可达数万字节,按固定行数切分会带来上百倍的偏差。实现要点:
- 单行大小通过对图层等间隔采样(默认 1000 行)估算,采样按步长散布而非取前缀,避免排序/聚集数据造成偏差;
- 目标行组约16 MB(
ROW_GROUP_TARGET_BYTES),行数上限 100 万行;文件开头额外留一个约1 MB的小预览组(ROW_GROUP_PREVIEW_BYTES),便于 HTTP 范围读取的读者快速看到表头数据; - 剩余行会均匀分摊到后续行组,避免末尾出现"袖珍组";
- 导出采用逐行组转换、随写随弃的策略,GeoJSON→WKB 的临时内存占用只与行组大小相关,而不是整个图层的大小(源码注释)。
行组规划测试 对这一策略做了系统验证:小图层单组、大几何自动拆分、预览组保持约 1 MB、行数上限 100 万、单行超预算时仍能逐行推进。
地理统计边界修正:与 pyarrow/GDAL 的兼容性
导出器还会修正行组地理统计中的 bbox 边界(avoidIntegerBboxBounds):hyparquet-writer会根据运行时值推断 thrift 线类型,若 bbox 边界恰好落在整数上会被写成 I32,而 Parquet 规范要求 double——pyarrow 21+、GDAL 3.11+ 等解析地理统计的读者会因此无法反序列化 footer,直接打不开文件。实现通过位级步进把整数边界外扩到相邻 double(宁大勿小,只影响读者跳过行组的收益);超过 2^53 无法步进时则删除该 bbox。地理统计测试 断言所有行组的 bbox 边界均非整数且仍覆盖全部点。
通用输出选项的配合
-o的通用选项同样作用于 GeoParquet:例如precision=会先对坐标取整再编码,取整后几何坍缩为空的要素仍保留在属性表中、几何列写为 null(导出测试)。另外,被hoist=提升到 GeoJSON Feature 根部的属性不会成为 Parquet 列(getPropertyNames)。
输出选项详解:compression、level 与 rowgroup
官方文档列出的 GeoParquet 专属输出选项有两个,CLI 选项注册见 mapshaper-options.mjs;此外源码还支持第三个rowgroup=选项。
compression=snappy|zstd|none
选择 Parquet 列压缩算法,默认snappy:
- snappy:压缩与解压速度快,压缩比中等,适合读写频繁的场景;
- zstd:压缩更慢但通常文件更小,适合以文件大小为首要目标的情形(例如发布下载包或存入云存储桶);
- none:写出未压缩的 Parquet(
uncompressed亦可作为等价取值)。
底层由 normalizeGeoParquetCompression 校验,未知取值会直接报错;测试确认compression=zstd写出文件的列编码为ZSTD、compression=none为UNCOMPRESSED。浏览器端与 Node 端分别通过@bokuweb/zstd-wasm的运行时加载与动态 import 提供 ZSTD 压缩器(mapshaper-gui-geoparquet.mjs 在 Web 端注册了hyparquet、hyparquet-compressors、hyparquet-writer与 zstd-wasm 四个模块)。
level=:ZSTD 压缩级别
仅在使用compression=zstd时有效,取值必须是1 到 22 的整数;省略时使用 ZSTD 库默认级别。校验逻辑在 validateGeoParquetCompressionLevel:在非 zstd 压缩下指定level=会报错 "The level= option only applies with compression=zstd"(导出测试 验证)。另有一个实现细节:当级别 ≥ 10 时,输出 page size 会固定为 64 KB(getGeoParquetPageSize)。若某页压缩失败(通常因级别过高),会提示降低level=值。
# 以 ZSTD 压缩、级别 10 写出 mapshaper roads.geojson -o format=geoparquet compression=zstd level=10 # 写出未压缩的 GeoParquet mapshaper roads.geojson -o roads.parquet compression=nonerowgroup=:手动指定每行组行数
源码还提供了文档未展开的第三个选项rowgroup=,描述为 "[GeoParquet] rows per row group (default: sized by data)"。取值为正整数行数;不指定时按上文"按字节规划"的默认策略分块。校验逻辑要求rowgroup >= 1且为整数,否则报错 "The rowgroup= option must be a positive integer number of rows"。测试用例rowgroup=250会把 1000 行切成 4 个 250 行的行组(导出测试)。
# 每个行组固定 250 行 mapshaper roads.geojson -o format=geoparquet rowgroup=250内存与规模:大文件处理建议
官方文档特别强调:GeoParquet 没有固定的文件大小上限,限制主要来自内存。
- 常规 CLI:上限即 Node 的堆大小。处理超大文件时改用
mapshaper-xl,它默认以8 GB 堆启动,并可进一步调大,例如mapshaper-xl 16gb ...。 - Web 应用:非常大的导入(通常取决于浏览器/设备,几百 MB 的量级)可能耗尽内存导致标签页崩溃。因此大 GeoParquet 文件请优先使用 CLI /
mapshaper-xl。
这一约束与导出端"整个数据集常驻内存、写出约需输出体积二十倍内存"的实现(行组规划源码注释)互为印证:行组规划的 16 MB 目标正是为了在单机内存模型下给读者保留并行度与过滤跳过的空间。
支持范围小结
| 能力 | 状态与说明 |
|---|---|
| 扩展名 | .parquet、.geoparquet(自动识别,可用format=覆盖) |
| 读取几何编码 | 仅 WKB;原生编码降级为纯属性导入并警告 |
| 写入几何列 | 单列、固定名为geometry的 WKB |
| 多图层 | 不支持,一个文件一个图层 |
| 压缩 | snappy(默认)/zstd/none |
| ZSTD 级别 | level=1–22 |
| 行组大小 | rowgroup=指定行数,默认按字节预算自动规划 |
| CRS | 导入解析 EPSG/ESRI 或 PROJJSON→Proj4;导出双写 geo 元数据与 GEOMETRY 逻辑类型 |
| 大文件 | 常规 CLI 受 Node 堆限制;建议mapshaper-xl 16gb;Web 端几百 MB 可能崩溃 |
进一步探索
- 格式对照总览:docs/formats/overview.md
- 导入源码:src/geoparquet/mapshaper-geoparquet-import.mjs
- 导出源码:src/geoparquet/mapshaper-geoparquet-export.mjs
- 导入测试:test/geoparquet-import-test.mjs;导出测试:test/geoparquet-export-test.mjs
- 测试样本:test/data/geoparquet(含 WKB/native 双编码的 point、linestring、polygon 及 multipoint/multilinestring/multipolygon 样本,Vermont CRS 系列样本,ZSTD 压缩样本
sample-zstd.parquet等) - 命令行选项定义:src/cli/mapshaper-options.mjs
另外说明:Mapshaper 的 GeoParquet 导入基于hyparquet库实现,写入基于hyparquet-writer,Web 端通过 mapshaper-gui-geoparquet.mjs 打包对应模块;更完整的格式规范与几何逻辑类型细节可参阅官方 GeoParquet 规范与 Apache Parquet 的 Geospatial 逻辑类型说明。
- GIS
- CLI
- 数据可视化
【免费下载链接】mapshaper
Tools for editing Shapefile, GeoJSON, TopoJSON and CSV files
相关推荐
Home Assistant Blue Current 集成:使用 `blue_current.start_charge_session` 动作远程启动充电会话
Home Assistant Blue Current 集成:使用 blue_current.start_charge_session 动作远程启动充电会话 本
GISCLI数据可视化PPT Master:把任意文档变成可完全编辑的 PowerPoint
PPT Master:把任意文档变成可完全编辑的 PowerPoint PPT Master 是一个开源的 AI PPT 生成工作流:给它一份 PDF、Word
AI 技能人工智能AI 应用旧Mac升级系统:一台2015款MacBook Pro免费续命Sequoia的全过程实录
旧Mac升级系统:一台2015款MacBook Pro免费续命Sequoia的全过程实录 如果你手头有一台被苹果官方系统支持列表"除名"的Mac,别急着判它死刑
操作系统固件驱动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考