本文档面向希望开发 cnmaps 兼容数据包的开发者。
目标是把“cnmaps 可消费的数据包”需要满足的协议、目录约定和校验规则写清楚,避免第三方开发者只能读源码猜实现。
如果你只是想了解官方数据包本身的用途、安装方式和发布方式,请先看:
一个 cnmaps 兼容数据包建议具备以下特征:
- 是一个独立的 Python 包
- 安装后能被
cnmaps自动发现 - 明确声明自己提供哪些数据集
- 提供稳定的索引方式和数据目录组织
- 能通过自动检查脚本验证基本正确性
推荐采用如下结构:
your-data-package/
├── pyproject.toml
├── README.md
├── cnmaps_data_xxx/
│ ├── __init__.py
│ ├── provider.py
│ ├── manifest.json
│ └── data/
│ ├── index/
│ │ └── administrative.db
│ └── datasets/
│ ├── administrative/
│ ├── geography/
│ └── sample/
说明:
- 包名不要求一定叫
cnmaps_data - 但建议目录中保留一个 manifest 和 provider 模块
cnmaps真正依赖的是 provider 协议,而不是某个固定包名
cnmaps 当前期望 provider 对象至少提供这些方法:
get_dataset_root(dataset: str) -> strget_index_db(dataset: str = "administrative") -> strget_sample_path(filename: str) -> strresolve_dataset_path(dataset: str, relative_path: str) -> str
推荐 provider 还提供这些属性:
nameversionmanifest
一个最小 provider 可以类似这样:
from pathlib import Path
class MyProvider:
def __init__(self, package_root: Path):
self.package_root = package_root
self.name = "my-data-package"
self.version = "1.0.0"
def get_dataset_root(self, dataset: str) -> str:
return str((self.package_root / "data" / "datasets" / dataset).resolve())
def get_index_db(self, dataset: str = "administrative") -> str:
return str((self.package_root / "data" / "index" / "administrative.db").resolve())
def get_sample_path(self, filename: str) -> str:
return str((self.package_root / "data" / "datasets" / "sample" / filename).resolve())
def resolve_dataset_path(self, dataset: str, relative_path: str) -> str:
relative = Path(relative_path)
if relative.parts and relative.parts[0] == dataset:
relative = Path(*relative.parts[1:])
return str((Path(self.get_dataset_root(dataset)) / relative).resolve())
def get_provider():
return MyProvider(Path(__file__).resolve().parent)第三方数据包推荐通过 Python entry point 暴露 provider。
entry point group 名称固定为:
cnmaps.data_providers
例如在 pyproject.toml 中:
[tool.poetry.plugins."cnmaps.data_providers"]
my_data = "my_data_package.provider:get_provider"或 PEP 621 风格:
[project.entry-points."cnmaps.data_providers"]
my_data = "my_data_package.provider:get_provider"cnmaps 会读取这个 group 下的 provider。
建议每个数据包都提供一个 manifest.json,用于声明自己的数据集和路径。
当前官方包的最小结构如下:
{
"name": "cnmaps-data",
"provider": "official",
"version": "1.0.0",
"cnmaps_data_api_version": "1",
"datasets": {
"administrative": {
"kind": "boundary",
"description": "Administrative boundary datasets and indexes",
"index_db": "data/index/administrative.db",
"root": "data/datasets/administrative"
},
"geography": {
"kind": "boundary",
"description": "Geography boundary datasets",
"root": "data/datasets/geography"
},
"sample": {
"kind": "sample",
"description": "Sample gridded datasets for demos and tests",
"root": "data/datasets/sample"
}
}
}- 顶层:
nameproviderversioncnmaps_data_api_versiondatasets
datasets.administrative:kindrootindex_db
datasets.geography:kindroot
datasets.sample:kindroot
当前 cnmaps 对行政区数据索引的读取逻辑基于 SQLite,并假设至少存在一张名为 ADMINISTRATIVE 的表。
CREATE TABLE ADMINISTRATIVE
(
id text,
country text,
iso3 text,
province text,
city text,
district text,
path text,
level text,
source text,
kind text
);并建议:
id唯一- 对
id建唯一索引
id: 唯一标识country: 国家名称iso3: ISO3 或自定义组合码。官方cnmaps-data中,国外记录使用各自代码,中国行政区记录统一使用CHNprovince: 省级名称city: 市级名称district: 区县名称path: 相对数据路径level: 行政等级,当前cnmaps识别国/省/市/区县source: 数据源。官方cnmaps-data中,中国行政区使用高德,国外国家 / 地区级数据统一使用世界银行kind: 数据类型,如陆地/海域等
如果你的数据包中同时包含中国行政区与国外边界,建议不要把国外数据强行套入中国式的“省 / 市 / 区县”层级。
对于国外国家级边界,推荐写法是:
country: 国家名称iso3: ISO3 或自定义组合码province:NULLcity:NULLdistrict:NULLlevel:国
官方 cnmaps-data 中新增的 cn-neighbors 就遵循这一规则。
如果你要加入不与中国接壤的其他国家,推荐单独放在类似 world-countries 这样的 source 下:
country: 国家名称iso3: ISO3 或自定义组合码province:NULLcity:NULLdistrict:NULLlevel:国
官方 cnmaps-data 当前也采用这一方式承载非邻国的世界国家级边界。
需要注意的是,官方 cnmaps-data 虽然在目录结构上区分:
administrative/cn-neighbors/...administrative/world-countries/...
但在 SQLite 中二者当前统一写作:
source = 世界银行
也就是说:
source表示国外数据的来源/出处path目录前缀表示其在包内属于哪一类数据集
如果你的数据包中包含中国行政区,推荐像官方 cnmaps-data 一样按以下规则写入:
- 一般中国行政区记录:
iso3 = CHN - 香港特别行政区相关记录:
iso3 = HKG - 澳门特别行政区相关记录:
iso3 = MAC - 台湾相关记录:仍统一写作
iso3 = CHN
这条规则不仅适用于国家级记录,也适用于中国的省 / 市 / 区县级记录。这样在做跨层级筛选、统计或后续联动查询时会更一致。
如果第三方数据包希望与官方包保持一致,也建议在国家 / 地区级记录中显式写入 iso3,而不要只依赖 path 文件名表达代码。
如果你的国外国家级数据需要与某一套中国边界口径配套使用,建议像官方 world-countries 一样,在写出 GeoJSON 前先执行一次针对中国边界的几何扣除,避免与中国边界产生重叠。
官方 cnmaps-data 当前统一维护了一份国家中文名映射表,见:
推荐做法是:
- GeoJSON 与 SQLite 中直接保存最终中文名
- 映射表只作为维护和批量更新辅助资料
- 通过单独脚本回写名称,而不是让主生成脚本强依赖映射表
path 应写成相对于数据集根目录或带数据集前缀的相对路径。
当前兼容这两类写法:
amap/land/110000.geojsonadministrative/amap/land/110000.geojson
当前 cnmaps 的 read_mapjson() 读取逻辑对 GeoJSON 有比较明确的要求。
可以是:
- 直接是
geometry对象 - 或者是包含
geometry字段的 Feature 风格对象
PolygonMultiPolygonMultiLineString
对于行政区边界,建议使用 Polygon / MultiPolygon。
- 坐标需为标准 GeoJSON 坐标数组
- 行政区数据当前默认假设其来源可与
cnmaps的坐标转换逻辑配合工作
当前 cnmaps 官方样例数据使用 netCDF 文件。
如果你提供 sample 数据,建议:
- DEM 样例:
china-dem.nc - 气温样例:
china-temp.nc - 风场样例:
china-wind.nc
当前官方 cnmaps 对样例文件的变量名假设为:
lonlatdemtempuv
本仓库提供了一个检查脚本:
cnmaps-data-check或者:
python -m cnmaps_data.checker它会检查:
- manifest 是否存在且字段完整
- 数据集目录是否存在
- 行政区索引库是否存在
ADMINISTRATIVE表及列是否完整path指向的 GeoJSON 文件是否存在- GeoJSON 的顶层结构和 geometry type 是否符合要求
第三方开发者建议在发布前至少跑一遍。
如果命令行里暂时没有 cnmaps-data-check,也可以始终使用:
python -m cnmaps_data.checker /path/to/your-data-package请注意,当前这份文档描述的是 cnmaps_data_api_version = 1 的约定。
这意味着:
- SQLite 仍是官方推荐索引方式
- 行政区索引当前仍以
ADMINISTRATIVE表为核心 - provider 协议主要围绕文件系统路径解析
未来如果 cnmaps 支持新的索引后端或新的几何格式,建议通过升级 cnmaps_data_api_version 来演进,而不是直接破坏现有字段定义。