通过经纬度获取详细地址

如题,想要通过经纬度知道附近都有哪些地点,这个可以通过高德地图提供的地理逆地理编码API和shapely库来获取数据。不开心的是,其实高德地图都提供了合适的端口可以直接拿到数据,眼瞎没看到,哎~不过了解到其他的知识也是很开心的

申请key

要使用高德地图的开放平台,需要先申请key

应用管理->创建新应用->填写应用相关信息,包括应用名称、用途(限制了url链接的可使用范围)。

获取地图边界

如果是需要整个城市或者区的的边界,高德地图有API可以直接调用。如果是需要某块区域,可以使用高德地图的坐标拾取器,也非常方便。

在浏览器打开高德地图->选择底部的“开放平台”->进入开放平台后->选择”地图工具”->选择”坐标拾取器”,然后就可以在坐标上根据实际需求选择范围并获得对应的坐标。

比如有这样一张图需要获取边界,就可以大概对比着来点点点啦!

1
2
3
4
5
6
7
8
9
10
11
12
# 拿到的边界坐标值
origin_points = [(110.987984, 21.506764), (110.990366, 21.514026), (110.993027, 21.530474), (110.999378, 21.535085),
(110.998027, 21.540195), (111.01324, 21.542869), (111.025149, 21.543069), (111.026286, 21.535843),
(111.035492, 21.524246), (111.01309, 21.499532), (111.005751, 21.491027), (110.995752, 21.494741),
(110.987984, 21.506764)]

# 处理数据
origin_points = list(map(lambda x: (int(x[0] * 1000000), int(x[1] * 1000000)), origin_points))
max_long = max([a[0] for a in origin_points])
min_long = min([a[0] for a in origin_points])
max_lat = max([a[1] for a in origin_points])
min_lat = min([a[1] for a in origin_points])

效果如下:

获取符合条件的坐标点

  1. 因为要抓取的区域比较小,所以每个点之间的距离也尽可能的小,不过这样拿到的数据就会有很多重复的。关于这一块的计算是参考L同学_的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 获取所有可能的坐标点
# 纬度1度为111.3195km,经度1度是111.319x(cos 0)。每隔1k米取一个点,那么任意两个地点之间的最大距离为2km,
# 也就是正方形对角线的距离必须小于等于2。根据勾股定理得到x=1.414214km,有六位小数,所以取1414214
x = 100000 # 数据量太少,取个100米吧
t = round(111.319 * cos(21 / 180 * pi), 4) # 以电白区中心来计算经纬度,也就是经度1度对应的长度103.925239km
g = round((max_long - min_long) * t / x) # 该地区总纬度差可以划分为几个数据点
g1 = [round(min_long + i * x / t) for i in range(g)]
h = round((max_lat - min_lat) * 111.3195 / x)
h1 = [round(min_lat + i * x / t) for i in range(h)]

coords = []
while g1:
g11 = g1.pop()
for j in h1:
coords.append((g11, j))
  1. 因为纬度1°为111.3195km,经度每度为111.3195cos(纬度),遍历之后得到以下的数据(蓝色范围)。经过筛选之后,只有红色点的数据满足。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 筛选数据
poly = MultiPoint(origin_points).convex_hull
res = []
for p in coords:
if poly.contains(Point(p)):
res.append(p)
print(res)

# 画图
long = [a[0] for a in origin_points]
lat = [a[1] for a in origin_points]
plt.plot(long, lat) # 地区范围曲线
plt.scatter([i[0] for i in coords], [i[1] for i in coords]) # 分割后的散点图
plt.scatter([i[0] for i in res], [i[1] for i in res], edgecolors='red') # 符合条件的点
plt.show()

获取结果

根据高德地图提供的API构建请求链接,然后发起请求就可以啦。具体需求可以查看手册进行设置,比如只需要抓取中餐厅,就可以设置poitype="中餐厅",详见poi类型.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# -*- coding:utf-8 -*-
# Time: 2018/9/13 16:54
import json
from pprint import pprint

import os
import pandas as pd
import openpyxl
from pandas import ExcelWriter
import requests
from points import get_point

file = '高德地图坐标.xlsx'

def parse_info(data):
res = []
try:
if data['info'] == 'OK':
for item in data['regeocode']['pois']:
d = {
'name': item['name'],
'lon': parse_location(item['location'])[0],
'lat': parse_location(item['location'])[1],
'address': item['address'],
}
res.append(d)
save(res)
except Exception as e:
print(e)

def parse_location(s):
return s.split(',')

def run():
gd_url = 'https://restapi.amap.com/v3/geocode/regeo?output=json&location={long},{lat}&key={key}&radius={radius}&extensions=all'
key = '47939581b9d62ed52efa9e875c137a13'
radius = 100 # 每个坐标点搜索半径
for lon, lat in get_point():
print(lon, lat)
html = requests.get(gd_url.format(long=lon, lat=lat, radius=radius, key=key))
print(html.text)
data = json.loads(html.text, encoding='utf8')
parse_info(data)

def save(data):
columns = ['name', 'lon', 'lat', 'address']
df = pd.DataFrame(data, columns=columns)
exist = os.path.exists(file)

if not exist:
writer = ExcelWriter(file, engine='openpyxl')
df.to_excel(writer, sheet_name='Main', header=True, index=False, columns=columns)
writer.save()
else:
book = openpyxl.load_workbook(file)
writer = ExcelWriter(file, engine='openpyxl')
writer.book = book
row = book['Main'].max_row
writer.sheets = dict((ws.title, ws) for ws in book.worksheets)
df.to_excel(writer, sheet_name='Main', startrow=row, index=False, header=False, columns=columns)
writer.save()

if __name__ == '__main__':
run()