Label kml features using `st_write` - r

I'd like to export an sf object as a .kml file, with labels for each feature I'm interested in so I can view the data easily in Google Earth. I know you can click on the "info" button in Google Earth, but for hundreds of polygons, this isn't ideal.
For example, I'd like to label each polygon feature below using the column NAME. How can I modify the st_write call below to label the kml polygons so that they appear in the sidebar table of contents in Google Earth?
library(sf)
library(dplyr)
# sf includes this dataset
county_polygons <- st_read(system.file("shape/nc.shp", package="sf")) %>%
st_transform(4326)
st_write(county_polygons , "test.kml", driver = "kml")
Here's a picture showing the lack of labels in Google Earth when this is imported as a kml file:

Consider this code, using a different, although also well known & well loved dataset - the polygons of North Carolina counties from ns.shp shipped with {sf} package:
library(sf)
library(dplyr)
# dataset included with sf package
county_polygons <- st_read(system.file("shape/nc.shp", package="sf")) %>%
st_transform(4326) %>% # just because wgs84...
select(Description = NAME) # see https://gdal.org/drivers/vector/kml.html#creation-options
st_write(county_polygons, "test.kml", driver = "kml", delete_dsn = TRUE)
It is built around the feature of KML export of DescriptionField (which is clickable in Google Earth) defaulting to sf column named Description.
If you want the feature's name in the sidebar instead, you can replace the word Description with Name in the code above.

Related

Points not added to the map plot

