使用Python以面向对象的方式调用高德地图API(一)

 最近的项目需要做一些地址编码、转换、测距的工作,考虑到各大地图都有API,就想着直接调用了,本文主要针对高德地图的API。

 这里先插一些基础知识,就是有关地图坐标系的问题,考虑到已经有很多文章提到了,这里只给出其中一篇的链接:

互联网地图坐标系简介及转换(地理坐标系)

 但高德地图API已经具有转换的接口,所以直接调用也可以。

 下面入正题,python作为一种脚本语言,可以以很简单的面向过程的方式完成调用web服务API的工作,可是,考虑到代码的复用性,已经后期我们的系统不断丰满,我还是提倡以类的方式去实现调用的功能。

 首先,是web服务api调用的一个比较有用的拼接参数的方法:

def join_parameters(parameters_dict, symbol_str='&'):
    assert isinstance(parameters_dict, dict)
    return symbol_str.join(key + '=' + value for key, value in parameters_dict.items())

 该方法主要是调用url常用的‘&’符号把参数字典里的key=value连接起来,该方法抽象出来后,编写后面的url路径会比较方便。

接着,便是基本的caller类了,负责调用api:

class AmapAPICaller:
    def __init__(self, key):
        self.key = key
        self.url = {
            'geo': r'https://restapi.amap.com/v3/geocode/geo',
            'convert': r'https://restapi.amap.com/v3/assistant/coordinate/convert',
            'distance': r'https://restapi.amap.com/v3/distance'
        }
        self.necessary = {
            'geo': ['address'],
            'convert': ['locations'],
            'distance': ['origins', 'destination']
        }
        self.safe_str = "/:=&?#+!$,;'@()*[]"

 我们先来看构造函数,其中key便是我们使用api时候申请的key,url字典和necessary字典的妙用可以在下一个方法看到,而safe_str主要是把URL编码的时候剔除的字符。

    def general_call(self, code, **parameters):
        assert isinstance(code, str)
        assert code in self.url and code in self.necessary
        for parameter_name in self.necessary[code]:
            assert parameter_name in parameters
        if 'key' not in parameters:
            parameters['key'] = self.key
        request_str = self.url[code] + '?' + join_parameters(parameters)
        data = urllib.request.urlopen(urllib.request.quote(request_str, safe=self.safe_str)).read()
        return json.loads(data.decode())

这是一个通用调用方法,code参数是指定需要调用的命令,我们可以看到当我们指定一个code的时候,代码会检查用户提供的可变参数parameters里面是否包含necessary字典里面列名的该命令必须要附带的参数,这样起到了在远程调用前检查的作用。以geo(地址编码)为例,当我们调用函数的时候这样写:

print(api_caller.general_call(code='geo', address='云浮市云城区云城街道新平路金山雅苑小区'), city='云浮市')

传入函数中的address和city就会是字典的形式:

{'address': '云浮市云城区云城街道新平路金山雅苑小区', 'city': '云浮市'}

这时候添加key再调用join_parameters方法后,便会拼接成:

key=yourkey&address=云浮市云城区云城街道新平路金山雅苑小区&city=云浮市

而后面request_str的生成也使用了url字典,以实现java里面case when的功能,非常简洁。最后把request_str编码后使用request发出去,再用json对返回的结果进行解包,注意返回的结果需要先decode才可以使用json.loads。而当我们需要新增调用命令的时候,需要把构造函数里面的url和necessary两个字典补充,后续程序庞大了以后可以写在配置文件里。

    def call_convert_lite(self, longitude, latitude, coordsys='baidu'):
        parameters = {
            'key': self.key,
            'locations': longitude + ',' + latitude,
            'coordsys': coordsys
        }
        request_str = self.url['convert'] + '?' + join_parameters(parameters)
        data = urllib.request.urlopen(urllib.request.quote(request_str, safe=self.safe_str)).read()
        return json.loads(data.decode())['locations'].split(',')

 这是把其中的一个命令调用抽出来做个性化的,在诸多的api里面,总有一些常用的我们希望能够简化参数的输入,例如这是其中一个对转换坐标系做个性化的,把参数经度,纬度独立出来,而在函数里面完成了经纬度用‘,’分割,再把所有参数连起来的过程。以下是另外两个个性化方法,分别是批量调用转换坐标和批量测距的:

    def call_convert_batch(self, locations, coordsys='baidu'):
        assert len(locations) <= 40
        parameters = {
            'key': self.key,
            'locations': '|'.join([','.join(location) for location in locations]),
            'coordsys': coordsys
        }
        request_str = self.url['convert'] + '?' + join_parameters(parameters)
        data = urllib.request.urlopen(urllib.request.quote(request_str, safe=self.safe_str)).read()
        return [location.split(',') for location in json.loads(data.decode())['locations'].split(';')]

    def call_distance_batch(self, src_locations, dst_location, type='0'):
        assert len(src_locations) <= 100
        parameters = {
            'key': self.key,
            'origins': '|'.join([','.join(location) for location in src_locations]),
            'destination': ','.join(dst_location),
            'type': type
        }
        request_str = self.url['distance'] + '?' + join_parameters(parameters)
        data = urllib.request.urlopen(urllib.request.quote(request_str, safe=self.safe_str)).read()
        return [result['distance'] for result in json.loads(data.decode())['results']]

其中的location都以坐标对的方式存入一个list,坐标对的实现方式可以用tuple和list都可以。

大家也可以针对自己的需要,编写其他常用api的个性方法,caller类的介绍到此为止,后面我们再谈谈怎样同一文件的读写和使用controller来调用caller。

你可能感兴趣的:(Python心得)