一、背景与需求
近期在做一个重庆市的区域数据可视化大屏项目,需要在前端(ECharts/Leaflet)中渲染省、市、区县、街道/乡镇、村/社区五级下钻地图。然而实际开发中遇到几个痛点:
1. **数据分散**:官方渠道通常只提供到区县级的 Shapefile,乡镇/村级边界难以获取;
2. **格式不一**:不同来源的数据坐标系各异(WGS-84、GCJ-02、甚至地方坐标系),字段命名混乱;
3. **层级断层**:缺少五级联动的 `parent_id` 关联关系,难以实现地图下钻。
因此,有必要从零整理一套**坐标系统一、字段规范、层级关联完整**的重庆市五级行政区划 GeoJSON 数据。
---
> 注:重庆作为直辖市,在行政区划代码体系中属于省级(代码前两位 `50`),市级层级与省级重合,因此在数据建模时需要注意层级压缩。
**主要数据来源:**
- 国家统计局《统计用区划代码和城乡划分代码》
- 重庆市民政局公开行政区划名录
- 天地图(国家地理信息公共服务平台)矢量切片
- 高德/百度地图 API 边界数据(辅助校验)
数据:
https://github.com/Hoosonc/chongqing-geojson/tree/main
## 三、技术处理流程
整体处理流程分为四步:
```
原始数据获取 → 坐标系统一 → 属性清洗与层级关联 → 导出标准 GeoJSON
```
### 3.1 环境准备
```bash
pip install geopandas shapely pyproj
```
### 3.2 核心处理代码
以下代码展示了如何将分散的 Shapefile 数据合并、清洗并建立五级层级关系:
```python
import geopandas as gpd
import pandas as pd
import json
from shapely.geometry import mapping
# 1. 读取原始 Shapefile(以区县级为例)
gdf = gpd.read_file('cq_district.shp', encoding='utf-8')
# 2. 统一坐标系为 WGS-84 (EPSG:4326)
gdf = gdf.to_crs(epsg=4326)
# 3. 字段标准化
column_map = {
'NAME': 'name',
'ADCODE': 'adcode',
'LEVEL': 'level', # province/city/district/street/village
'PARENT': 'parent_code'
}
gdf = gdf.rename(columns=column_map)
# 4. 根据行政区划代码补全层级关系
# 重庆行政区划代码规则:省2位+市2位+县2位+镇3位+村3位,共12位
def extract_level(code):
code = str(code).zfill(12)
if code[2:4] == '00' and code[4:6] == '00':
return 'province', code[:2]
elif code[4:6] == '00':
return 'city', code[:4]
elif code[6:9] == '000':
return 'district', code[:6]
elif code[9:12] == '000':
return 'street', code[:9]
else:
return 'village', code
# 应用到所有数据
gdf['level'], gdf['parent_code'] = zip(*gdf['adcode'].apply(extract_level))
# 5. 空间融合:将乡镇合并为区县(示例)
# 如果需要上级边界,可对下级数据进行 dissolve
district_gdf = gdf[gdf['level'] == 'street'].dissolve(
by='parent_code',
aggfunc={'name': 'first'}
).reset_index()
# 6. 导出标准 GeoJSON
# 确保符合 RFC 7946 规范
geojson_data = {
"type": "FeatureCollection",
"features": []
}
for _, row in gdf.iterrows():
feature = {
"type": "Feature",
"properties": {
"name": row['name'],
"adcode": row['adcode'],
"level": row['level'],
"parent_code": row['parent_code']
},
"geometry": mapping(row.geometry) if row.geometry else None
}
geojson_data["features"].append(feature)
# 保存
with open('chongqing_admin_5level.geojson', 'w', encoding='utf-8') as f:
json.dump(geojson_data, f, ensure_ascii=False, indent=2)
```
### 3.3 层级关联设计
为实现地图下钻,每个要素都需包含指向父级和子级的关联字段:
```python
# 建立父子关系表
relation_df = gdf[['adcode', 'name', 'level', 'parent_code']].copy()
# 校验:确保每个 parent_code 都有对应的记录
orphans = relation_df[~relation_df['parent_code'].isin(relation_df['adcode'])]
print(f"存在 {len(orphans)} 条孤儿记录,需人工核查:")
print(orphans[['adcode', 'name', 'level']])
```
---
## 四、数据结构说明
最终生成的 GeoJSON 中,每个 Feature 的属性字段如下:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `name` | string | 行政区划名称,如"渝中区" |
| `adcode` | string | 12位行政区划代码 |
| `level` | string | 层级:`province`/`city`/`district`/`street`/`village` |
| `parent_code` | string | 父级行政区划代码 |
| `center` | array | 区域中心点坐标 `[lng, lat]` |
**坐标系**:统一采用 `EPSG:4326`(WGS-84),可直接用于 Leaflet、Mapbox、ECharts 等标准 Web 地图引擎。
---
## 五、可视化加载示例
### 5.1 ECharts 地图下钻
```html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px;"></div>
<script>
const chart = echarts.init(document.getElementById('map'));
// 加载重庆市级边界
fetch('./chongqing_5level.geojson')
.then(res => res.json())
.then(geoJson => {
echarts.registerMap('CQ', geoJson);
chart.setOption({
series: [{
type: 'map',
map: 'CQ',
roam: true,
zoom: 1.2,
label: { show: true },
itemStyle: {
areaColor: '#e7e8ea',
borderColor: '#404a59'
}
}]
});
});
</script>
</body>
</html

427

被折叠的 条评论
为什么被折叠?



