动手实操(一):如何用七牛云 API 实现相片地图?

编者注:本文通过七牛云图片的扩展API记录了图片的拍摄地点等信息,结合高德地图的API,实现了将图片记录在地图上,有需要可以看看实现思路。

实操玩家:

实操玩家

在苹果手机上,我们只要打开定位服务,拍照后便能在相簿中找到地图,地图上显示着在各地拍摄的相片。网站上这种显示方式也并不少见,例如 Flickr、即将关闭的 Panoramio 等。

作为地图爱好者,每每看到地图就激动不已,就想若能在自己博客上也这么显示,那该多好!

摄于汕头市澄海区莲下镇▲ 苹果手机上的相片地图

▲ 苹果手机上的相片地图

相片的地理位置信息,是通过手机等设备的 GPS 模块记录的,并在其图片文件的 EXIF 数据,保留了这些位置信息。

使用七牛做博客图床以来,其图片处理 API 为各种显示需求提供了便利,这其中有个可以获取图片 EXIF 信息的 API,可以很方便的获取到图片的 EXIF 信息,而 EXIF 包含的 GPS 位置信息,则可以实现以上所提到的效果。

实现流程

EXIF 为位置信息保留了经纬坐标、海拔等,将它转成实际地址信息,或者显示到地图上,还需要配合一个地图 API,在此就以高德地图为例。

实现流程大致如下

  • 请求数据:利用七牛 API 请求获取图片的 GPS 信息
  • 转换坐标:因相片的坐标数据是以 WGS84 坐标系统为准的,得转换坐标
  • 显示地址:通过高德地图 API 实现

获取坐标

我们还是以一张图片为例,来分析下如何实现。

莲阳河畔,渡亭堤顶的花

▲ 莲阳河畔,渡亭堤顶的花

上图是在汕头市澄海区莲下镇渡亭村莲阳河畔拍的花,其图片地址如下:http://source.fooleap.org/show-photo-location-on-map-with-qiniu-and-amap-api.jpg

那么,七牛图片 EXIF 信息的接口如下:http://source.fooleap.org/show-photo-location-on-map-with-qiniu-and-amap-api.jpg?exif

直接用浏览器打开,可以很方便地找到所有 GPS 开头的属性。其中,海拔等数据可以忽略掉,位置信息最为重要的是经纬坐标,即以下四个值:

{
   \"GPSLatitude\": {
       \"val\": \"23, 29, 10.91\",
       \"type\": 5
   },
   \"GPSLatitudeRef\": {
       \"val\": \"N\",
       \"type\": 2
   },
   \"GPSLongitude\": {
       \"val\": \"116, 46, 36.49\",
       \"type\": 5
   },
   \"GPSLongitudeRef\": {
       \"val\": \"E\",
       \"type\": 2
   }

}

那么这个坐标为 23°29’10.91”N, 116°46’36.49”E,也就是北纬 24 度 29 分 10.91 秒,东经 116 度 46 分 36.49 秒。

在各种互联网地图上,坐标是以度为单位的,东经北纬为正值,西经南纬为负值。这里需要将度分秒的经纬度转换以度为单位的,因度分秒之间为 60 进制,故转换公式为:

度分秒 = 度 + 分 / 60 + 秒 / (60 * 60)

计算后得出上面的坐标约为 116.776803, 23.486364,这是一个 WGS84 坐标。

坐标转换此前已经说过不少了,因在网页上使用,直接采用 coordtransform 模块[3] 即可,以上坐标转换为 GCJ02 是 116.781381, 23.483788。

可以使用高德的坐标拾取系统,按坐标搜索一下,验证结果是否正确。

以上过程使用 JS 实现代码如下,需引入 coordtransform 模块。

var xhr = new XMLHttpRequest();

