Why is plot of GADM SpatialPolygonsDataFrame not loading in R? - r

One-liner: R is taking too long to plot a SpatialPolygonsDataFrame and the best answer I've found online is that the issue is machine-specific.
Problem: I am trying to plot a SpatialPolygonsDataFrame of India (level = 1) that I downloaded from GADM but no plotting function (spplot, plot, tm_shape) is producing a plot in any given time; instead R appears to do work in the background but 5+ minutes later there still is no plot. Pressing the stop button that appears above the console does nothing and eventually in order to re-try some other method I have to force-quit the app. My question is similar to this question but the consensus on that question was that the issue appears machine specific with no solution thereafter.
Here's some info about my machine/R studio version:
R version 3.3.2 (2016-10-31) / R Studio Version 1.0.136 / OS: MacOS Sierra version 10.12.6.
The file was downloaded from: GADM in RDS format
(1.) I've tried different plotting mechanisms
setwd("~/Data/Reference/")
india <- readRDS("IND_adm1.rds")
library(sp)
library(tmap)
# Plot method #1
plot(india)
# Plot method #2
spplot(india, "NAME_1")
# Plot method #3
tm_shape(india) + tm_borders()
(2.) I've tried recreating the SpatialPolygonsDataFrame from a shapefile instead of the RDS
india <- readOGR(dsn = "~/Data/Reference/" , layer = "IND_adm1")
(3.) I've updated all packages on the machine and uninstalled and re-installed the sp package and the tmap package.
(4.) I've tried other countries' file and had the same issue.
(5.) I've examined the structure of the SpatialPolygonsDataFrame and found no issues with it -- things seem to be in place and in the right way.
(6.) Using maps package to plot their data works fine. For some reason SpatialPolygonsDataFrame classes are not plotting. I've now also tried to plot the shape files from DIVA-GIS.
library(maps)
tx <- map("county", "texas", plot = FALSE, fill = TRUE)
plot(tx) # this plots fine and immediately
Requirement I basically need a state boundary map of India which I can put into a Shiny interactive RMarkdown report and fill state colors based on a factor variable. Is there some work-around for the data that I can use?
Ask: I'm not sure what to do or how to further diagnose. I'd appreciate any help that I can get and happy to provide any more info if that would be helpful. I'm also linking the exact RDS file that I'm using for you to load and try on your machine.
Update
None of the suggested solutions worked on my system but I think this challenge I was having was systems specific. I ultimately tried running the same code on a remote Windows instance on Amazon web services and there the code worked fine. So I am not sure whether it was a Mac issue or a hardware issue, but I hope this update is helpful.

