Showing posts with label Nokia. Show all posts
Showing posts with label Nokia. Show all posts

Thursday, November 6, 2014

geocodeHERE 0.1 is on CRAN

In my previous blog post, I detailed how I created my first R package called geocodeHERE. This package is a convenient wrapper for Nokia's HERE geocoding API. The cool thing about this API is that it allows for bulk geocoding. So, instead of doing n API calls to geocode n addresses, you can do it with just a couple API calls. Also, you can run 10,000 API calls per day vs. Google's 2,500.

Any how, I went through the process to submit the package to CRAN and it was accepted. It took me 3 attempts, but I did it and it should be replicated across the mirrors by now. You can check it out on CRAN here.

Here's how it works, starting with downloading and installing the package (you'll need the httr package installed)…

install.packages("geocodeHERE", repo="http://cran.rstudio.com",
                 dependencies=TRUE)  
## Installing package into ‘/home/c/R/x86_64-pc-linux-gnu-library/3.1’
## (as ‘lib’ is unspecified)
## trying URL 'http://cran.rstudio.com/src/contrib/geocodeHERE_0.1.tar.gz'
## ...
## * DONE (geocodeHERE)

Now, we can try to run some simple queries. Given an address or place name, it will return the latitude, longitude, or NA if it can't find anything

library(geocodeHERE)  
geocodeHERE_simple("wrigley field chicago IL")
## $Latitude
## [1] 41.95
## 
## $Longitude
## [1] -87.65
geocodeHERE_simple("navy pier chicago IL")
## $Latitude
## [1] 41.94
## 
## $Longitude
## [1] -87.7
geocodeHERE_simple("the bean chicago IL")
## [1] NA

That's kinda boring though. There exists functions in other packages to do this same thing, namely ggmap::geocode(). What's interesting about Nokia HERE's API is that you can do gecoding of many addresses in bulk.

data(chicago_landmarks)
addresses <- chicago_landmarks[,"Address"]
# tack on "chicago IL" to the end of these addresses
addresses <- paste(addresses, "chicago IL")
# make a dataframe with an id column so you can match the lat, lngs back
addresses_df <- data.frame(id=1:length(addresses), addresses=addresses)
str(addresses_df)
## 'data.frame':    375 obs. of  2 variables:
##  $ id       : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ addresses: Factor w/ 374 levels "1001 W. Belmont Avenue chicago IL",..: 127 149 160 161 345 290 312 206 318 203 ...
address_str <- df_to_string(addresses_df)
request_id <- geocodeHERE_batch_upload(address_string = address_str,
                                       email_address = "test@test.com")
# wait about 15 seconds... status should go from "running" to "completed"
Sys.sleep(15)
geocodeHERE_batch_status(request_id)
## [1] "completed"
# download the data
geocode_data <- geocodeHERE_batch_get_data(request_id)
# match it back to your original addresses dataframe
addresses_df <- merge(addresses_df, geocode_data, by.x="id", by.y="recId", all.x=T)
str(addresses_df)
## 'data.frame':    375 obs. of  16 variables:
##  $ id              : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ addresses       : Factor w/ 374 levels "1001 W. Belmont Avenue chicago IL",..: 127 149 160 161 345 290 312 206 318 203 ...
##  $ SeqNumber       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ seqLength       : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ displayLatitude : num  41.9 41.9 41.9 41.9 41.8 ...
##  $ displayLongitude: num  -87.6 -87.6 -87.6 -87.6 -87.6 ...
##  $ houseNumber     : int  300 333 35 3600 NA 6901 860 4605 9326 4550 ...
##  $ street          : chr  "W Adams St" "N Michigan Ave" "E Wacker Dr" "N Halsted St" ...
##  $ district        : chr  "Loop" "Loop" "Loop" "Lake View" ...
##  $ city            : chr  "Chicago" "Chicago" "Chicago" "Chicago" ...
##  $ postalCode      : int  60606 60601 60601 60613 60637 60649 60611 60640 60643 60640 ...
##  $ county          : chr  "Cook" "Cook" "Cook" "Cook" ...
##  $ state           : chr  "IL" "IL" "IL" "IL" ...
##  $ country         : chr  "USA" "USA" "USA" "USA" ...
##  $ matchLevel      : chr  "houseNumber" "houseNumber" "houseNumber" "houseNumber" ...
##  $ relevance       : num  1 1 1 1 0.63 1 0.99 1 1 1 ...

All of the previous commands were done under the DEMO license keys. If you are going to use this a lot, it's probably best to get your own API keys. You can do that here.

If you have any questions, hit me up on twitter or shoot me an email at corynissen_AT_gmail.com

Thursday, October 23, 2014

Making an R Package to use the HERE geocode API

