2010-07-15 16 views
25

He tratado de ejecutar el código de codificación geográfica de ubicaciones en R a través de Google Maps y el paquete XML de esta entrada del blog: http://www.r-chart.com/2010/07/maps-geocoding-and-r-user-conference.htmlgeocodificación en R con Google Maps

He aquí sus funciones:

getDocNodeVal=function(doc, path){ 
    sapply(getNodeSet(doc, path), function(el) xmlValue(el)) 
} 

gGeoCode=function(str){ 
    library(XML) 
    u=paste('http://maps.google.com/maps/api/geocode/xml?sensor=false&address=',str) 
    doc = xmlTreeParse(u, useInternal=TRUE) 
    str=gsub(' ','%20',str) 
    lng=getDocNodeVal(doc, "/GeocodeResponse/result/geometry/location/lat") 
    lat=getDocNodeVal(doc, "/GeocodeResponse/result/geometry/location/lng") 
    c(lat,lng) 
} 

Cuando corro gGeoCode(), me sale el siguiente error:

> gGeoCode("Philadelphia, PA") 
failed to load external entity "http%3A//maps.google.com/maps/api/geocode/xml%3Fsensor=false&address=%20Philadelphia,%20PA" 
Error: 1: failed to load external entity "http%3A//maps.google.com/maps/api/geocode/xml%3Fsensor=false&address=%20Philadelphia,%20PA" 

Si tan sólo pegar en un navegador de la ur API l con Philadelphia, PA añadido al final, como la cadena pasó a xmlParseTree, obtengo un resultado que parece xml legítimo cuando lo descargo.

¿Esto es un problema con el código, o no he podido configurar algo?

+0

Aside/off-topic: en lugar de str = gsub ('', '% 20', str), podría hacer str = URLencode (str). Solo comento aquí porque creo que es una función genial :) –

+0

Sí, una función muy buena. Si solo funcionara! : -/ – JoFrhwld

+0

la llamada json funciona, vea mi respuesta a continuación :) –

Respuesta

23

¿Ha pensado en utilizar la llamada JSON en su lugar? En cuanto a su código, podría lograr lo mismo al hacer esto (deberá instalar los paquetes RCurl y RJSONIO de omegahat.com).

Copia y pega esto en R:

library(RCurl) 
library(RJSONIO) 

construct.geocode.url <- function(address, return.call = "json", sensor = "false") { 
    root <- "http://maps.google.com/maps/api/geocode/" 
    u <- paste(root, return.call, "?address=", address, "&sensor=", sensor, sep = "") 
    return(URLencode(u)) 
} 

gGeoCode <- function(address,verbose=FALSE) { 
    if(verbose) cat(address,"\n") 
    u <- construct.geocode.url(address) 
    doc <- getURL(u) 
    x <- fromJSON(doc,simplify = FALSE) 
    if(x$status=="OK") { 
    lat <- x$results[[1]]$geometry$location$lat 
    lng <- x$results[[1]]$geometry$location$lng 
    return(c(lat, lng)) 
    } else { 
    return(c(NA,NA)) 
    } 
} 

Así es como se utiliza las funciones anteriores:

x <- gGeoCode("Philadelphia, PA") 

Este es el resultado que se obtiene. Creo que en el código original, lat y lng están mezclados? Pero espero que esto es lo que quiere:

> x 
[1] 39.95233 -75.16379 

Esperanza que ayuda a una pequeña compañera,

, Tony Breyal

+0

¡Fantástico! Hace el trabajo! – JoFrhwld

+0

Super, podría usar esto. – apeescape

+0

@Tony Breyal: Probé este código en R 2.14.0. Desafortunadamente recibo un error: 'Error en x $ results [[1]] $ geometry $ location $ lat: $ operator no es válido para vectores atómicos'. ¿Qué estoy haciendo mal aquí? – radek

5

Este código funciona utilizando sólo la biblioteca XML

library(XML) 
url = 'http://maps.googleapis.com/maps/api/geocode/xml?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true' 
doc = xmlTreeParse(url, useInternal=TRUE) 
lat = as.numeric(xmlValue(getNodeSet(doc, '//location/lat')[[1]])) 
lng = as.numeric(xmlValue(getNodeSet(doc, '//location/lng')[[1]])) 
2

he modificado Solución de Tony Breyal para que la función gGeoCode también tome un vector de direcciones como entrada. Con esta versión, no solo puede hacer gGeoCode("Philadelphia, PA") sino también gGeoCode(c("Philadelphia, PA","New York, NY")) con este valor de retorno.

address   lat   lng   
1 "Philadelphia, PA" "39.952335" "-75.163789" 
2 "New York, NY"  "40.7143528" "-74.0059731" 

Tenga en cuenta que la API de Google Maps tiene un límite diario de 2.500 para que su vector no sea demasiado largo. Aquí está la función de actualización:

library(RCurl) 
library(RJSONIO) 

construct.geocode.url <- function(address, return.call = "json", sensor = "false") { 
    root <- "http://maps.google.com/maps/api/geocode/" 
    u <- paste(root, return.call, "?address=", address, "&sensor=", sensor, sep = "") 
    return(URLencode(u)) 
} 

gGeoCode <- function(address,verbose=FALSE) { 
    require("plyr") 
    if(verbose) cat(address,"\n") 
    u <- aaply(address,1,construct.geocode.url) 
    doc <- aaply(u,1,getURL) 
    json <- alply(doc,1,fromJSON,simplify = FALSE) 
    coord = laply(json,function(x) { 
    if(x$status=="OK") { 
     lat <- x$results[[1]]$geometry$location$lat 
     lng <- x$results[[1]]$geometry$location$lng 
     return(c(lat, lng)) 
    } else { 
     return(c(NA,NA)) 
    } 
    }) 
    if(length(address)>1) colnames(coord)=c("lat","lng") 
    else names(coord)=c("lat","lng") 
    return(data.frame(address,coord)) 
} 

EDIT: pequeña corrección en el código para que lat y lng se devuelven como valores numéricos.

2

Necesitaba obtener todas las direcciones devueltas desde geocode, no solo la primera, así que escribí una pequeña función para hacerlo.Se puede utilizar para geocode y para codificación geográfica inversa

geocode <- function(address,reverse=FALSE) { 
    require("RJSONIO") 
    baseURL <- "http://maps.google.com/maps/api/geocode/json?sensor=false&" 

    # This is not necessary, 
    # because the parameter "address" accepts both formatted address and latlng 

    conURL <- ifelse(reverse,paste0(baseURL,'latlng=',URLencode(address)), 
            paste0(baseURL,'address=',URLencode(address))) 
    con <- url(conURL) 
    data.json <- fromJSON(paste(readLines(con), collapse="")) 
    close(con) 
    status <- data.json["status"] 
if(toupper(status) == "OK"){ 
    t(sapply(data.json$results,function(x) { 
     list(address=x$formatted_address,lat=x$geometry$location[1], 
               lng=x$geometry$location[2])})) 
} else { 
    warning(status) 
    NULL 
} 
} 

ejemplo Geocode:

geocode("Dupont Cir NW, Washington, DC 20036, USA")

 address                lat  lng  
[1,] "Dupont Circle Northwest, Washington, DC 20036, USA"     38.90914 -77.04366 
[2,] "Dupont Circle, 1 Dupont Circle Northwest, Washington, DC 20036, USA" 38.90921 -77.04438 
[3,] "Washington, DC 20036, USA"           38.90808 -77.04061 
[4,] "Dupont Circle, Washington, DC 20036, USA"       38.90958 -77.04344 

Geocode Reverse ejemplo:

nota de que la dirección se puede dirección o latlng ya sea el formato, el parámetro reverse no se utiliza pero está incluyendo para su uso futuro con otros servicios de geocodificación

geocode("38.910262, -77.043565")

 address             lat  lng  
[1,] "40-58 Dupont Circle Northwest, Washington, DC 20036, USA" 38.91027 -77.04357 
[2,] "Washington, DC 20036, USA"        38.90808 -77.04061 
[3,] "Dupont Circle, Washington, DC, USA"      38.90969 -77.04334 
[4,] "Northwest Washington, Washington, DC, USA"    38.94068 -77.06796 
[5,] "District of Columbia, USA"        38.90598 -77.03342 
[6,] "Washington, DC, USA"          38.90723 -77.03646 
[7,] "United States"           37.09024 -95.71289 
0

Esto también se puede hacer con mi paquete googleway y una válida API de Google Maps clave

library(googleway) 

key <- "your_api_key" 

df <- google_geocode("Philadelphia, PA", 
         key = key) 

df$results$geometry$location 
#  lat  lng 
# 1 39.95258 -75.16522 

y para revertir-codificación geográfica

df <- google_reverse_geocode(location = c(39.95258, -75.16522), 
          key = key) 

df$results$formatted_address 
# [1] "1414 PA-611, Philadelphia, PA 19102, USA"   "15th St Station - MFL, Philadelphia, PA 19102, USA" 
# [3] "Center City West, Philadelphia, PA, USA"   "Center City, Philadelphia, PA, USA"     
# [5] "Philadelphia, PA, USA"        "Philadelphia, PA 19107, USA"      
# [7] "Philadelphia County, PA, USA"      "Philadelphia-Camden-Wilmington, PA-NJ-DE-MD, USA" 
# [9] "Philadelphia Metropolitan Area, USA"    "Pennsylvania, USA"         
# [11] "United States" 
Cuestiones relacionadas