xhr.open(\'GET\', \'http://source.fooleap.org/show-photo-location-on-map-with-qiniu-and-amap-api.jpg?exif\', true);

xhr.onreadystatechange = function() {
   if (xhr.readyState == 4 && xhr.status == 200)
   {
       var data = JSON.parse(xhr.responseText);
       if ( !!data.GPSLongitude ) {
           var olat = data.GPSLatitude.val.split(\', \');
           var olng = data.GPSLongitude.val.split(\', \');
           var lat=0, lng=0, coord;
           for( var i = 0; i < olat.length; i++ ){
               lat += olat[i] / Math.pow(60, i);
               lng += olng[i] / Math.pow(60, i);
           }
           lat = data.GPSLatitudeRef.val == \'S\' ? -lat: lat;
           lng = data.GPSLongitudeRef.val == \'W\' ? -lng: lng;
           coord = coordtransform.wgs84togcj02(lng, lat).join(\',\');
       }
   }

}

xhr.send();

获取图片地址信息

有了坐标就有了地理位置,有了地理位置就知道是什么地方。高德地图逆地理编码 API 正在招手:「给我一个坐标,我将还你一个详细地址。」

根据文档,最基本的只需 location 参数传个坐标即可返回地址信息。此 API 还可批量获取,可以根据自己需求传入可选的参数。

以上面获取的坐标为例:

http://restapi.amap.com/v3/geocode/regeo?location=116.781381,23.48378&key=<用户的key>

请求后返回的 JSON 如下,具体返回的参数的详细说明参考文档。

{
   \"status\": \"1\",
   \"info\": \"OK\",
   \"infocode\": \"10000\",
   \"regeocode\": {
       \"formatted_address\": \"广东省汕头市澄海区莲下镇渡亭村\",
       \"addressComponent\": {
           \"country\": \"中国\",
           \"province\": \"广东省\",
           \"city\": \"汕头市\",
           \"citycode\": \"0754\",
           \"district\": \"澄海区\",
           \"adcode\": \"440515\",
           \"township\": \"莲下镇\",
           \"towncode\": \"440515102000\",
           \"neighborhood\": {
               \"name\": [],
               \"type\": []
           },
           \"building\": {
               \"name\": [],
               \"type\": []
           },
           \"streetNumber\": {
               \"street\": \"文明路\",
               \"number\": \"159号\",
               \"location\": \"116.78592,23.48872\",
               \"direction\": \"东北\",
               \"distance\": \"718.365\"
           },
           \"businessAreas\": [
               []
           ]
       }
   }

}

准确度一般般,到乡(镇、街道)这一级基本没问题。我们也不需要太准确的信息,毕竟定位信息有时候还是挺隐私的。

显示相片地图 

将相片显示在地图上,这似乎才是初衷,在这里,我将它分成两部分,一部分是静态地图,一部分是动态地图。

静态地图

将图片显示到静态地图上面,需要用到的高德地图 API 是静态地图 API。

根据高德的文档,自定义标注(marker)图片只能支持 PNG 格式,需要多来一张图片吗?不需要,这里可以直接用七牛图片处理 API,将原图调下大小并指定输出格式。

http://source.fooleap.org/show-photo-location-on-map-with-qiniu-and-amap-api.jpg?imageView2/1/w/100/h/100/format/png

上面的链接是输出等比缩放并居中裁剪的 100×100 PNG 格式图片,然而应用到高德接口还是出错了。我尝试在最后再加上 .png,结果成功,这意味着高德是以后缀名判断文件格式的,也是醉了。

http://restapi.amap.com/v3/staticmap?zoom=12&size=640*427&scale=2&markers=-1,http://source.fooleap.org/show-photo-location-on-map-with-qiniu-and-amap-api.jpg?imageView2/1/w/100/h/100/format/png.png,0:116.781381,23.48378&key=<用户的key>

以上拼凑出的链接显示如下。

▲ 高德静态地图 API 显示相片位置
▲ 高德静态地图 API 显示相片位置

高德静态地图 API 一样支持批量,具体可查看文档。

动态地图

想要在网页中实现跟苹果相册地图差不多样式且可控的地图,这就得用到高德地图 JS API,主要是用到点标记,这里就不多废话了,直接上图。

文章来源


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流