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

    技术2022-07-10  121

    文章目录

    1.背景2.行政区域数据获取3.获取GeoJson数据

    1.背景

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

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

    2.行政区域数据获取

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

    接口获取的数据是按照行政等级多层嵌套,为了方便后续操作,将数据转化为了List。 以下代码将行政区划转化为List并添加了id和parent_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)
    Processed: 0.059, SQL: 9