重庆市五级(包含省、市、区县、街道、乡村)行政区划 GeoJSON 数据的采集与处理实践

一、背景与需求

近期在做一个重庆市的区域数据可视化大屏项目,需要在前端(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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值