Convert latitude and longitude coordinates to country name in R
Asked Answered
F

2

27

I have a list of latitude and longitude coordinates, and wish to find out which country they all reside in.

I modified an answer from this question about lat-long to US states, and have a working function, but I run into the problem that the worldHires map (from the mapdata package) is hideously out of date and contains a lot of obsolete countries such as Yugoslavia and the USSR.

How would I modify this function to use a more modern package, such as rworldmap? I have only managed to frustrate myself so far...

library(sp)
library(maps)
library(rgeos)
library(maptools)

# The single argument to this function, points, is a data.frame in which:
#   - column 1 contains the longitude in degrees
#   - column 2 contains the latitude in degrees
coords2country = function(points)
{
    # prepare a SpatialPolygons object with one poly per country
    countries = map('worldHires', fill=TRUE, col="transparent", plot=FALSE)
    names = sapply(strsplit(countries$names, ":"), function(x) x[1])

    # clean up polygons that are out of bounds
    filter = countries$x < -180 & !is.na(countries$x)
    countries$x[filter] = -180

    filter = countries$x > 180 & !is.na(countries$x)
    countries$x[filter] = 180

    countriesSP = map2SpatialPolygons(countries, IDs=ids, proj4string=CRS("+proj=longlat +datum=wgs84"))


    # convert our list of points to a SpatialPoints object
    pointsSP = SpatialPoints(points, proj4string=CRS("+proj=longlat +datum=wgs84"))


    # use 'over' to get indices of the Polygons object containing each point 
    indices = over(pointsSP, countriesSP)


    # Return the state names of the Polygons object containing each point
    myNames = sapply(countriesSP@polygons, function(x) x@ID)
    myNames[indices]
}

##
## this works... but it has obsolete countries in it
## 

# set up some points to test
points = data.frame(lon=c(0, 5, 10, 15, 20), lat=c(51.5, 50, 48.5, 47, 44.5))

# plot them on a map
map("worldHires", xlim=c(-10, 30), ylim=c(30, 60))
points(points$lon, points$lat, col="red")

# get a list of country names
coords2country(points)
# returns [1] "UK"         "Belgium"    "Germany"    "Austria"    "Yugoslavia"
# number 5 should probably be in Serbia...
Farhi answered 15/1, 2013 at 9:49 Comment(1)
The maps package is now updated with new countries. No more USSR or Yugoslavia, etc.Unblown
B
40

Thanks for the carefully constructed question. It required just a couple of line changes to be able to use rworldmap (containing up-to-date countries) see below. I'm not an expert on CRS but I don't think the change I had to make to the proj4string makes any difference. Others might like to comment on that.

This worked for me & gave :

> coords2country(points)
[1] United Kingdom     Belgium            Germany            Austria           
[5] Republic of Serbia

All the best, Andy

library(sp)
library(rworldmap)

# The single argument to this function, points, is a data.frame in which:
#   - column 1 contains the longitude in degrees
#   - column 2 contains the latitude in degrees
coords2country = function(points)
{  
  countriesSP <- getMap(resolution='low')
  #countriesSP <- getMap(resolution='high') #you could use high res map from rworldxtra if you were concerned about detail

  # convert our list of points to a SpatialPoints object

  # pointsSP = SpatialPoints(points, proj4string=CRS(" +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +towgs84=0,0,0"))

  #setting CRS directly to that from rworldmap
  pointsSP = SpatialPoints(points, proj4string=CRS(proj4string(countriesSP)))  


  # use 'over' to get indices of the Polygons object containing each point 
  indices = over(pointsSP, countriesSP)

  # return the ADMIN names of each country
  indices$ADMIN  
  #indices$ISO3 # returns the ISO3 code 
  #indices$continent   # returns the continent (6 continent model)
  #indices$REGION   # returns the continent (7 continent model)
}
Bethezel answered 15/1, 2013 at 16:22 Comment(5)
When I try to use this function, I get identicalCRS(x, y) is not TRUEPrecast
That should be fixed by this edit : pointsSP = SpatialPoints(points, proj4string=CRS(proj4string(countriesSP)))Bethezel
@Bethezel could this be modified to extract a continent? and I also got some NA's when I ran this, e.g. (53.225516,-4.132845,NA) (41.524314,-70.669578,NA) - do you know why?Flinger
@GriffinEvo I've added 2 lines at end to show how the function can be modified to return continent. Regarding the returned NAs, I think they are in the ocean. To check : library(rworldmap) plot(getMap()) points(53.225516,-4.132845,col='green')Bethezel
@Bethezel to resolve the problem with NAs, is it possible to use polygons rather than points to resolve the continent?Kehoe
U
11

You can use my geonames package to lookup from the http://geonames.org/ service:

> GNcountryCode(51.5,0)
$languages
[1] "en-GB,cy-GB,gd"

$distance
[1] "0"

$countryName
[1] "United Kingdom of Great Britain and Northern Ireland"

$countryCode
[1] "GB"

> GNcountryCode(44.5,20)
$languages
[1] "sr,hu,bs,rom"

$distance
[1] "0"

$countryName
[1] "Serbia"

$countryCode
[1] "RS"

Get it from r-forge because I'm not sure I bothered to release it to CRAN:

https://r-forge.r-project.org/projects/geonames/

Yes, it depends on an external service, but at least it knows what happened to communism... :)

Urochrome answered 15/1, 2013 at 10:36 Comment(2)
it seems the API requires a username built into each call, but the GNcountryCode function doesn't allow for a username. Will you be adjusting the API calls? @UrochromePinnace
and for large data set, this may not be the ideal (=local) solution.Nonsuch

© 2022 - 2024 — McMap. All rights reserved.