欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

省市区三级行政区数据获取和GeoJson地图下载

程序员文章站 2022-07-18 11:50:16
文章目录1.背景2.行政区域数据获取3.获取GeoJson数据1.背景项目中用到省市区三级的行政区划的选择,在网上找到的数据与最新的行政区域划分不一致,也难以确认数据的完成性。基于echarts完成数据地区分布图时,需要提供地区对应的geoJson格式地图。2.行政区域数据获取高德开放平台提供了丰富数据API,其中行政区域信息可通过以下接口:https://restapi.amap.com/v3/config/district?keywords=中国&subdistrict=3&...

1.背景

项目中用到省市区三级的行政区划的选择,在网上找到的数据与最新的行政区域划分不一致,也难以确认数据的完成性。

基于echarts完成数据地区分布图时,需要提供地区对应的geoJson格式地图。

2.行政区域数据获取

高德开放平台提供了丰富数据API,其中行政区域信息可通过以下接口:
https://restapi.amap.com/v3/config/district?keywords=中国&subdistrict=3&key=5b138cc729f37d29702ff904ca9cedeb

接口获取的数据是按照行政等级多层嵌套,为了方便后续操作,将数据转化为了List。
以下代码将行政区划转化为List并添加了idparent_id,同时保存为json数组(area_dict.json)和MySQL数据库脚本(area_dict.sql)。

#-*-coding:UTF-8-*-
"""
Author: Gray Snail
Date: 2020-06-30

最新行政区划获取
基于高德地图API获取数据
https://restapi.amap.com/v3/config/district?keywords=中国&subdistrict=3&key=5b138cc729f37d29702ff904ca9cedeb

"""
import json
import requests

def parse_district(districtObj : dict, idx=1, parent_id=0):
    res = []
    if 'name' in districtObj.keys():
        if districtObj['level'] == 'street':
            return res
        
        lng, lat = districtCenter(districtObj['center'])
        level = districtLevel(districtObj['level'])
        citycode = districtObj['citycode'] if isinstance(districtObj['citycode'], str) else ''
        
        # {"citycode":"0379","adcode":"410300","name":"洛阳市","center":"112.434468,34.663041","level":"city"}
        # idx, districtObj['adcode'], districtObj['name'], level, citycode, lng, lat, parent_id
        item = {
            'id'        : idx,
            'adcode'    : districtObj['adcode'],
            'name'      : districtObj['name'],
            'level'     : level,
            'citycode'  : citycode,
            'lng'       : lng,
            'lat'       : lat,
            'parent_id' : parent_id
        }
        res.append(item)
        parent_id = idx
        idx = idx + 1

    if isinstance(districtObj.get('districts'), list) and len(districtObj['districts']) > 0:
        for subitem in districtObj['districts']:
            subs = parse_district(subitem, idx, parent_id)
            res += subs
            idx += len(subs)
    return res

def districtLevel(levelStr):
    map_val = {
        'country': 0,
        'province': 1,
        'city': 2,
        'district': 3
    }
    return map_val[levelStr]

def districtCenter(center):
    items = center.split(',')
    return float(items[0]), float(items[1])

# 结果保存为json数组
def saveJson(data):
    with open('area_dict.json', 'w', encoding='utf-8') as fp:
        json.dump(data, fp, ensure_ascii=False, indent=4)
    print('Save json file: area_dict.json')

