通过高德地图 Web 服务 API 对咖啡店地址进行地理编码
打开后续各种 LBS 功能大门的钥匙是首先将咖啡店的物理地址转化为地图上的经纬度格式,这个过程叫做地理编码(Geocode)。我们可以通过多种方式来实现地理编码,高德地图、百度地图、Google地图这些地图服务都提供了开放的 API,由于学院君之前用过一次高德地图的 API,所以这里以高德地图为例进行演示,其他地图服务实现思路完全一样。
在上一篇教程中,我们对提交过来的咖啡店数据进行了验证,我们将在其基础上进行本教程的构建。所有的咖啡店都包含详细的地址信息,我们将通过这些地址信息来定位经纬度,然后再在经纬度的基础上实现距离计算、附近的咖啡店、在地图上绘图等功能。
在开始之前,需要先概览下高德地图 Web 服务 API 文档:https://lbs.amap.com/api/webservice/summary/,我们将基于该文档中提供的地理/逆地理编码功能来实现地理编码。
第一步:获取高德地图 Web 服务 API Key
调用第三方服务提供的 API,首先自然是要申请 API Key,我们可以遵循高德地图提供的获取Key这篇文档在控制台创建一个新应用,然后点击「添加 Key」 按钮,服务平台选择「Web服务」来添加一个 Key,添加成功后,我们就拥有了可以访问高德地图 Web 服务 API 的 Key 了。
第二步:添加 API Key 到配置文件
获取到 API Key 之后,我们将其添加到 Laravel 应用的配置文件中,方便后续调用,首先将 API Key 存储到 .env
里面:
GAODE_MAPS_WS_API_KEY={YOUR_API_KEY}
然后在 config/services.php
中引用:
'gaode' => [
'ws_api_key' => env('GAODE_MAPS_WS_API_KEY'),
]
这样,我们就可以在编写代码时通过 config('services.gaode.ws_api_key')
获取 API Key 了。
第三步:构建高德地图工具类
接下来,我们来创建一个工具类 GaodeMaps
用于调用高德地图 API 实现相关功能。首先创建一个 app\Utilities
目录,然后在该目录下创建 GaodeMaps
类文件,使用 PHPStorm 的话会自动生成如下代码:
<?php
namespace App\Utilities;
class GaodeMaps
{
}
接下来我们就要编写地理编码实现代码了。
第四步:安装 Guzzle HTTP 扩展包
由于我们是在 Laravel 后端发起 API 请求,所以在调用高德地图 API 之前需要安装相应的网络扩展包发送 HTTP 请求,这里我们使用 Guzzle HTTP 扩展包,如果你已经安装过这个扩展包,可以跳过本步骤,否则可以通过 Composer 进行安装:
composer require guzzlehttp/guzzle
第五步:添加地理编码方法到工具类
做好以上准备工作后,接下来开始正式编写将咖啡店地址信息转化为经纬度的地理编码处理逻辑,在开始编写代码之前建议翻阅下高德地图提供的地理编码文档,以便熟悉相关的 API 方法和返回字段,我们在上面创建的 GaodeMaps
类中定义一个静态方法 geocodeAddress
来实现地理编码:
/**
* 通过真实地址获取对应的经纬度
* @param $address
* @param $city
* @param $state
* @param $zip
* @return mixed
*/
public static function geocodeAddress($address, $city, $state)
{
// 省、市、区、详细地址
$address = urlencode($state . $city . $address);
// Web 服务 API Key
$apiKey = config('services.gaode.ws_api_key');
// 构建地理编码 API 请求 URL,默认返回 JSON 格式响应
$url = 'https://restapi.amap.com/v3/geocode/geo?address=' . $address . '&key=' . $apiKey;
// 创建 Guzzle HTTP 客户端发起请求
$client = new Client();
// 发送请求并获取响应数据
$geocodeResponse = $client->get($url)->getBody();
$geocodeData = json_decode($geocodeResponse);
// 初始化地理编码位置
$coordinates['lat'] = null;
$coordinates['lng'] = null;
// 如果响应数据不为空则解析出经纬度
if (!empty($geocodeData)
&& $geocodeData->status // 0 表示失败,1 表示成功
&& isset($geocodeData->geocodes)
&& isset($geocodeData->geocodes[0])) {
list($latitude, $longitude) = explode(',', $geocodeData->geocodes[0]->location);
$coordinates['lat'] = $latitude; // 经度
$coordinates['lng'] = $longitude; // 纬度
}
// 返回地理编码位置数据
return $coordinates;
}
在上述代码中,我们使用了 Guzzle HTTP Client 向高德地图地理编码 API 发送请求并获取响应,该 API 默认返回数据格式是 JSON,当然你也可以指定其他返回格式(参考文档),最后从响应数据中解析出经纬度信息并将其返回以供调用方使用。
我们可以在 routes/web.php
定义一个简单的路由来测试这段代码:
Route::get('geocode', function () {
return \App\Utilities\GaodeMaps::geocodeAddress('天城路1号', '杭州', '浙江');
});
在浏览器中访问 http://roast.test/geocode
,页面显示如下,则表示调用地理编码 API 成功:
第六步:在新增咖啡店时保存经纬度数据
最后,我们要在咖啡店数据保存到数据库之前设置相应的经纬度数据,打开 app/Http/Controllers/API/CafesController.php
控制器,修改 postNewCafe
方法如下:
public function postNewCafe(StoreCafeRequest $request)
{
$cafe = new Cafe();
$cafe->name = $request->input('name');
$cafe->address = $request->input('address');
$cafe->city = $request->input('city');
$cafe->state = $request->input('state');
$cafe->zip = $request->input('zip');
$coordinates = GaodeMaps::geocodeAddress($cafe->address, $cafe->city, $cafe->state);
$cafe->latitude = $coordinates['lat'];
$cafe->longitude = $coordinates['lng'];
$cafe->save();
return response()->json($cafe, 201);
}
在上述代码中,我们在获取到咖啡店地址信息后,调用前面定义的 GaodeMaps::geocodeAddress
方法进行地理编码,并从返回数据中获取经纬度信息设置相应的 $cafe
模型字段,就完成了咖啡店经纬度设置,这样,再次新增咖啡店,就可以拥有经纬度信息了,由于我之前创建的咖啡店多是无效地址,所以之前教程中新增的咖啡店数据删除,重新添加:
点击「提交」按钮,保存成功后查看数据库,就可以看到经纬度信息了:
下一篇教程,我们将基于咖啡店的经纬度信息将其显示在高德地图上:
13 Comments
在
GaodeMaps
中记得引入Guzzle HTTP 扩展包:use GuzzleHttp\Client;
记得线上服务器添加
GAODE_MAPS_WS_API_KEY
应该是 env.example 记得添加
GAODE_MAPS_WS_API_KEY
。。。
+_+
Guzzle 一直提示这个rgument 3 passed to GuzzleHttp\Client::request() must be of the type array, boolean given,
自己 debug 下吧 提示已经告知你原因了 排查为什么传进去的是布尔值而不是数组
看了手册发现guzzle的语法被废弃了
找到原因了,guzzle的一个ssl设置语法被废弃了,修改了就正常了
我的结果是 {"lat":null,"lng":null}
然后 dd($geocodeData);
{#242 ▼ +"status": "0" +"info": "INVALID_USER_KEY" +"infocode": "10001" } 为什么失败啊??
INVALID_USER_KEY 你的APP_KEY有问题?