Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Latitude Longitude Coordinates to State Code in R

Tags:

Is there a fast way to convert latitude and longitude coordinates to State codes in R? I've been using the zipcode package as a look up table but it's too slow when I'm querying lots of lat/long values

If not in R is there any way to do this using google geocoder or any other type of fast querying service?

Thanks!

like image 643
Michael Avatar asked Jan 05 '12 23:01

Michael


People also ask

Can you plot coordinates on Google Maps?

Enter coordinates to find a place On your computer, open Google Maps. In the search box, enter your coordinates. Here are examples of formats that work: Decimal degrees (DD): 41.40338, 2.17403.


1 Answers

Here are two options, one using sf and one using sp package functions. sf is the more modern (and, here in 2020, recommended) package for analyzing spatial data, but in case it's still useful, I am leaving my original 2012 answer showing how to do this with sp-related functions.


Method 1 (using sf):

library(sf) library(spData)  ## pointsDF: A data.frame whose first column contains longitudes and ##           whose second column contains latitudes. ## ## states:   An sf MULTIPOLYGON object with 50 states plus DC. ## ## name_col: Name of a column in `states` that supplies the states' ##           names. lonlat_to_state <- function(pointsDF,                             states = spData::us_states,                             name_col = "NAME") {     ## Convert points data.frame to an sf POINTS object     pts <- st_as_sf(pointsDF, coords = 1:2, crs = 4326)      ## Transform spatial data to some planar coordinate system     ## (e.g. Web Mercator) as required for geometric operations     states <- st_transform(states, crs = 3857)     pts <- st_transform(pts, crs = 3857)      ## Find names of state (if any) intersected by each point     state_names <- states[[name_col]]     ii <- as.integer(st_intersects(pts, states))     state_names[ii] }  ## Test the function with points in Wisconsin, Oregon, and France testPoints <- data.frame(x = c(-90, -120, 0), y = c(44, 44, 44)) lonlat_to_state(testPoints) ## [1] "Wisconsin" "Oregon"    NA 

If you need higher resolution state boundaries, read in your own vector data as an sf object using sf::st_read() or by some other means. One nice option is to install the rnaturalearth package and use it to load a state vector layer from rnaturalearthhires. Then use the lonlat_to_state() function we just defined as shown here:

library(rnaturalearth) us_states_ne <- ne_states(country = "United States of America",                           returnclass = "sf") lonlat_to_state(testPoints, states = us_states_ne, name_col = "name") ## [1] "Wisconsin" "Oregon"    NA          

For very accurate results, you can download a geopackage containing GADM-maintained administrative borders for the United States from this page. Then, load the state boundary data and use them like this:

USA_gadm <- st_read(dsn = "gadm36_USA.gpkg", layer = "gadm36_USA_1") lonlat_to_state(testPoints, states = USA_gadm, name_col = "NAME_1") ## [1] "Wisconsin" "Oregon"    NA         

Method 2 (using sp):

Here is a function that takes a data.frame of lat-longs within the lower 48 states, and for each point, returns the state in which it is located.

Most of the function simply prepares the SpatialPoints and SpatialPolygons objects needed by the over() function in the sp package, which does the real heavy lifting of calculating the 'intersection' of points and polygons:

library(sp) library(maps) library(maptools)  # The single argument to this function, pointsDF, is a data.frame in which: #   - column 1 contains the longitude in degrees (negative in the US) #   - column 2 contains the latitude in degrees  lonlat_to_state_sp <- function(pointsDF) {     # Prepare SpatialPolygons object with one SpatialPolygon     # per state (plus DC, minus HI & AK)     states <- map('state', fill=TRUE, col="transparent", plot=FALSE)     IDs <- sapply(strsplit(states$names, ":"), function(x) x[1])     states_sp <- map2SpatialPolygons(states, IDs=IDs,                      proj4string=CRS("+proj=longlat +datum=WGS84"))      # Convert pointsDF to a SpatialPoints object      pointsSP <- SpatialPoints(pointsDF,                      proj4string=CRS("+proj=longlat +datum=WGS84"))      # Use 'over' to get _indices_ of the Polygons object containing each point          indices <- over(pointsSP, states_sp)      # Return the state names of the Polygons object containing each point     stateNames <- sapply(states_sp@polygons, function(x) x@ID)     stateNames[indices] }  # Test the function using points in Wisconsin and Oregon. testPoints <- data.frame(x = c(-90, -120), y = c(44, 44))  lonlat_to_state_sp(testPoints) [1] "wisconsin" "oregon" # IT WORKS 
like image 139
Josh O'Brien Avatar answered Oct 07 '22 09:10

Josh O'Brien