将一个城市的地图按照每1平方公里进行拆分为若干个区域(地图使用高德)
如图:
核心代码(Python 3.5):
map_zoning.py
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import json import math import os import time from amap import Map class MapZoning: def __init__(self, city, distance=None, map_service=None): self.city = city self.distance = distance if distance else 1 # 单位km,默认0.5km self.earth_radius = 40075.04 / (2 * math.pi) self.lat_unit = self.get_lat_unit(self.distance) self.map_service = map_service if map_service else Map(os.environ.get('MAP_KEY')) self.location_validated = {} # key:[经度,维度] value: 1验证成功,2超出范围 def get_lat_unit(self, distance): """ 获取一定距离的维度单位 :param distance: :return: """ return distance * 360 / (2 * math.pi) / self.earth_radius def get_lng_unit(self, lat, distance): """ 获取一定距离的经度单位 :param lat: :param distance: :return: """ return distance * 360 / (2 * math.pi) / math.cos(math.radians(lat)) / self.earth_radius def _get_area_points(self, quadrant, location): """ 获取区域的点 :param int quadrant: :param list location: :return list : 矩形四个点,顺序:左上、右上、右下、左下 """ if quadrant == 1: left_lng = float(location[0]) # 经度 bottom_lat = float(location[1]) # 维度 top_lat = round(bottom_lat + self.lat_unit, 6) right_lng = round(left_lng + self.get_lng_unit(float(top_lat), self.distance), 6) elif quadrant == 2: right_lng = float(location[0]) bottom_lat = float(location[1]) top_lat = round(bottom_lat + self.get_lat_unit(self.distance), 6) left_lng = round(right_lng - self.get_lng_unit(top_lat, self.distance), 6) elif quadrant == 3: right_lng = float(location[0]) top_lat = float(location[1]) left_lng = round(right_lng - self.get_lng_unit(top_lat, self.distance), 6) bottom_lat = round(top_lat - self.get_lat_unit(self.distance), 6) elif quadrant == 4: left_lng = float(location[0]) top_lat = float(location[1]) right_lng = round(left_lng + self.get_lng_unit(float(top_lat), self.distance), 6) bottom_lat = round(top_lat - self.lat_unit, 6) else: return None return [[left_lng, top_lat], [right_lng, top_lat], [right_lng, bottom_lat], [left_lng, bottom_lat]] def get_area_points_x(self, quadrant, res_points, points, i_temp): if i_temp > 60: return if quadrant == 1: location = points[2] elif quadrant == 2: location = points[3] elif quadrant == 3: location = points[0] elif quadrant == 4: location = points[1] else: return tmp_pos = self._get_area_points(quadrant, location) if self.validate_points(tmp_pos): res_points.append(tmp_pos) i_temp += 1 self.get_area_points_x(quadrant, res_points, tmp_pos, i_temp) def get_area_points_y(self, quadrant, res_points, points, i_temp): if i_temp > 60: return if quadrant == 1: location = points[0] elif quadrant == 2: location = points[1] elif quadrant == 3: location = points[2] elif quadrant == 4: location = points[3] else: return tmp_pos = self._get_area_points(quadrant, location) if self.validate_points(tmp_pos): res_points.append(tmp_pos) i_temp += 1 self.get_area_points_x(quadrant, res_points, tmp_pos, 1) self.get_area_points_y(quadrant, res_points, tmp_pos, i_temp) def get_all_area_points(self, quadrant, res_points, location): tmp_pos = self._get_area_points(quadrant, location) res_points.append(tmp_pos) self.get_area_points_x(quadrant, res_points, tmp_pos, 1) self.get_area_points_y(quadrant, res_points, tmp_pos, 1) def validate_points(self, points): b_result = False for location in points: if location[0] < 113.74181 or location[1] < 22.438768 or location[1] > 22.887492 or location[0] > 114.649706: continue v_key = ','.join([str(x) for x in location]) v_value = self.location_validated.get(v_key) if not v_value: res = self.map_service.get_get_address(location) address_component = res.get('regeocode').get('addressComponent') if res and res.get('regeocode') else {} if not address_component or address_component.get('citycode') != '0755' or address_component.get( 'seaArea'): self.location_validated[v_key] = 2 else: b_result = True self.location_validated[v_key] = 1 elif v_value == 1: b_result = True return b_result def zoning(self): print(time.time()) res_points = [] location = [114.057868, 22.543099] # self.map_service.get_geo_code(self.city).split(',') self.get_all_area_points(1, res_points, location) self.get_all_area_points(2, res_points, location) self.get_all_area_points(3, res_points, location) self.get_all_area_points(4, res_points, location) with open('tmp/points.js', 'w', encoding='utf-8') as _file: _file.write('var points = ' + json.dumps(res_points) + ';') print(time.time()) # points = [['113.980092', '22.475719'], ['113.984953', '22.475719'], ['113.984953', '22.471227'], # ['113.980092', '22.471227']] # print(self.validate_points(points)) # print(self.map_service.get_get_address(['113.980092', '22.475719'])) return res_points
amap.py
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import logging import requests class Map: def __init__(self, key): if not key: raise Exception('初始map服务错误无效Key') self.key = key def get_geo_code(self, address): """ 获取地理位置编码 :param address: :return: """ api_geocode = 'http://restapi.amap.com/v3/geocode/geo' try: response = requests.get(api_geocode, {'key': self.key, 'address': address, 'output': 'JSON'}) if response.status_code != 200: return None res = response.json() if not res: return None geocodes = res.get('geocodes') if not geocodes: return None location = geocodes[0].get('location') if not location: return None return location except Exception as ex: logging.error('获取地址位置编码错误:' + str(ex)) return None def get_get_address(self, location): api = 'http://restapi.amap.com/v3/geocode/regeo' try: response = requests.get(api, {'key': self.key, 'location': ','.join([str(x) for x in location]), 'output': 'JSON'}) if response.status_code != 200: return None res = response.json() if not res: return None return res except Exception as ex: logging.error('获取地址位置错误:' + str(ex)) return None
app.py
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import logging from os.path import join, dirname from dotenv import load_dotenv from map_zoning import MapZoning dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) if __name__ == '__main__': try: MapZoning('深圳市').zoning() except Exception as ex: logging.error(ex)
.env
# application APP_NAME="split_map" APP_RUNTIME="dev" APP_LOG_LEVEL="INFO" APP_LOG_HANDLER="console" # map MAP_KEY="amap_web_api_key"