# 保存为SQL脚本
def saveSqlFile(data, includeCreate=True):
    # +--------------+-------------+------+-----+---------+----------------+
    # | Field        | Type        | Null | Key | Default | Extra          |
    # +--------------+-------------+------+-----+---------+----------------+
    # | area_id      | int(11)     | NO   | PRI | NULL    | auto_increment |
    # | area_code    | char(6)     | NO   | MUL | NULL    |                |
    # | area_name    | varchar(20) | NO   | MUL | NULL    |                |
    # | level        | tinyint(1)  | NO   | MUL | 0       |                |
    # | city_code    | char(4)     | YES  |     | NULL    |                |
    # | longitudinal | int(11)     | YES  |     | 0       |                |
    # | lateral      | int(11)     | YES  |     | 0       |                |
    # | parent_id    | int(11)     | NO   | MUL | -1      |                |
    # +--------------+-------------+------+-----+---------+----------------+
    createCode = """
CREATE TABLE `area_dict` (
    `area_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '地区Id',
    `area_code` char(6) NOT NULL COMMENT '地区编码',
    `area_name` varchar(20) NOT NULL COMMENT '地区名',
    `level` tinyint(1) NOT NULL DEFAULT '0' COMMENT '地区级别(1:省份province,2:市city,3:区县district,4:街道street)',
    `city_code` char(4) DEFAULT NULL COMMENT '城市编码',
    `lng` int(11) DEFAULT '0' COMMENT '城市中心经度',
    `lat` int(11) DEFAULT '0' COMMENT '城市中心纬度',
    `parent_id` int(11) NOT NULL DEFAULT '-1' COMMENT '地区父节点',
    PRIMARY KEY (`area_id`),
    KEY `areaCode` (`area_code`),
    KEY `parentId` (`parent_id`),
    KEY `level` (`level`),
    KEY `areaName` (`area_name`)
) ENGINE=InnoDB AUTO_INCREMENT=3261 DEFAULT CHARSET=utf8 COMMENT='地区码表';
"""
    with open('area_dict.sql', 'w', encoding='utf-8') as fp:
        if includeCreate:
            fp.write(createCode)
        for item in data:
            item['lng'] = int(item['lng'] * 1e6)
            item['lat'] = int(item['lat'] * 1e6)
            sql = "INSERT INTO area_dict(`area_id`,`area_code`,`area_name`,`level`,`city_code`,`lng`,`lat`,`parent_id`) " + \
                "VALUES({id},'{adcode}','{name}',{level},'{citycode}',{lng},{lat},{parent_id});\n".format(**item)

            fp.write(sql)
            
    print('Save sql file: area_dict.sql')

if __name__ == "__main__":
    url = 'https://restapi.amap.com/v3/config/district?keywords=中国&subdistrict=3&key=5b138cc729f37d29702ff904ca9cedeb'

    response = requests.get(url)
    if response.ok and response.status_code == 200:
        data = response.json()
        data = parse_district(data)
        print('Download data successful, total:{0}!'.format(len(data)))
        saveJson(data)
        saveSqlFile(data)
    else:
        print('Request error!')

3.获取GeoJson数据

数据来源:基于阿里云datav,数据文件以地区编码命名。
根据行政区域数据中保存的area_dict.json自动下载对应GeoJson文件。

行政区划的更新,两个平台的数据可能存在差异,即同一地区有着不同的地区编码,导致对应地区的地图无法下载。代码中errorCodes记录了未成功下载的地区编码。2020.07.01,未下载成功的不到30条。

#-*-coding:UTF-8-*-
"""
Author: Gray Snail
Date: 2020-06-30

GeoJson地图数据下载
基于阿里云datav
http://datav.aliyun.com/tools/atlas
"""
import requests
import json
import os

def loadDistrict(filename):
    # {"citycode":"0379","adcode":"410300","name":"洛阳市","center":"112.434468,34.663041","level":"city"}
    data = []
    with open(filename, 'r', encoding='utf-8') as fp:
        data = json.load(fp)
    return data

def saveGeoJson(areaCode, force=False):
    saveName = 'geo/{0}.json'.format(areaCode)
    if not force and os.path.isfile(saveName):
        return None 

    baseUrl = 'https://geo.datav.aliyun.com/areas_v2/bound/{0}_full.json'
    baseUrl2 = 'https://geo.datav.aliyun.com/areas_v2/bound/{0}.json'

    if areaCode[-2:] == '00':
        url = baseUrl.format(areaCode)
    else:
        url = baseUrl2.format(areaCode)
    print(url)
    
    response = requests.get(url)
    if response.ok and response.status_code == 200:
        res_json = response.json()
        with open(saveName, 'w', encoding='utf-8') as fp:
            json.dump(res_json, fp, ensure_ascii=False)
    else:
        return areaCode

if __name__ == "__main__":
    districts = loadDistrict('area_dict.json')

    errorCodes = []
    for district in districts:
        code = saveGeoJson(district['adcode'])
        if not code is None:
            errorCodes.append(code)
    print(errorCodes)

本文地址:https://blog.csdn.net/u010942902/article/details/107055995