前言

当你从某处获取了一个经纬度数据集, 想要看看能不能从中提取到有价值的数据, 有时候需要找到一个方法采集额外的数据, 高程就是地理数据的一种. 本文主要介绍利用 Google Maps API 来采集指定经纬度的高程数据.

请注意, 在阅读本文之前请确保你能够访问 Google, 包括你的代码运行时, 你的计算机也必须能够访问 Google.

什么是 Google Maps API

首先介绍一下什么是 API: Application Programming Interface, 应用程序接口.

你可以把 API 理解成有人给你提供了一个特殊的程序, 你向它提交一定格式的数据, 它给你返回一定的信息. Google Maps 就提供了许多有用的 API, 你可以参考 Google Maps API | Google Developers.

本文此次需要使用的是 Google Maps Elevation API, 文档十分清晰, 而且有助于你理解什么是 API.

向 Google 申请 API 密钥

在上文提供的链接中你可以很快找到 “激活 API 和获取 API 密钥” 这一项, 点击 “获取密钥” 按钮, 在弹出的窗口中选择 “Create a new project”, 输入名称并点击 Next 按钮, 稍等一会儿, 你就会收到一个 Key.

Your Api Key

Your Api Key

请务必保存好你的 Key, 如果这个 Key 被滥用那么你的账户有一定的可能被 Ban, 如果你忘记了你的 Key, 可以进入 API Console 进行管理 (是的, 就是图里的那个).

接下来如果你有兴趣的话可以继续研究文档, 本文就不深入了. 下面就开始编程了.

使用 googlemaps 包来保护你的键盘

显然, 你并不需要像 Google Maps API 的文档中描述的那样, 把数据转换成 HTTP 请求, 然后解析获得的数据, 这一切都已经有人帮你完成了, 你只需要使用 googlemaps - Github 包来处理你的数据就行了.

首先使用安装 googlemaps, 打开 cmd 或者其他什么 shell:

1
pip install -U googlemaps

然后就可以直接开始码代码啦! 如果想知道 googlemaps 包的更多用法, 可以到前文中 googlemaps 的 Github 页面翻一翻文档和 demo. 很快你就会知道如何使用了.

开工!

数据预处理

假设你通过某种方法, 现在已经得到了一个列表, 列表中的每一项都是一个点的坐标 (包括经纬度), 就假定这个列表叫做 points_list 好了, 由于大部分时候都是一次处理许多个点 (否则你完全可以使用手动方法来解决这个问题), 而 Google Maps API 要求一次最多只能请求 512 个点 (不是非常确定, 但肯定不少于 512 个点), 因此需要对数据集进行拆分, numpy 提供了一个 array_split() 函数来划分数据集, 参考代码如下:

1
2
request_num = int(np.floor(len(points_list)/maxnum) + 1)   # maxnum==512
npoints = np.array_split(points_list, request_num) # 以 request_num 为步长划分数据集

请求数据

然后使用你的 API (代码中的 api_key) 初始化 googlemaps client:

1
gmaps = googlemaps.Client(key=api_key)

现在, 你可以使用如下语句来请求数据了:

1
2
3
elevations = []
for loc in npoints:
elevations.append(gmaps.elevation(locations=loc.tolist()))

输出到文件

返回的对象包含了点的经纬度, 高程, 分辨率等信息. 将其转换成能输出的格式, 并保存到文件中:

1
2
3
4
5
6
7
8
9
10
with open('elevation_return.txt', 'w', encoding='utf-8') as f:
for loclist in elevations:
for subloc in loclist:
# 如果你打算输出 csv 文件, 这里使用逗号作为分隔符可能是更好的选择
outputstr = "{:.5f} {:.5f} {:.5f} {:.5f}\n".format(
subloc['location']['lng'],
subloc['location']['lat'],
subloc['elevation'],
subloc['resolution'])
f.writelines(outputstr)

如果你对上面这段代码不是很理解, 建议你深入研究一下 Python 格式化字符串.

至此, 你已经成功获取到了点的高程, 下一小节给出完整参考代码.

完整参考代码

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
import googlemaps
import numpy as np
import csv
import re
import sys

api_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX'
filename = 'elevation.csv'

def read_points():
data = []
with open(filename, 'r', encoding='utf-8') as f:
csv_reader = csv.reader(f)
for line in csv_reader:
for p in line:
data.append(tuple(map(float, p.split(', '))))
return data

def request_elevation(points_list, maxnum=512):
request_num = int(np.floor(len(points_list)/maxnum) + 1)
npoints = np.array_split(points_list, request_num)
gmaps = googlemaps.Client(key=api_key)
if request_num > 2500:
print("Error: Request number exceed! Change grid or region.")
return [gmaps.elevation(locations=loc.tolist()) for loc in npoints]

def main():
points = read_points()
elevations = request_elevation(points)
with open('elevation_return.txt', 'w', encoding='utf-8') as f:
for loclist in elevations:
for subloc in loclist:
outputstr = "{:.5f} {:.5f} {:.5f} {:.5f}\n".format(
subloc['location']['lng'],
subloc['location']['lat'],
subloc['elevation'],
subloc['resolution'])
f.writelines(outputstr)


if __name__ =='__main__':
main()