I need to add some points to the map using simple points function. The issue is that points don't add to the map. It's simple command, I follow some tutorial where adding points to the map works this way but not in my case. Plot function plots Texas choropleth properly but next line (points) doesn't add points to the map at all:
library(rgdal)
library(rgeos)
library(sp)
companies <- read.csv('geoloc_data_comp.csv', header = T, dec = ',', sep = ';')
states <- readOGR('.', 'states')
plot(states[states#data$stat_name == 'texas',])
points(companies$coords.x1, companies$coords.x2, pch = 21)
First you shoud start to avoid rgeos/rgdal because they will stop being maintains. See : https://github.com/r-spatial/evolution
sf is replacing them:
library(sp)
library(sf)
library(spData) #used because I wanted US states
# list of data in spData you have one with US states
data(package = "spData")
if you want to read shapefile or other GIS format check sf::st_read() (instead of readOGR())
# one way with sf
plot(us_states$geometry[us_states$NAME == "Texas"])
# if you want do use the sp way
us_sp <- as(us_states, "Spatial") # convert to sp
plot(us_sp[us_sp#data$NAME == "Texas",])
with sf you have the geometry in one column (see "geometry") instead of having an R S4 with nested lists (see #data and #polygones).
Before getting some points we need to check in which CRS our data are. If you do not know CRS I like this website : https://ihatecoordinatesystems.com/
You also have information in the us_states documentation: https://www.rdocumentation.org/packages/spData/versions/2.0.1/topics/us_states
Then you can use:
sp::proj4string(us_sp)
sf::st_crs(us_states)
# This is EPSG 4269 or NAD83
If you want to use points() they need to be in this coordinates system (I suspect this explain your trouble ie different CRS).
You didn't provide data points so I produced some:
library(osmdata)
#this will just download node matching the key/value place=city
some_city_in_texas <- osmdata::opq(osmdata::getbb("Texas US"),
nodes_only = TRUE) %>%
osmdata::add_osm_feature(key = "place", value = "city") %>%
osmdata::osmdata_sf() #keep them in sf format
# osmdata_sp() also exist
The class osmdata is a bit complicated but here you just need to know that some_city_in_texas$osm_points provide us with points (to test points()). Now we can check their CRS:
sf::st_crs(some_city_in_texas$osm_points)
As you can see we are in an other CRS so we need to transform it. (you will probably need to do it).
city_in_texas <- sf::st_transform(some_city_in_texas$osm_points,
4269)
sf use simple feature standard to store localization and points() want two vectors x&y. You should also check that (common cause of error): R use x/y (long/lat) and not lat/long.
Here we convert city_in_texas to just coords. (if you need to do the reverse, ie converting data frame with X/Y, into an sf object look at sf::st_as_sf())
coords_city <- sf::st_coordinates(city_in_texas)
Finally this works fine now:
plot(us_states$geometry[us_states$NAME == "Texas"])
points(coords_city, pch = 21)
Good ressources are https://r-spatial.org/ and https://geocompr.robinlovelace.net/

Plot postcodes on London Map in R

I have a column of over 1,000 London full postcodes. I need to convert these postcodes into Latitudes and Longitudes and then plot each point onto a base map of London. I want to show the spatial distribution and concentration of my postcodes as dots on London.
I have attempted this exercise using ggmap, maps and mapdata but to no avail.
install.packages("ggmap")
install.packages(c("maps", "mapdata"))
library(ggmap)
library(mapdata)
library(tidyverse)
library(dplyr)
mapdata <- filter(mapdata, Country--'UK')
Error Message:
Error in as.ts(x) : object 'mapdata' not found
First, I found a shapefile layer of UK water here. You might wish to find a better one. It's best to download all your shapefiles in ONE GIS FOLDER for future use, including all the files that are zipped along with them.
Second, I used the rgdal package to open that shapefile using the readOGR command.
Third, I used the leaflet package to place that data on a map of London.
library(leaflet);library(rgdal)
setwd("/yourpathhere/") #update your path here
ukwater<-readOGR(dsn='.',layer='GBR_water_lines_dcw')
leaflet() %>%
addProviderTiles("Stamen.Toner") %>%
setView(0,51.4,zoom=9) %>% # longitude and latitute, respectively
addPolygons(data=ukwater)

R: Match Polygons from Shapefile 1 to Area Codes in shapefile 2

I was asked whether R can work with shapefiles - I never worked with shapefiles myself before, but I am sure, others must have come across this kind of question!
I have two shapefiles:
a) shapefile 1 (PolygonSamples.shp) contains a list of polygons which are distributed all over Germany (attached is a sample). The polygons might be smaller, equal or larger than the polygon of one postal codes polygon.
b) shapefile 2 lists the german postal codes and can be downloaded from
https://blog.oraylis.de/2010/05/german-map-spatial-data-for-plz-postal-code-regions/
The question is now:
How to 'match' the two shapefiles to get a dataframe that lists which polygon in shapefile 1 matches which postal codes(s) of shapefile 2. The result ideally looks like
Polygon ID (shapefile 1) Postal Code (shapefile 2)
1 80995
2 80997
2 80999
3 81247
Nothing of what I found matches really my question.
For example From a shapefile with polygons/areas, and points (lat,lon), figure out which polygon/area each point belongs to? In R
seems close, but I don't manage to get the desired dataframe (or datatable) output.
library(maps)
library(maptools)
# Polygons
tmp_dir <- "C:/Users/.../"
polygons <- readShapeSpatial(sprintf('%s/polygons.shp', tmp_dir)
plot(polygons)
# Postal codes
dir <- "C:/Users/..../"
postcode <- readShapeSpatial(sprintf('%s/post_pl.shp', dir)
plot(postcode)
The missing codes snipplet would read something like
result_table <- match(polygons_ID, postcode,
data1= polygon, data2 = postcode,
by = "coordinates in the shapefile"
Sample of polygons in a shapefile (.shp) incl. other spatial files (.dbf,.prj, .qpj,.shx) can be send.
Any help is really VERY much appreciated!
PS: R version 3.2.3, 64 bit, RStudio on Windows 7
Unfortunately I did not find an answer in R, but I could figure out how to match the two independent shapefiles in QGIS.
The main problem: The custom shapefile uses in the .prj file the geocoding Google Mercator (EPSG = 900913), while the downloaded postal code shapefile uses EPSG 4326.
QGIS does not automatically recognize these .prj files as projection files. One has to set them by hand.
Most importantly: Google Mercator (EPSG = 900913) was changed to EPSG= 3857. So for the custom shapefile I had to set – by hand! – the CRS to WGS 84/Pseudo-Mercator EPSG = 3857.
Now I could right click on the custom shape layer -> save as …. And Change the CRS to EPSG 4326. Thus the new custom shapefile now has the same projection like the downloaded postal code shapefile, and they can be joined by location.
(PS: Although I have a solution to do the conversion by hand, I would love to do this in R, because I need the resulting file for analysis.)
Check out: https://gis.stackexchange.com/questions/140504/extracting-intersection-areas-in-r?newreg=033544fa0f5349bcb8167d78867c8073
It gives you which shapefiles in dataset B overlap with a shapefile in dataset A as well as how much area in each of B's shapefiles is present in the target shapefile.

Choropleth world map

I have read so many threads and articles and I keep getting errors. I am trying to make a choropleth? map of the world using data I have from the global terrorism database. I want to color countries on a factor of nkills or just the number of attacks in that country.. I don't care at this point. Because there are so many countries with data, it is unreasonable to make any plots to show this data.
Help is strongly appreciated and if I did not ask this correctly I sincerely apologize, I am learning the rules of this website as I go.
my code (so far..)
library(maps)
library(ggplot2)
map("world")
world<- map_data("world")
gtd<- data.frame(gtd)
names(gtd)<- tolower(names(gtd))
gtd$country_txt<- tolower(rownames(gtd))
demo<- merge(world, gts, sort=FALSE, by="country_txt")
In the gtd data frame, the name for the countries column is "country_txt" so I thought I would use that but I get error in fix.by(by.x, x) : 'by' must specify a uniquely valid column
If that were to work, I would plot as I have seen on a few websites..
I have honestly been working on this for so long and I have read so many codes/other similar questions/websites/r handbooks etc.. I will accept that I am incompetent when it comes to R gladly for some help.
Something like this? This is a solution using rgdal and ggplot. I long ago gave up on using base R for this type of thing.
library(rgdal) # for readOGR(...)
library(RColorBrewer) # for brewer.pal(...)
library(ggplot2)
setwd(" < directory with all files >")
gtd <- read.csv("globalterrorismdb_1213dist.csv")
gtd.recent <- gtd[gtd$iyear>2009,]
gtd.recent <- aggregate(nkill~country_txt,gtd.recent,sum)
world <- readOGR(dsn=".",
layer="world_country_admin_boundary_shapefile_with_fips_codes")
countries <- world#data
countries <- cbind(id=rownames(countries),countries)
countries <- merge(countries,gtd.recent,
by.x="CNTRY_NAME", by.y="country_txt", all.x=T)
map.df <- fortify(world)
map.df <- merge(map.df,countries, by="id")
ggplot(map.df, aes(x=long,y=lat,group=group)) +
geom_polygon(aes(fill=nkill))+
geom_path(colour="grey50")+
scale_fill_gradientn(name="Deaths",
colours=rev(brewer.pal(9,"Spectral")),
na.value="white")+
coord_fixed()+labs(x="",y="")
There are several versions of the Global Terrorism Database. I used the full dataset available here, and then subsetted for year > 2009. So this map shows total deaths due to terrorism, by country, from 2010-01-01 to 2013-01-01 (the last data available from this source). The files are available as MS Excel download, which I converted to csv for import into R.
The world map is available as a shapefile from the GeoCommons website.
The tricky part of making choropleth maps is associating your data with the correct polygons (countries). This is generally a four step process:
Find a field in the shapefile attributes table that maps (no pun intended) to a corresponding field in your data. In this case, it appears that the field "CNTRY_NAME" in the shapefile maps to the field "country_txt" in gtd database.
Create an association between ploygon IDs (stored in the row names of the attribute table), and the CNTRY_NAME field.
Merge the result with your data using CNTRY_NAME and country_txt.
Merge the result of that with the data frame created using the fortify(map) - this associates ploygons with deaths (nkill).
Building on the nice work by #jlhoward. You could instead use rworldmap that already has a world map in R and has functions to aid joining data to the map. The default map is deliberately low resolution to create a 'cleaner' look. The map can be customised (see rworldmap documentation) but here is a start :
library(rworldmap)
#3 lines from #jlhoward
gtd <- read.csv("globalterrorismdb_1213dist.csv")
gtd.recent <- gtd[gtd$iyear>2009,]
gtd.recent <- aggregate(nkill~country_txt,gtd.recent,sum)
#join data to a map
gtdMap <- joinCountryData2Map( gtd.recent,
nameJoinColumn="country_txt",
joinCode="NAME" )
mapDevice('x11') #create a world shaped window
#plot the map
mapCountryData( gtdMap,
nameColumnToPlot='nkill',
catMethod='fixedWidth',
numCats=100 )
Following a comment from #hk47, you can also add the points to the map sized by the number of casualties.
deaths <- subset(x=gtd, nkill >0)
mapBubbles(deaths,
nameX='longitude',
nameY='latitude',
nameZSize='nkill',
nameZColour='black',
fill=FALSE,
addLegend=FALSE,
add=TRUE)

Choropleth Maps in R - TIGER Shapefile issue

Have a Question on Mapping with R, specifically around the choropleth maps in R.
I have a dataset of ZIP codes assigned to an are and some associated data (dataset is here).
My final data format is: Area ID, ZIP, Probability Value, Customer Count, Area Probability and Area Customer Total. I am attempting to present this data by plotting area probability and Area Customer Total on a Map. I have tried to do this by using the census TIGER Shapefiles but I guess R cannot handle the complete country.
I am comfortable with the Statistical capabilities and now I am moving all my Mapping from third party GIS focused applications to doing all my Mapping in R. Does anyone have any pointers to how to achieve this from within R?
To be a little more detailed, here's the point where R stops working -
shapes <- readShapeSpatial("tl_2013_us_zcta510.shp")
(where the shp file is the census/TIGER) shape file.
Edit - Providing further details. I am trying to first read the TIGER shapefiles, hoping to combine this spatial dataset with my data and eventually plot. I am having an issue at the very beginning when attempting to read the shape file. Below is the code with the output
require(maptools)
shapes<-readShapeSpatial("tl_2013_us_zcta510.shp")
Error: cannot allocate vector of size 317 Kb
There are several examples and tutorials on making maps using R, but most are very general and, unfortunately, most map projects have nuances that create inscrutable problems. Yours is a case in point.
The biggest issue I came across was that the US Census Bureau zip code tabulation area shapefile for the whole US is huge: ~800MB. When loaded using readOGR(...) the R SpatialPolygonDataFrame object is about 913MB. Trying to process a file this size, (e.g., converting to a data frame using fortify(...)), at least on my system, resulted in errors like the one you identified above. So the solution is to subset the file based in the zip codes that are actually in your data.
This map:
was made from your data using the following code.
library(rgdal)
library(ggplot2)
library(stringr)
library(RColorBrewer)
setwd("<directory containing shapfiles and sample data>")
data <- read.csv("Sample.csv",header=T) # your sample data, downloaded as csv
data$ZIP <- str_pad(data$ZIP,5,"left","0") # convert ZIP to char(5) w/leading zeros
zips <- readOGR(dsn=".","tl_2013_us_zcta510") # import zip code polygon shapefile
map <- zips[zips$ZCTA5CE10 %in% data$ZIP,] # extract only zips in your Sample.csv
map.df <- fortify(map) # convert to data frame suitable for plotting
# merge data from Samples.csv into map data frame
map.data <- data.frame(id=rownames(map#data),ZIP=map#data$ZCTA5CE10)
map.data <- merge(map.data,data,by="ZIP")
map.df <- merge(map.df,map.data,by="id")
# load state boundaries
states <- readOGR(dsn=".","gz_2010_us_040_00_5m")
states <- states[states$NAME %in% c("New York","New Jersey"),] # extract NY and NJ
states.df <- fortify(states) # convert to data frame suitable for plotting
ggMap <- ggplot(data = map.df, aes(long, lat, group = group))
ggMap <- ggMap + geom_polygon(aes(fill = Probability_1))
ggMap <- ggMap + geom_path(data=states.df, aes(x=long,y=lat,group=group))
ggMap <- ggMap + scale_fill_gradientn(name="Probability",colours=brewer.pal(9,"Reds"))
ggMap <- ggMap + coord_equal()
ggMap
Explanation:
The rgdal package facilitates the creation of R Spatial objects from ESRI shapefiles. In your case we are importing a polygon shapefile into a SpatialPolygonDataFrame object in R. The latter has two main parts: a polygon section, which contains the latitude and longitude points that will be joined to create the polygons on the map, and a data section which contains information about the polygons (so, one row for each polygon). If, e.g., we call the Spatial object map, then the two sections can be referenced as map#polygons and map#data. The basic challenge in making choropleth maps is to associate data from your Sample.csv file, with the relevant polygons (zip codes).
So the basic workflow is as follows:
1. Load polygon shapefiles into Spatial object ( => zips)
2. Subset if appropriate ( => map).
3. Convert to data frame suitable for plotting ( => map.df).
4. Merge data from Sample.csv into map.df.
5. Draw the map.
Step 4 is the one that causes all the problems. First we have to associate zip codes with each polygon. Then we have to associate Probability_1 with each zip code. This is a three step process.
Each polygon in the Spatial data file has a unique ID, but these ID's are not the zip codes. The polygon ID's are stored as row names in map#data. The zip codes are stored in map#data, in column ZCTA5CE10. So first we must create a data frame that associates the map#data row names (id) with map#data$ZCTA5CE10 (ZIP). Then we merge your Sample.csv with the result using the ZIP field in both data frames. Then we merge the result of that into map.df. This can be done in 3 lines of code.
Drawing the map involves telling ggplot what dataset to use (map.df), which columns to use for x and y (long and lat) and how to group the data by polygon (group=group). The columns long, lat, and group in map.df are all created by the call to fortify(...). The call to geom_polygon(...) tells ggplot to draw polygons and fill using the information in map.df$Probability_1. The call to geom_path(...) tells ggplot to create a layer with state boundaries. The call to scale_fill_gradientn(...) tells ggplot to use a color scheme based on the color brewer "Reds" palette. Finally, the call to coord_equal(...) tells ggplot to use the same scale for x and y so the map is not distorted.
NB: The state boundary layer, uses the US States TIGER file.
I would advise the following.
Use readOGR from the rgdal package rather than readShapeSpatial.
Consider using ggplot2 for good-looking maps - many of the examples use this.
Refer to one of the existing examples of creating a choropleth such as this one to get an overview.
Start with a simple choropleth and gradually add your own data; don't try and get it all right at once.
If you need more help, create a reproducible example with a SMALL fake dataset and with links to the shapefiles in question. The idea is that you make it easy to help us help you rather than discourage us by not supplying code and data in your question.

Resources