使用 geopy 处理地址和坐标数据

你一定知道地物的地址与其大地坐标之间存在对应关系。以往当我需要某个城市或标志物的坐标时,都是通过电子地图服务或者维基百科查询,这样的效率很低。geopy 是 Python 处理地理数据的包,它封装了大量的公共地图服务,用它来处理地址与坐标的关系非常方便。同时,其距离计算功能也比较强大。

本文将对 geopy 的主要功能进行介绍。

安装

geopy 项目基于 MIT 协议开源,代码托管在 Github 上。当前的 geopy 支持 Python 2.7 以及 Python 3.4 以上的版本。但根据开发计划,即将发布的 geopy 2.0 将只支持 Python 3。你可以使用 pip 来方便地安装 geopy:

1
pip install geopy

地址与坐标计算

geopy 支持众多的电子地图服务,例如著名的 Google、Bing、百度、Here 等,你可以通过如下的代码查询其支持的所有服务商:

1
2
>>> import geopy
>>> [s for s in dir(geopy.geocoders) if s[0].isupper()]

这些服务商被封装为对应的类,并且提供统一的方法供使用者调用。例如下面提到的地址和坐标查询的 geocodereverse 函数。

地址查询

使用 geopy 查询地址前需要首先选择一个地图服务,绝大多数的服务需要一个密钥(api_key)。你需要到其开发者页面注册和申请,例如百度。免费的密钥通常会对使用频率和次数做限制。出于演示目的,我们以不需要密钥的 OpenStreetMap 服务作为例子:

1
2
3
>>> from geopy.geocoders import Nominatim
>>> geolocater = Nominatim(user_agent='demo_of_gnss_help')
>>> location = geolocater.geocode('天安门, 北京市')

这里的 Nominatim 类为对 OpenStreetMap 服务的实现和封装。其提供的 geocode 方法用于将关键字在地图上查询,并返回一个 Location 对象。你可以用如下的方式获取结果:

1
2
3
4
>>> location.address
'天安门, 1, 西长安街, 崇文, 北京市, 东城区, 北京市, 100010, China 中国'
>>> location.latitude, location.longitude, location.altitude
(39.907359, 116.391263017795, 0.0)

坐标查询

同样的,你可以通过 reverse 方法输入坐标来查询地址:

1
2
3
>>> location = geolocater.reverse('45.768189, 126.6212835')
>>> location.address
'索菲亚大教堂, 透笼街, 道里区, 哈尔滨市, 黑龙江省, 150000, 中国'

reverse 方法接收经纬度字符串作为输入,纬度在前,经度在后。该方法同样返回一个 Location 对象。

距离计算

geopy 的距离计算功能包含在 distance 中。其计算的距离有两种:大圆距离与大地线距离。其区别在于:

  • 大圆算法将地球简化为一个圆球,其计算的距离是球面上过两点的大圆距离;
  • 大地线使用目前国际通用的方法,用旋转椭球面表示地球,其计算的是两点在椭球面上的最短距离。

如果你对地球物理学有过了解,一定知道大地线算法更精确。实际上,geopy 默认的距离计算方法就是大地线。你可以通过如下方式计算哈尔滨到北京的大地线距离:

1
2
3
4
>>> from geopy import distance
>>> dist = distance.distance((45.768189, 126.6212835), (39.907359, 116.3912630))
>>> dist.kilometers
1058.4645051382365

该方法返回一个 Distance 对象,其中包含单位转换方法,方便获取以千米、米或者英里为单位的距离。你也可以用等效的显式方法:

1
2
3
>>> dist = distance.geodesic((45.768189, 126.6212835), (39.907359, 116.3912630))
>>> dist.meters
1058464.5051382366

但大地线的劣势在于计算速度太慢,如果你可以容忍一定的误差(0.5% 以内),完全可以采用大圆算法替代。根据我的测试,大圆距离计算时间大约只有前者的 1/20。大圆距离的使用方式如下:

1
2
3
4
5
>>> gc = distance.great_circle((45.768189, 126.6212835), (39.907359, 116.3912630))
>>> gc.km
1057.0946827378225
>>> print(f'{(dist.km - gc.km) / dist.km:%}')
0.129416%

该方法同样返回 Distance 对象,并且可以看到,哈尔滨到北京的大圆与大地线距离之间有 0.13% 的差异。