I've had this problem on my mac for a while and when searching for solutions, found this on R Bloggers. The code simplifies a shapefile enabling it to plot much more easily:
library("shapefiles")
map = readOGR(dsn = "shp", layer = "shp")
for(i in 1:length(map#polygons)){
for(j in 1:length(map#polygons[[i]]#Polygons)){
temp <- as.data.frame(map#polygons[[i]]#Polygons[[j]]#coords)
names(temp) <- c("x", "y")
temp2 <- dp(temp, 0.01)
map#polygons[[i]]#Polygons[[j]]#coords <- as.matrix(cbind(temp2$x, temp2$y))
}
}
plot(map, col = "black")
(http://www.r-bloggers.com/simplifying-polygon-shapefiles-in-r/).
However, although this can help the plotting process, it becomes troublesome to try and perform GIS operations on the new shapefile due to its simplified form.
It is worth looking for country shapefiles from sites other than GADM. For example, all the shapefiles I have used from this site plot effortlessly and instantly on my mac: http://mapeastafrica.com/countries/east-africa-shapefiles/.

Related

Why is the legend distorted on my map using tmap?

I'm following an example from Geocomputation with R in Chapter 4, section 4.2.6. In the example from the book, the map of New Zealand that has the average elevation in the polygons/regions has a nice, compact legend that is easy to read and placed automatically in the upper left corner. See the image output from the book below.
Here is the link to the section of the book: https://geocompr.robinlovelace.net/spatial-operations.html
When I execute the same code to try to duplicate this map, the image that is produced for me has a very distorted legend with text that is also small. I've tried adjusting my Plots pane, exporting the image in multiple formats, etc., and the legend is still distorted like this.
The code below is what I am executing:
library(sf)
library(spData)
library(tmap)
# Summarize nz regions by average elevation
nz_agg = aggregate(x = nz_height, by = nz, FUN = mean)
# Map nz regions colored by average elevation
tm_shape(nz) +
tm_polygons() +
tm_shape(nz_agg) +
tm_polygons(col = "elevation")
A couple months ago, when I produced tmaps with legends, I wasn't having this issue. I don't know if some setting has been changed or updated that I don't know about. The version of R that I'm using is 4.1.2 (2021-11-01) -- "Bird Hippie".
So, it wasn't a problem with my R version or the versions of the R packages I am using.
I use a laptop and have a couple of different locations where I use it for work. One location has multiple monitors; the other does not. I didn't notice it until researching this further, but when I plug in/unplug my monitors, sometimes it changes the resolution settings on my computer. Apparently, it wasn't enough for me to notice on my laptop until attempting to run this code. So, the cause of the distortion was due to changes on my computer that I was unaware of that happened automatically. I'll have to keep an eye on this.

Convert Spatial Lines to Spatial Polygons

Is there an easy way to convert a Spatial Lines into a Spatial Polygon object within R?
Reproducible Example
I have put together a reusable dataset here, which is downloaded from OpenStreetMaps through the overpass package. This extracts the locations of a few airports in South England:
devtools::install_github("hrbrmstr/overpass")
library(overpass)
library(raster)
library(sp)
# Write Query
query_airport <- '
(node["aeroway"="aerodrome"](50.8, -1.6,51.1, -1.1);
way["aeroway"="aerodrome"](50.8, -1.6,51.1, -1.1);
relation["aeroway"="aerodrome"](50.8, -1.6,51.1, -1.1);
);
out body;
>;
out skel qt;
'
# Run query
shp_airports <- overpass::overpass_query(query_airport, quiet = TRUE)
crs(shp_airports) <- CRS("+init=epsg:4326") # Add coordinates
shp_airports <- shp_airports[,1]
# Plot Results
plot(shp_airports, axes = T)
However, the data is of the class "SpatialLinesDataFrame". This really messes things up if you want to do any form of spatial joins or intersections, as it only acknowledges the edge of the region.
Potential Leads
I was exploring the use of SpatialLines2PolySet within the maptools package, but in my time exploring I produced nothing but error codes, so I didn't think there would be any worth including these within the question. There is some guidance about these functions here: https://rdrr.io/rforge/maptools/man/SpatialLines2PolySet.html
Notes
I have searched the web and SO to see find similar questions and struggled to find any questions directly referring to this. A lot seem to reference converting SpatialPoints -> SpatialLineDataFrames , but not SpatialLineDataFrames -> SpatialPolygonDataFrames. This question is similar but lacks any answers (or a reproducible dataset): Close a spatial line into a polygon using a shapefile
In addition, it seems strange that this would be difficult as it is something which can be done so easily in ArcGIS using the "Feature to Polygon" tool. This function requires no additional arguments specified and it works perfectly.
A way to solve the problem would be to use the library sf. After your query
library(sp)
library(raster)
library(sf)
sf_airports <- st_as_sf(shp_airports)
sf_airports_polygons <- st_polygonize(sf_airports)
shp_airports <- as(sf_airports_polygons, "Spatial") # If you want sp
class(shp_airports)

Autokriging spatial data

I'm trying to use a kriging function to create vertical maps of chemical parameters in an ocean transect, and I'm having a hard time getting started.
My data look like this:
horiz=rep(1:5, 5)
depth=runif(25)
value = horiz+runif(25)/5
df <- data.frame(horiz, depth, value)
The autoKrige function in the automap package looks like it should do the job for me but it takes an object of class SpatialPointsDataFrame. As far as I can tell, the function spTransform in package rgdal creates SpatialPointsDataFrame objects, but there are two problems:
OSX binaries of this aren't available from CRAN, and my copy of RStudio running on OXS 10.7 doesn't seem to be able to install it, and
This function seems to work on lat/long data and correct distance values for the curvature of the Earth. Since I'm dealing with a vertical plane (and short distances, scale of hundreds of meters) I don't want to correct my distances.
There's an excellent discussion of kriging in R here, but due to the issues listed above I don't quite understand how to apply it to my specific problem.
I want a matrix or dataframe describing a grid of points with interpolated values for my chemical parameters, which I can then plot (ideally using ggplot2). I suspect that the solution to my problem is considerably easier than I'm making it out to be.
So there a a few question you want answered:
The spTransform function does not create SPDF's, but transforms between projections. To create a SPDF you can use a simple data.frame as a start. To transform df to a SPDF:
coordinates(df) = c("horiz", "depth")
OS X binaries of rgdal can be found at http://www.kyngchaos.com. But I doubt if you need rgdal.
spTransform can operate on latlong data, but also on projected data. But I do not think you need rgdal, or spTransform, see also point 1.
After you create the SPDF using point 1, you can use the info at the post you mentioned to go on.

Creating x and y distance coordinates for R from a .kml file

I would like to use a .kml track file to make a set of x, y coordinates for use in R.
What I have right now is a GoogleEarth track, which I believe is a LineString. I have heard that the rgdal package is usually what people use, but it doesn't work on Mac versions of R. If possible, I'd like to do this on a Mac, where I do the rest of my analyses. If necessary, I can do the conversion on R64 with Windows, and then bring the coordinates to my Mac, but that seems...clunky.
The beginning of the .kml code looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>Perimeter_Track.kml</name>
<Placemark>
<name>ACTIVE LOG</name>
<LineString>
<coordinates>
-157.80736808,21.4323436,20.324951171875
I want to convert it into x , y coordinates in kilometers from a point in my map. The finished product will be a line outline of a body of water, with species abundance data overlaid on it.
I have tried a couple of methods already:
1. Converting the .kml file into a .csv and importing it to r using read.csv;
2. Importing coordinates using getKMLcoordinates in the maptools package.
The problem with (1) is that when I try to convert the .kml coords into csv, I get an error in the converter program (kmlcsv) that says it can't read the file (I'm not sure why- the error logs aren't available).
When I try (2), I get coordinates that are arranged weirdly.
spa<-getKMLcoordinates("Perimeter_Track.kml", ignoreAltitude=TRUE)
summary(spa) returns:
Length Class Mode
[1,] 128 -none- numeric
[2,] 242 -none- numeric
[3,] 34 -none- numeric
[4,] 126 -none- numeric
I believe this is because the .kml file is actually four separate tracks, separated by small gaps (i.e., where they turned the GPS off for a short time, then started again). Do I need to import these all separately in order to get the whole shape? If so, how do I do this?
I would like, eventually, to get this shape on a grid that is x by y km, where the coordinates are in km instead of GPS coords. If anyone has any insight into how to do this, I would love to hear from you!
Thanks very much in advance.
Even though a precompiled package isn't available, you can still install rgdal from its source on a mac like follows:
Install the "GDAL complete" framework from http://www.kyngchaos.com/software/frameworks.
Add the locations of the programs you just installed to your unix path. In mac terminal, do:
PATH=/Library/Frameworks/GDAL.framework/unix/bin:/Library/Frameworks/PROJ.framework/unix/bin$PATH
Download the source for the rgdal package from CRAN at http://cran.r-project.org/web/packages/rgdal/index.html.
Open R and build/install the rgdal package. Note that we have to specify the locations to some of the stuff we just installed.
install.packages('~/Downloads/rgdal_0.7-1.tar.gz', repos=NULL, type='source', configure.args=c('--with-proj-include=/Library/Frameworks/PROJ.framework/unix/include', '--with-proj-lib=/Library/Frameworks/PROJ.framework/unix/lib'))
This installs fine on my Mac OS X 10.6. Good luck!
So the basic idea with your data might be:
library(rgdal)
library(maptools)
# Load KML coordinates
coords = getKMLcoordinates('data.kml')
coords = SpatialPoints(coords, CRS('+proj=longlat'))
# Load US Maps (get from www.gadm.org)
load('USA_adm1.RData')
hawaii = gadm[gadm$NAME_1 == 'Hawaii', ]
# Transform coordinates
hawaii.proj = spTransform(hawaii, CRS=CRS('+init=epsg:2784 +units=km'))
coords.proj = spTransform(coords, CRS=CRS('+init=epsg:2784 +units=km'))
# Plot
dev.new(width=4, height=4)
plot(hawaii.proj, axes=T, xlim=c(450,550), ylim=c(0,60))
points(coords.proj, pch=16, col='red')
Great place to live!
Once you've read something into an "sp" class object (probably a SpatialLinesDataFrame here) using readOGR from rgdal, you can transform it to a cartesian system from lat-long with the spTransform function.
Which system to transform it into depends on where on the earth it is. There's a bunch of standard transforms that depend on longitude called 'UTM' zones (Universal Transverse Mercator). Simply look up the zone for your longitude, find the EPSG code, and fire up spTransform.
For the UK, there's a standard grid system called the Ordnance Survey Grid, which has an EPSG code of 27700. So to transform something in lat-long (EPSG:4326) to OSGB metres, I do:
mapOS = spTransform(mapLL,CRS=CRS("+init=epsg:27700"))
There's lots of examples in the help for spTransform.
Note this is all great only if your data are on a small part of the earth...

Developing Geographic Thematic Maps with R

There are clearly a number of packages in R for all sorts of spatial analysis. That can by seen in the CRAN Task View: Analysis of Spatial Data. These packages are numerous and diverse, but all I want to do is some simple thematic maps. I have data with county and state FIPS codes and I have ESRI shape files of county and state boundaries and the accompanying FIPS codes which allows joining with the data. The shape files could be easily converted to other formats, if needed.
So what's the most straight forward way to create thematic maps with R?
This map looks like it was created with an ESRI Arc product, but this is the type of thing I would like to do with R:
alt text http://www.infousagov.com/images/choro.jpg Map copied from here.
The following code has served me well. Customize it a little and you are done.
(source: eduardoleoni.com)
library(maptools)
substitute your shapefiles here
state.map <- readShapeSpatial("BRASIL.shp")
counties.map <- readShapeSpatial("55mu2500gsd.shp")
## this is the variable we will be plotting
counties.map#data$noise <- rnorm(nrow(counties.map#data))
heatmap function
plot.heat <- function(counties.map,state.map,z,title=NULL,breaks=NULL,reverse=FALSE,cex.legend=1,bw=.2,col.vec=NULL,plot.legend=TRUE) {
##Break down the value variable
if (is.null(breaks)) {
breaks=
seq(
floor(min(counties.map#data[,z],na.rm=TRUE)*10)/10
,
ceiling(max(counties.map#data[,z],na.rm=TRUE)*10)/10
,.1)
}
counties.map#data$zCat <- cut(counties.map#data[,z],breaks,include.lowest=TRUE)
cutpoints <- levels(counties.map#data$zCat)
if (is.null(col.vec)) col.vec <- heat.colors(length(levels(counties.map#data$zCat)))
if (reverse) {
cutpointsColors <- rev(col.vec)
} else {
cutpointsColors <- col.vec
}
levels(counties.map#data$zCat) <- cutpointsColors
plot(counties.map,border=gray(.8), lwd=bw,axes = FALSE, las = 1,col=as.character(counties.map#data$zCat))
if (!is.null(state.map)) {
plot(state.map,add=TRUE,lwd=1)
}
##with(counties.map.c,text(x,y,name,cex=0.75))
if (plot.legend) legend("bottomleft", cutpoints, fill = cutpointsColors,bty="n",title=title,cex=cex.legend)
##title("Cartogram")
}
plot it
plot.heat(counties.map,state.map,z="noise",breaks=c(-Inf,-2,-1,0,1,2,Inf))
Thought I would add some new information here since there has been some activity around this topic since the posting. Here are two great links to "Choropleth Map R Challenge" on the Revolutions blog:
Choropleth Map R Challenge
Choropleth Challenge Results
Hopefully these are useful for people viewing this question.
All the best,
Jay
Check out the packages
library(sp)
library(rgdal)
which are nice for geodata, and
library(RColorBrewer)
is useful for colouring. This map is made with the above packages and this code:
VegMap <- readOGR(".", "VegMapFile")
Veg9<-brewer.pal(9,'Set2')
spplot(VegMap, "Veg", col.regions=Veg9,
+at=c(0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5),
+main='Vegetation map')
"VegMapFile" is a shapefile and "Veg" is the variable displayed. Can probably be done better with a little work. I don`t seem to be allowed to upload image, here is an link to the image:
Take a look at the PBSmapping package (see borh the vignette/manual and the demo) and
this O'Reilly Data Mashups in R article (unfortunately it is not free of charge but it worth 4.99$ to download, according Revolutions blog ).
It is just three lines!
library(maps);
colors = floor(runif(63)*657);
map("state", col = colors, fill = T, resolution = 0)
Done!!
Just change the second line to any vector of 63 elements (each element between 0 and 657, which are members of colors())
Now if you want to get fancy you can write:
library(maps);
library(mapproj);
colors = floor(runif(63)*657);
map("state", col = colors, fill = T, projection = "polyconic", resolution = 0);
The 63 elements represent the 63 regions whose names you can get by running:
map("state")$names;
The R Graphics Gallery has a very similar map which should make for a good starting point. The code is here: www.ai.rug.nl/~hedderik/R/US2004 . You'd need to add a legend with the legend() function.
If you stumble upon this question in the 2020ies, use the magnificent tmap package. It's very simple and straightforward and revolutionized making maps in R. Do not bother to investigate this complicated code.
Check the vignette here.

Resources