前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用编程赋能工作系列——高德地图开发

用编程赋能工作系列——高德地图开发

作者头像
数据小磨坊
发布2020-02-25 11:40:21
1.3K0
发布2020-02-25 11:40:21
举报
文章被收录于专栏:数据小魔方数据小魔方

作为菜鸟分析师一枚,日常工作中需要处理大量地理位置相关(如城市、辖区、街道、商场、楼宇等)数据。分析报告中总是用吐了的柱形图、条形图,不仅自己看着辣眼睛,老板也审美疲劳。

想画个地图来装点一下报告视觉水平,可是又不会处理经纬度,知道地址不会批量获取经纬度,给出经纬度也不会获取对应的地址。求隔壁的开发小哥哥又远水解不了近渴(开发小哥哥又不是你家的)。

还好我早有准备,偷偷学习了Python和R,处理经纬度这点儿小事儿怎能难住我,自己动手丰衣足食,说不定哪天你就抢了隔壁开发的饭碗。


经纬度获取与处理是空间数据处理的基本功,当前各行业(特别是互联网行业)能叫的上号的头部公司,他们的主营业务也大多涉及复杂的地理区域分析工作,如用户的空间行为轨迹、O2O业务的配送轨迹、传统电商的物流轨迹、经营B端业务的商户地址、旅游&交通用户出行轨迹、摩拜单车行车轨迹、滴滴打车乘车轨迹等……

扯了这么多,没错我只是想说,学会今天要分享的经纬度获取知识,你在日常数据分析工作中会更加游刃有余,会拥有与众不同的视角和切入点。

好吧以下是学习时间:


本篇分为两个技能模块,四个小节,分别会用R语言和Python介绍并实现如何通过具体地址从高德地图api接口中调取地理经纬度,以及如何通过具体经纬度调用具体地理地址。

在开始介绍过程之前,你需要先申请一个高德地图开发者秘钥:

https://lbs.amap.com/api/webservice/guide/create-project/get-key

然后最好读一下高德地图经纬度解析的官方文档

https://lbs.amap.com/api/webservice/guide/api/georegeo

前年的时候分享过一篇百度地图经纬度解析的内容,但是因为两者经纬度编码规则不同,无法直接兼容,所以这里给出高德版方案,以后有时间详细介绍两者如何进行转换的算法。

高德地图开放了经纬度解析的api服务,但是对于个人开发者有日配额,作为非开发人员,完全可以满足日常需要。这些api是通用接口,大多数语言都具备调用能力,其中可提供的返回信息非常多,你可以参考开发api开发文档说明酌情考虑接收你需要的数据。

以下我只提供了精简版的代码,如果你需要更丰富的数据,可以适当修改代码即可。

一、R语言实现方案

1)地址转换经纬度

代码语言:javascript
复制
## !/user/bin/env RStudio 1.1.423
## -*- coding: utf-8 -*-
## 高德地图经纬度编码解析

library("httr") 
library("magrittr") 
library("jsonlite")
library('stringr')
library('dplyr')
library('rlist')

dizhi = c('北京市朝阳区望京东路4号横店大厦','北京市海淀区上地信息路9号奎科科技大厦','aaa',NA)
#将地址解析为具体的经纬度:
GetJD <- function(address){
  url = "https://restapi.amap.com/v3/geocode/geo?parameters"
  # 这里是地址转经纬度的api服务调用地址
  header  <- c("User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36")
  # 这个是请求的报头,用来把你的开发平台伪装成一个真实的浏览器
  payload = list(
    'output' = 'json', 
    'key' = '***********************'
    # 各位小伙伴儿一定要自己去申请秘钥,我自己的我就匿了
  ) # 这里是请求的核心参数,一个都不能少,秘钥要自己去申请
  addinfo <- list()
  for (i in dizhi){
    payload[["address"]] = i
    tryCatch({
      web <- GET(url,add_headers(.headers = header),query = payload) %>% content(as="text",encoding="UTF-8") %>% fromJSON(flatten = TRUE) %>% .$geocodes
      if(length(web)  > 0){
        content <- web  %>% .$location %>% str_split(',') %>% `[[`(1) 
        print(sprintf("正在抓取【%s】的经纬度",i))
      }else{
        content <- c(NA,NA)
        cat(sprintf("任务【%s】请求失败!",i),sep = "\n") 
      }
      addinfo <- rbind(addinfo,content) 
    },error = function(e){
      cat(sprintf("任务【%s】处理失败!",i),sep = "\n")
      addinfo <- rbind(addinfo,c(NA,NA))
    })
    Sys.sleep(runif(1.5))
  }
  result_data <- addinfo %>% matrix(ncol=2) %>% data.frame(adress = dizhi,stringsAsFactors = F)  %>% rename(lng =X1,lat = X2 )
  result_data$lng <- as.numeric(result_data$lng)
  result_data$lat <- as.numeric(result_data$lat)
  print("所有数据全部抓取完毕!!!")
  return(result_data) 
}
system.time(myresult<-GetJD(dizhi))

存放地址的向量中加入了NA和aaa这样的无效值仅仅是为了保证程序容错能力,整体上不涉及太复杂的逻辑实现。

2)经纬度转地址

