前言
随着移动互联网的发展,基于位置的服务(LBS)需求日益增长。商场作为城市商业活动的重要载体,其周边的地理信息对于消费者、商家以及城市规划者具有重要的参考价值。在高德地图中,POI(Point of Interest,兴趣点)数据包含了各种类型的地理实体。高德地图默认采用中国标准的火星坐标系(GCJ-02)。WGS84 坐标系统是一种全球通用的地理坐标系统。在高德的检索 API 中,传入的检索面数据的坐标通常要求是高德坐标系。如果需求是传入 WGS84 的坐标,则需要进行坐标转换后才能作为查询参数传入。
本文将以某商场的 POI 数据检索为例,探讨这一方法的实现过程和应用效果。除了讲解如何实现不同的空间面转换查询,还讲解如何实现对高德地图进行穷举查询。
一、面数据检索简介
本节重点对高德地图中的面检索 API 进行介绍,分别从服务地址、请求参数、响应参数三个方面进行讲解。
1、服务地址
多边形区域搜索 API 服务地址
| URL | 请求方式 |
|---|---|
| https://restapi.amap.com/v5/place/polygon?parameters | GET |
parameters 代表的参数包括必填参数和可选参数。所有参数均使用和号字符 (&) 进行分隔。
2、请求参数
| 参数名 | 含义 | 规则说明 | 是否必须 | 缺省值 |
|---|---|---|---|---|
| key | 高德 Key | 用户在高德地图官网申请 Web 服务 API 类型 Key | 必填 | 无 |
| polygon | 多边形区域 | 多个坐标对集合,坐标对用" | "分割。多边形为矩形时,可传入左上右下两顶点坐标对;其他情况下首尾坐标对需相同。 | 必填 |
| keywords | 地点关键字 | 需要被检索的地点文本信息。只支持一个关键字,文本总长度不可超过 80 字符 | 可选 | 无 |
| types | 指定地点类型 | 地点文本搜索接口支持按照设定的 POI 类型限定地点搜索结果;地点类型与 poi typecode 是同类内容,可以传入多个 poi typecode,相互之间用' | '分隔 | 可选 |
| show_fields | 返回结果控制 | 用来筛选 response 结果中可选字段。多个字段间采用','进行分割;未设置时,只返回基础信息类内字段 | 可选 | 空 |
| page_size | 当前分页展示的数据条数 | 取值 1-25 | 可选 | 默认为 10 |
| page_num | 请求第几分页 | 请求第几分页 | 可选 | 默认为 1 |
| sig | 数字签名 | 请参考数字签名获取和使用方法 | 可选 | 无 |
| output | 返回结果格式类型 | 默认格式为 json,目前只支持 json 格式 | 可选 | json |
| callback | 回调函数 | 此参数只在 output 参数设置为 JSON 时有效 | 可选 | 无 |
3、服务示例
https://restapi.amap.com/v5/place/polygon?polygon=116.460988,40.006919|116.48231,40.007381|116.47516,39.99713|116.472596,39.985227|116.45669,39.984989|116.460988,40.006919&keywords=肯德基&types=050301&key=<用户的 key>
| 参数 | 值 | 备注 | 必选 |
|---|---|---|---|
| polygon | 多边形区域,多个坐标对集合,坐标对用" | "分割。多边形为矩形时,可传入左上右下两顶点坐标对;其他情况下首尾坐标对需相同 | |
| keywords | 地点关键字,需要被检索的地点文本信息,只支持一个关键字 | 可选 | |
| types | 指定地点类型,地点文本搜索接口支持按照设定的 POI 类型限定地点搜索结果 |
4、返回结果
| 名称 | 类型 | 说明 |
|---|---|---|
| status | string | 本次 API 访问状态,如果成功返回 1,如果失败返回 0。 |
| info | string | 访问状态值的说明,如果成功返回"ok",失败返回错误原因。 |
| infocode | string | 返回状态说明,10000 代表正确。 |
| count | string | 单次请求返回的实际 poi 点的个数 |
| pois | object | 返回的 poi 完整集合 |
poi 对象包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| name | string | poi 名称 |
| id | string | poi 唯一标识 |
| location | string | poi 经纬度 |
| type | string | poi 所属类型 |
| typecode | string | poi 分类编码 |
| pname | string | poi 所属省份 |
| cityname | string | poi 所属城市 |
| adname | string | poi 所属区县 |
| address | string | poi 详细地址 |
| pcode | string | poi 所属省份编码 |
| adcode | string | poi 所属区域编码 |
| citycode | string | poi 所属城市编码 |
注意以下字段如需返回需要通过'show_fields'进行参数类设置:
| 字段 | 类型 | 说明 |
|---|---|---|
| children | object | 设置后返回子 POI 信息 |
| business | object | 设置后返回子 POI 信息 |
| indoor | object | 设置后返回室内相关信息 |
| navi | object | 设置后返回导航位置相关信息 |
| photos | object | 设置后返回 poi 图片相关信息 |
二、面数据检索实践
结合具体的商场 - 以步步高梅溪新天地为例,重点介绍基于梅溪新天地的面数据检索。同时对比使用 GCJ-02 和 WGS84 两个不同的坐标参考,最后介绍使用穷举的办法实现面数据的检索。
1、GCJ-02 面检索
为了演示方便,我们使用的数据查询面是直接从高德地图获取的面数据。因此,我们最开始的查询面数据类型是 GCJ-02 的类型。
@Test
public void searchByPolygon() throws InterruptedException {
String polygon = "112.859225,28.20061;112.859172,28.20059;112.859117,28.200556;112.859058,28.200503;112.859003,28.200391;112.858739,28.199761;112.858627,28.199041;112.858673,28.198558;112.859401,28.196282;112.859435,28.196228;112.859515,28.196194;112.859594,28.196185;112.859938,28.196191;112.860163,28.196155;112.860421,28.196063;112.860709,28.195926;112.860964,28.195702;112.861103,28.195304;112.861116,28.195285;112.86115,28.195274;112.863672,28.195709;112.863705,28.195727;112.863726,28.195767;112.863732,28.195806;112.863328,28.197513;112.863249,28.19793;112.863134,28.198666;112.863084,28.198923;112.862918,28.19949;112.862647,28.200901;112.862622,28.200931;112.862583,28.200958;112.862531,28.200989;112.862463,28.201008;112.859225,28.20061";
polygon = polygon.replaceAll(";", "|");
String types = "080000|060000";
String page_size = "25";
//String region = "430104";
String show_fields = "children,business,indoor,navi,photos";
HttpResponse<String> result = null;
for(int i = 1; i <= 1; i++) {
result = amapSearchService.searchByPolygon(polygon, keywords, types, page_size, String.valueOf(i), show_fields, AMAP_CLIENT_AK);
System.out.println(result.getBodyResult());
Thread.sleep(3000L); //休眠 3 秒
}
}
运行以后可以看到输出,说明按照面检索成功,返回数据符合预期,是所属商场返回的 POI 数据。
2、WGS84 面检索
除了直接使用 GCJ-02 的这种查询方式,更多的场景下需要使用 WGS84 的坐标,因此我们需要使用将 GCJ-02 的坐标转换成 WGS84。
private String gcj2wgs84(String source) {
StringBuffer wgs84 = new StringBuffer(source.length());
String[] AOI_Str_Array = source.split(";");
//处理坐标
for (int i = 0; i < AOI_Str_Array.length; i++) {
String loc = AOI_Str_Array[i];
String[] latlon = loc.split(",");
double lng = Double.parseDouble(latlon[0]);
double lat = Double.parseDouble(latlon[1]);
//将高德坐标转换成 WGS84 坐标
double[] gcj284 = CoordinateTransformUtil.gcj02towgs84(lng, lat);
wgs84.append(gcj284[0]).append(",").append(gcj284[1]).append("|");
}
return wgs84.substring(0, wgs84.length()-1);
}
下面是更详细的 gcj-02 坐标转 wgs84 点数据的转换方法:
/**
* -GCJ02(火星坐标系) 转 GPS84
*
* @param lng 火星坐标系的经度
* @param lat 火星坐标系纬度
* @return WGS84 坐标数组
*/
public static double[] gcj02towgs84(double lng, double lat) {
if (out_of_china(lng, lat)) {
return new double[] { lng, lat };
}
double dlat = transformlat(lng - 105.0, lat - 35.0);
double dlng = transformlng(lng - 105.0, lat - 35.0);
double radlat = lat / 180.0 * Math.PI;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * Math.PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * Math.PI);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[] { lng * 2 - mglng, lat * 2 - mglat };
}
与 GCJ-02 坐标的面检索方法一致,我们将 GCJ-02 坐标转为 WGS84 后,再进行检索实验。可以看到在返回的检索结果数据中,跟之前返回的数据不一致了。这些 POI 兴趣点已经不在指定的商场里面了。由此得出一个结论,就是调用高德的面数据查询中,传入的面数据的坐标类型是 GCJ-02,如果传入的是 WGS84,其最后的计算结果肯定会偏移,因此要求大家前偏转回来或者直接使用 GCJ-02 的坐标参考。
3、穷举检索实现
与天地图和百度地图的 API 不同的是,在高德的数据返回接口中,没有一个明确的总数的概念。而通常在实际业务中,我们需要将所有的数据都进行抓取,在这里我们可以使用穷举的方法来。简单来讲就是保证调用服务最少调用一次,然后通过返回的当前数据条数有没有大于 0,如果大于 0,表示当前还有数据,需要继续;否则停止调用。
HttpResponse<String> result = null;
int scrapingIndex = 1;
int dataCount = 0;
do {
result = amapSearchService.searchByPolygon(gcjPolygon, keywords, types, page_size, String.valueOf(scrapingIndex), show_fields, AMAP_CLIENT_AK);
System.out.println("使用高德面搜索结果");
System.out.println(result.getBodyResult());
if(StringUtils.isNotEmpty(result.getBodyResult())) {
AmapSearchVO searchVO = gson.fromJson(result.getBodyResult(), AmapSearchVO.class);
System.out.println(searchVO.getCount());
dataCount = searchVO.getCount();
scrapingIndex ++;
}
Thread.sleep(3000L); //休眠 3 秒
} while (dataCount > 0);
System.out.println("一共抓取数据页数:" + scrapingIndex);
使用的方法就是使用 do...while 循环,至少保证一次进入,在符合条件的情况下,循环推出。
运行上面的程序后,可以看到数据被抓取到了,而且还分页了。同时在 IDE 中可以看到以下输出,这句话表示当前的数据检索大约有 10 页,超过 10 页基本也就没数据了。
一共抓取数据页数:10
三、总结
本文介绍了基于 Java 在高德地图面查询检索中使用 WGS84 坐标的方法。利用 Java 开发的高德地图 API,可以方便地调用地图服务,实现坐标转换、地图绘制、数据查询等功能。通过本文,不仅可以学习高德地图的按面检索 API,同时了解了使用不同的坐标参考系的查询结果影响,通过实例对结果进行了简单的说明。