HERE is a product by Nokia, formerly called Nokia maps and before that, Ovi maps. It's the result of the acquisition of NAVTEQ in 2007 combined with Plazes and Metacarta, among others. It has a geocoding API, mapping tiles, routing services, and other things. I'm focused on the geocoding service. Under the “Base” license, you can run 10,000 geocoding requests per day. According to wikipedia, that is the most among the free geocoding services. On top of that, HERE does bulk encoding where you submit a file of things to geocode and it returns a link to download a file of results. This sure beats making thousands of requests one at a time.

I figured coding up a quick function to use this service would be the perfect reason to build my first R package. So, after getting my HERE API keys, I fired up devtools and got started with the R package.

First, I had to install the latest version of devtools and roxygen2

install.packages("devtools", repos="http://cran.rstudio.com/")
install.packages("roxygen2", repos="http://cran.rstudio.com/")
library(devtools)
library(roxygen2)

Next, I chose a directory, and ran the create() function from devtools. This creates a package skeleton that contains the basic structure needed for an R package.

devtools::create("geocodeHERE")

Several files and directories are created after running create(). I moved over to the 'R' directory and created a file called “geocodeHERE_simple.R”. I put my function to use the HERE geocoding API in there. This function was designed to be minimalist and similar to the ggmap geocode() function.

#' Attempt to geocode a string
#'
#' Enter a string and have latitude and longitude returned using the HERE API
#' @param search A string to search
#' @param App_id App_id to use the production HERE API. Get one here... http://developer.here.com/get-started. If left blank, will default to demo key with an unknown usage limit.
#' @param App_code App_code to use the production HERE API. Get one here... http://developer.here.com/get-started. If left blank, will default to demo key with an unknown usage limit.
#' @keywords geocode
#' @export
#' @examples
#' \dontrun{
#' geocodeHERE_simple("chicago")
#' geocodeHERE_simple("wrigley field chicago IL")
#' geocodeHERE_simple("233 S Wacker Dr, Chicago, IL 60606")
#' }
#' geocodeHERE_simple
geocodeHERE_simple <- function(search, App_id="", App_code=""){
  if(!is.character(search)){stop("'search' must be a character string")}
  if(!is.character(App_id)){stop("'App_id' must be a character string")}
  if(!is.character(App_code)){stop("'App_code' must be a character string")}

  if(App_id=="" & App_code==""){
    App_id <- "DemoAppId01082013GAL"
    App_code <- "AJKnXv84fjrb0KIHawS0Tg"
    base_url <- "http://geocoder.cit.api.here.com/6.2/geocode."
  }else{
    base_url <- "http://geocoder.api.here.com/6.2/geocode."
  }

  search <- RCurl::curlEscape(search)

  final_url <- paste0(base_url, format, "?app_id=", ids$App_id, "&app_code=",
                      ids$App_code, "&searchtext=", search)

  response <- RCurl::getURL(final_url)
  response_parsed <- RJSONIO::fromJSON(response)
  if(length(response_parsed$Response$View) > 0){
    ret <- response_parsed$Response$View[[1]]$Result[[1]]$Location$DisplayPosition
  }else{
    ret <- NA
  }
  return(ret)
}

Note the text on the top of the code. That is added in order to automatically create help documentation for the function.

Now, if you look closely, I am using two other packages in that function… RCurl and RJSONIO. Also, notice that I'm not making any library() calls. This must be done in the DESCRIPTION file that is automatically generated by create(). In addition to calling out what packages to import, you input the package name, author, description, etc.

Package: geocodeHERE
Title: Wrapper for the HERE geocoding API
Version: 0.1
Authors@R: "Cory Nissen <corynissen@gmail.com> [aut, cre]"
Description: Wrapper for the HERE geocoding API
Depends:
    R (>= 3.1.1)
License: MIT
LazyData: true
Imports:
    RJSONIO,
    RCurl

Then, I set my working directory to the package root and ran the document() function from devtools that automatically creates package documentation.

setwd("geocodeHERE")
document()

From this point, you can upload your package to github and use install_github(), or use the install() function to install your package locally.

setwd("..")
install()

As far as the HERE geocoding API goes, I find it pretty decent at doing “fuzzy matching”. That is, given a place name instead of an address, it does a good job of correctly returning the coordinates. The downside is that you must register for an API key, where Google does not require registration to use it's geocoding service. But, you only get 2500 requests per day via Google, HERE offers 10,000 per day.

Any how, a big shout out to Hilary Parker for her blog post on creating an R package using devtools, Hadley Wickham for the devtools package (among others), and RStudio for the fantastic and free (and open source) IDE for R.

The geocodeHERE package is available on github. I'll be adding bulk geocoding functionality as time permits. You can install the package with the following code:

devtools::install_github("corynissen/geocodeHERE")
library(geocodeHERE)
geocodeHERE_simple("wrigley field chicago il")