diff --git a/R/geocode.R b/R/geocode.R index acafd46..dd46740 100644 --- a/R/geocode.R +++ b/R/geocode.R @@ -78,7 +78,7 @@ #' } #' geocode <- function(location, output = c("latlon", "latlona", "more", "all"), - source = c("google", "dsk"), messaging = FALSE, + source = c("google", "dsk", "osm"), messaging = FALSE, force = ifelse(source == "dsk", FALSE, TRUE), sensor = FALSE, override_limit = FALSE, client = "", signature = "", nameType = c("long", "short"), data @@ -167,13 +167,21 @@ geocode <- function(location, output = c("latlon", "latlona", "more", "all"), client4url <- paste("client=", client, sep = "") signature4url <- paste("signature=", signature, sep = "") location4url <- chartr(" ", "+", location) - posturl <- paste(location, sensor4url, sep = "&") - if(userType == "business") posturl <- paste(posturl, client4url, signature4url, sep = "&") + + # Slightly different format for OSM + if(source == "osm"){ + posturl <- paste(location, "?format=json", sep="") + } else{ + posturl <- paste(location, sensor4url, sep = "&") + } + if(userType == "business") posturl <- paste(posturl, client4url, signature4url, sep = "&") if(source == "google"){ url_string <- paste("http://maps.googleapis.com/maps/api/geocode/json?address=", posturl, sep = "") } else if(source == "dsk"){ url_string <- paste("http://www.datasciencetoolkit.org/maps/api/geocode/json?address=", posturl, sep = "") + } else if(source == "osm"){ + url_string <- paste("http://nominatim.openstreetmap.org/search/", posturl, sep="") } url_string <- URLencode(url_string) @@ -230,9 +238,16 @@ geocode <- function(location, output = c("latlon", "latlona", "more", "all"), # return if you want full output if(output == "all") return(gc) - + # Assign a "status" for OSM + # This is not part of the return + if(source == "osm" & length(gc) == 0){ + gc$status <- "OSM fail" + }else if(source == "osm"){ + gc$status <- "OK" + } # did geocode fail? - print(gc$status) + # Added in gc length test for OSM if(gc$status != "OK"){ warning(paste("geocode failed with status ", gc$status, ", location = \"", location, "\"", sep = ""), call. = FALSE) @@ -256,19 +271,40 @@ geocode <- function(location, output = c("latlon", "latlona", "more", "all"), x } - gcdf <- with(gc$results[[1]], { - data.frame( - lon = NULLtoNA(geometry$location$lng), - lat = NULLtoNA(geometry$location$lat), - type = tolower(NULLtoNA(types[1])), - loctype = tolower(NULLtoNA(geometry$location_type)), - address = location, # dsk doesn't give the address - north = NULLtoNA(geometry$viewport$northeast$lat), - south = NULLtoNA(geometry$viewport$southwest$lat), - east = NULLtoNA(geometry$viewport$northeast$lng), - west = NULLtoNA(geometry$viewport$southwest$lng) - ) - }) + # Homogenize data formats + if(source == "osm"){ + + gcdf <- with(gc[[1]], { + data.frame( + lon = NULLtoNA(lon), + lat = NULLtoNA(lat), + type = NULLtoNA(type), + loctype = NULLtoNA(class), + address = NULLtoNA(display_name), + north = NULLtoNA(as.numeric(boundingbox[2])), + south = NULLtoNA(as.numeric(boundingbox[1])), + west = NULLtoNA(as.numeric(boundingbox[3])), + east = NULLtoNA(as.numeric(boundingbox[4])) + ) + }) + + } else{ + + gcdf <- with(gc$results[[1]], { + data.frame( + lon = NULLtoNA(geometry$location$lng), + lat = NULLtoNA(geometry$location$lat), + type = tolower(NULLtoNA(types[1])), + loctype = tolower(NULLtoNA(geometry$location_type)), + address = location, # dsk doesn't give the address + north = NULLtoNA(geometry$viewport$northeast$lat), + south = NULLtoNA(geometry$viewport$southwest$lat), + east = NULLtoNA(geometry$viewport$northeast$lng), + west = NULLtoNA(geometry$viewport$southwest$lng) + ) + }) + + } diff --git a/R/revgeocode.R b/R/revgeocode.R index c92f84e..3bf25e2 100644 --- a/R/revgeocode.R +++ b/R/revgeocode.R @@ -18,6 +18,8 @@ #' @param signature signature for business users, see #' https://developers.google.com/maps/documentation/business/webservices/auth #' +#' @param source source to use for reverse geocoding +#' #' @return depends (at least an address) #' @details note that the google maps api limits to 2500 queries a #' day. @@ -39,14 +41,17 @@ #' revgeocode <- function(location, output = c('address','more','all'), messaging = FALSE, sensor = FALSE, override_limit = FALSE, - client = "", signature = "" + client = "", signature = "", source = c('google', 'osm') ){ # check parameters stopifnot(is.numeric(location) && length(location) == 2) output <- match.arg(output) + source <- match.arg(source) stopifnot(is.logical(messaging)) stopifnot(is.logical(sensor)) + + # Only relevant for google. Might need to add in a clause for OSM if(client != "" && signature != ""){ if(substr(client, 1, 4) != 'gme-') client <- paste("gme-", client, sep = "") userType <- "business" @@ -59,13 +64,25 @@ revgeocode <- function(location, output = c('address','more','all'), } # format url - loc4url <- paste(rev(location), collapse = ',') if(sensor){ sensor <- 'true' } else { sensor <- 'false' } sensor4url <- paste('&sensor=', sensor, sep = '') # includes & client4url <- paste('&client=', client, sep = '') signature4url <- paste('&signature=', signature, sep = '') - url_string <- paste("http://maps.googleapis.com/maps/api/geocode/json?latlng=", - loc4url, sensor4url, sep = "") + + if(source == "osm"){ + laturl <- paste('lat=', location[2], sep = "") + lonurl <- paste('&lon=', location[1], sep = "") + + loc4url <- paste(laturl, lonurl, sep="") + url_string <- paste("http://nominatim.openstreetmap.org/reverse?", loc4url, '&format=json', sep = "") + + }else{ + loc4url <- paste(rev(location), collapse = ',') + url_string <- paste("http://maps.googleapis.com/maps/api/geocode/json?latlng=", + loc4url, sensor4url, sep = "") + } + + if(userType == "business"){ url_string <- paste(url_string, client4url, signature4url, sep = "") } @@ -89,14 +106,23 @@ revgeocode <- function(location, output = c('address','more','all'), # geocode connect <- url(url_string) - rgc <- fromJSON(paste(readLines(connect), collapse = '')) + rgc <- fromJSON(paste(readLines(connect, warn = FALSE), collapse = '')) close(connect) if(output == 'all') return(rgc) # did geocode fail? - if(rgc$status != 'OK'){ + post_warning <- FALSE + if(source == "osm" & !is.null(rgc$error)){ + post_warning <- TRUE + } else if(source != "osm"){ + if(rgc$status != 'OK'){ + post_warning <- TRUE + } + } + + if(post_warning){ warning(paste('reverse geocode failed - bad location? location = "', - location, '"', sep = '')) + location, '"', sep = '')) return(data.frame(address = NA)) } @@ -109,18 +135,38 @@ revgeocode <- function(location, output = c('address','more','all'), '", reverse geocoding first...\n', sep = '')) } - # format - rgc <- rgc$results[[1]] + # Coerce OSM results to match Google + # Little "hacky" but minimizes code rewriting + if(source == "osm"){ + rgc$formatted_address <- rgc$display_name + }else{ + # format + rgc <- rgc$results[[1]] + } + + # If user wants just the address, return the address! if(output == 'address') return(rgc$formatted_address) with(rgc,{rgcdf <<- data.frame( address = formatted_address )}) - for(k in seq_along(rgc$address_components)){ - rgcdf <- cbind(rgcdf, rgc$address_components[[k]]$long_name) - } - names(rgcdf) <- c('address', sapply(rgc$address_components, function(l) l$types[1])) + # Requires a bit of special handling for OSM returns + if(source == "osm"){ + + rgcdf <- cbind(rgcdf, as.data.frame(rgc$address)) + + }else{ + + for(k in seq_along(rgc$address_components)){ + rgcdf <- cbind(rgcdf, rgc$address_components[[k]]$long_name) + } + + names(rgcdf) <- c('address', sapply(rgc$address_components, function(l) l$types[1])) + + } + # return 'more' output rgcdf + }