代码语言:javascript
复制
#地址逆解析——将经纬度还原为具体地址
lddata = data.frame(
  lat = c(39.934,40.013,40.047,NA,4444),
  lon = c(116.329,116.495,116.313,NA,6666)
)
GetAddress <- function(lddata){
  url = "https://restapi.amap.com/v3/geocode/regeo?parameters"
  header = c('User-Agent'= 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36')
  payload = list(
    'output' = 'json', 
    'key'    = '*******************'
    # 匿了匿了
  ) 
  addinfo = c() 
  for (i in 1:nrow(lddata)){
    payload[['location']] = sprintf('%.3f,%.3f',lddata[i,'lon'],lddata[i,'lat'])
    tryCatch({
      web <-  GET(url,add_headers(.headers = header),query = payload)  %>% content(as="text",encoding="UTF-8")  %>% fromJSON(flatten = TRUE)   
      if(length(web$regeocode$formatted_address) > 0 ){
        content <-  web  %>% .$regeocode %>% .$formatted_address
        cat(sprintf("正在处理第【%d】个任务!",i),sep = "\n")
      } else {
        content <-NA
        cat(sprintf("第【%d】个任务请求失败!",i),sep = "\n")
      }
      addinfo <- content %>% c(addinfo,.)
    },error = function(e){
      cat(sprintf("第【%d】个任务处理错误!",i),sep = "\n")
      addinfo <- c(addinfo,NA)
    })
    Sys.sleep(runif(2))
  } 
  print("所有任务处理完毕!!!")
  cbind(addinfo,lddata) %>% return()
 
}
system.time(MyAddress <- GetAddress(lddata))

测试的经纬度中加入了NA和444这样的非法经纬度,同样是为了在代码中植入容错模块,提高容错性。在构思代码之前,要提前思考可能遇到哪些错误,不同的错误应该在那个阶段进行容错,不同的错误会导致哪些不同的结果,出现了错误应该如何替补默认返回值。

二、Python实现方案

3)地址转经纬度

代码语言:javascript
复制
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import  time
import requests
import re,random
import  numpy  as np
import  pandas  as pd
import 
dizhi = ['北京市朝阳区望京东路6号望京国际研发园三期','北京市海淀区上地信息路9号奎科科技大厦',None,'aaa']
def getid(dizhi):
    url = "https://restapi.amap.com/v3/geocode/geo?parameters"
    header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'}
    payload = {
        'output':'json',
        'key':'*******************'
        # 匿了哦
        }
    addinfo = []
    for i in dizhi:
        payload['address'] = i
        try:
            content =  requests.get(url,params=payload,headers=header).json()
            if content['status'] == '1':
                addinfo.append(re.split(',',content['geocodes'][0]['location']))
                print("任务【{}】请求成功!".format(i))
            else:
                addinfo.append([np.NaN,np.NaN])
                print("任务【{}】请求失败!".format(i))
        except:
            addinfo.append([np.NaN,np.NaN])
            print("任务【{}】处理失败!".format(i))
            pass
        time.sleep(random.choice(range(2)))
    temp = pd.DataFrame(addinfo,columns= ['lng','lat'])
    temp['address'] = dizhi
    return( temp)

if __name__ == "__main__":
    #计时开始:
    t0 = time.time()
    myaddress = getid(dizhi)
    t1 = time.time()
    total = t1 - t0
    print("消耗时间:{}".format(total))

此类api的调用方案,Python的可获取资料网上会比较多一些,毕竟这几年Python火起来之后,Python网络爬虫成了好多小伙伴儿更换职业赛道的重要突破口。

4)经纬度转地址

代码语言:javascript
复制
lat = [39.934,40.013,40.047,None,7000]
long = [116.329,116.495,116.313,None,40000]
lddata = [(j,w) for j,w in zip(long,lat)]
def GetAddress(lddata):
    url = "https://restapi.amap.com/v3/geocode/regeo?parameters"
    header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'}
    payload = {
        'output':'json',
        'key':'**********************'
        }
    addinfo = []
    j = 0
    for lon,lat in lddata:
        payload['location'] = '{0:s},{1:s}'.format(str(lon),str(lat))
        try:
            content =  requests.get(url,params=payload,headers=header).json()
            if content['status'] == '1':
                addinfo.append(content['regeocode']['formatted_address'])
                print("任务【{}】请求成功!".format(j+1))
            else:
                addinfo.append(np.NaN)
                print("任务【{}】请求失败!".format(j+1))
        except:
            addinfo.append(np.NaN)
            print("任务【{}】处理失败!".format(j+1))
        time.sleep(random.choice(range(2)))
        j += 1
    temp = pd.DataFrame(np.array(lddata),columns = ['long','lat'])
    print(addinfo)
    temp['address'] = addinfo
    return(temp)
if __name__ == "__main__":    
    #计时开始:
    t0 = time.time()
    mylonlat = GetAddress(lddata)
    t1 = time.time()
    total = t1 - t0
    print("消耗时间:{}".format(total))

针对高德地图API的调用总结几点:

1)api开放接口是很规范的数据获取渠道,调用成本低,效率高(在接口时限内)相比你从html或者xml里面解析数据要高效的多,核心只需要了解有那些必须提供的请求参数(如想要的返回值格式、使用服务的合法秘钥等)。

2)拿到返回值之后就可以在各平台进行结构化处理,一般都会选择json进行返回,因为这种格式比较主流,各平台处理工具也比较多,比如R语言中的jsonlite、python中则因为字典天然与json格式高度兼容,甚至都不怎么需要特殊处理,使用字典的基本函数就可以很好的提取其中有效信息。

3)关于容错,R语言需要重点复习trycach else的用法,Python中测试需要掌握try …… except等异常捕获处理机制。这里最重要的是,你要判断哪些是错误,哪些可以合法通过请求,但是却不一定能拿到想要的数据,这时需要想办法进行替补或跳出循环(即直接忽略)。

本文真的是一篇满满的干货,也就是这段疫情不能出门的时间才有足够的心力、时间去写,简直可以称作是疫情期间的学习日记。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据小魔方 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档