In this note I take some first steps in learning to work with spatial objects in R using the sf-package. There are off course different spatial objects: Points, Lines and Polygons, to mention just the basics. In this note I use polygons as example. They are often important for map-making exercises which is also going to be one of the examples given here:
Secondly, I want to take a look at some of the basic methods available for manipulation of sf-objects in particular the polygons (so points and lines must wait for another note).
The objects are data.frames with at least one column being a list column. Compared to a matrix, the benefit of a data.frame, is that it can store different types of data in each column. However, compared to lists, data.frames are still limited because they have a single number of rows, hence each column is equally long.
So how can you initialize a data.frame where one column is a list column? First how not to do it
a <- 1:3
b <- letters[1:3]
c <- list(a=c(1,2,3),b=c(2,3,4),c=c(3,4,5))
data.frame(a,b,c)
## a b a.1 b.1 c
## 1 1 a 1 2 3
## 2 2 b 2 3 4
## 3 3 c 3 4 5
secondly how to it using the operator
data.frame(a,b,I(c))
## a b c
## a 1 a 1, 2, 3
## b 2 b 2, 3, 4
## c 3 c 3, 4, 5
as explained here https://stackoverflow.com/questions/9547518/create-a-data-frame-where-a-column-is-a-list/9547594.
path_shape <- "C://Users//np83zg//OneDrive - Aalborg Universitet//Skrivebord//cars-master//rspace_osm//Learning SF//shapes"
setwd(path_shape)
library(sf)
## Warning: package 'sf' was built under R version 4.0.5
## Linking to GEOS 3.9.0, GDAL 3.2.1, PROJ 7.2.1
mun_shp <- st_read("municipalities_DK.shp",options = "ENCODING=UTF-8")
## options: ENCODING=UTF-8
## Reading layer `municipalities_DK' from data source `C:\Users\np83zg\OneDrive - Aalborg Universitet\Skrivebord\cars-master\rspace_osm\Learning SF\shapes\municipalities_DK.shp' using driver `ESRI Shapefile'
## Simple feature collection with 98 features and 2 fields
## Geometry type: MULTIPOLYGON
## Dimension: XYZ
## Bounding box: xmin: 441676 ymin: 6049782 xmax: 892787 ymax: 6402217
## z_range: zmin: -999 zmax: 0
## Projected CRS: ETRS89 / UTM zone 32N
# There are problems with danish names ... don't know how to solve
# Using rgdal instead
# If reading a shapefile, the data source name (dsn= argument) is the
# folder (directory) where the shapefile is, and the layer is the name
# of the shapefile (without the .shp extension).
library(rgdal)
## Warning: package 'rgdal' was built under R version 4.0.5
## Loading required package: sp
## Warning: package 'sp' was built under R version 4.0.5
## rgdal: version: 1.5-23, (SVN revision 1121)
## Geospatial Data Abstraction Library extensions to R successfully loaded
## Loaded GDAL runtime: GDAL 3.2.1, released 2020/12/29
## Path to GDAL shared files: C:/Users/np83zg/OneDrive - Aalborg Universitet/Dokumenter/R/win-library/4.0/rgdal/gdal
## GDAL binary built with GEOS: TRUE
## Loaded PROJ runtime: Rel. 7.2.1, January 1st, 2021, [PJ_VERSION: 721]
## Path to PROJ shared files: C:/Users/np83zg/OneDrive - Aalborg Universitet/Dokumenter/R/win-library/4.0/rgdal/proj
## PROJ CDN enabled: FALSE
## Linking to sp version:1.4-5
## To mute warnings of possible GDAL/OSR exportToProj4() degradation,
## use options("rgdal_show_exportToProj4_warnings"="none") before loading rgdal.
## Overwritten PROJ_LIB was C:/Users/np83zg/OneDrive - Aalborg Universitet/Dokumenter/R/win-library/4.0/rgdal/proj
foo <- readOGR(dsn=".", layer="municipalities_DK")
## Warning in OGRSpatialRef(dsn, layer, morphFromESRI = morphFromESRI, dumpSRS =
## dumpSRS, : Discarded datum European_Terrestrial_Reference_System_1989 in Proj4
## definition: +proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m
## +no_defs
## OGR data source with driver: ESRI Shapefile
## Source: "C:\Users\np83zg\OneDrive - Aalborg Universitet\Skrivebord\cars-master\rspace_osm\Learning SF\shapes", layer: "municipalities_DK"
## with 98 features
## It has 2 fields
## Warning in readOGR(dsn = ".", layer = "municipalities_DK"): Z-dimension
## discarded
# This returns S4 onject. You can get slotnames using
slotNames(foo)
## [1] "data" "polygons" "plotOrder" "bbox" "proj4string"
# The access slots using
foo@data
## muni_name ID_muni
## 0 København 101
## 1 Frederiksberg 147
## 2 Ballerup 151
## 3 Brøndby 153
## 4 Dragør 155
## 5 Gentofte 157
## 6 Gladsaxe 159
## 7 Glostrup 161
## 8 Herlev 163
## 9 Albertslund 165
## 10 Hvidovre 167
## 11 Høje Taastrup 169
## 12 Lyngby-Taarbæk 173
## 13 Rødovre 175
## 14 Ishøj 183
## 15 TÃ¥rnby 185
## 16 Vallensbæk 187
## 17 Furesø 190
## 18 Allerød 201
## 19 Fredensborg 210
## 20 Helsingør 217
## 21 Hillerød 219
## 22 Hørsholm 223
## 23 Rudersdal 230
## 24 Egedal 240
## 25 Frederikssund 250
## 26 Greve 253
## 27 Køge 259
## 28 Halsnæs 260
## 29 Roskilde 265
## 30 Solrød 269
## 31 Gribskov 270
## 32 Odsherred 306
## 33 Holbæk 316
## 34 Faxe 320
## 35 Kalundborg 326
## 36 Ringsted 329
## 37 Slagelse 330
## 38 Stevns 336
## 39 Sorø 340
## 40 Lejre 350
## 41 Lolland 360
## 42 Næstved 370
## 43 Guldborgsund 376
## 44 Vordingborg 390
## 45 Bornholm 400
## 46 Middelfart 410
## 47 Assens 420
## 48 Faaborg-Midtfyn 430
## 49 Kerteminde 440
## 50 Nyborg 450
## 51 Odense 461
## 52 Svendborg 479
## 53 Nordfyns 480
## 54 Langeland 482
## 55 Ærø 492
## 56 Haderslev 510
## 57 Billund 530
## 58 Sønderborg 540
## 59 Tønder 550
## 60 Esbjerg 561
## 61 Fanø 563
## 62 Varde 573
## 63 Vejen 575
## 64 Aabenraa 580
## 65 Fredericia 607
## 66 Horsens 615
## 67 Kolding 621
## 68 Vejle 630
## 69 Herning 657
## 70 Holstebro 661
## 71 Lemvig 665
## 72 Struer 671
## 73 Syddjurs 706
## 74 Norddjurs 707
## 75 Favrskov 710
## 76 Odder 727
## 77 Randers 730
## 78 Silkeborg 740
## 79 Samsø 741
## 80 Skanderborg 746
## 81 Aarhus 751
## 82 Ikast-Brande 756
## 83 Ringkøbing-Skjern 760
## 84 Hedensted 766
## 85 Morsø 773
## 86 Skive 779
## 87 Thisted 787
## 88 Viborg 791
## 89 Brønderslev 810
## 90 Frederikshavn 813
## 91 Vesthimmerlands 820
## 92 Læsø 825
## 93 Rebild 840
## 94 Mariagerfjord 846
## 95 Jammerbugt 849
## 96 Aalborg 851
## 97 Hjørring 860
slot(foo,"data")
## muni_name ID_muni
## 0 København 101
## 1 Frederiksberg 147
## 2 Ballerup 151
## 3 Brøndby 153
## 4 Dragør 155
## 5 Gentofte 157
## 6 Gladsaxe 159
## 7 Glostrup 161
## 8 Herlev 163
## 9 Albertslund 165
## 10 Hvidovre 167
## 11 Høje Taastrup 169
## 12 Lyngby-Taarbæk 173
## 13 Rødovre 175
## 14 Ishøj 183
## 15 TÃ¥rnby 185
## 16 Vallensbæk 187
## 17 Furesø 190
## 18 Allerød 201
## 19 Fredensborg 210
## 20 Helsingør 217
## 21 Hillerød 219
## 22 Hørsholm 223
## 23 Rudersdal 230
## 24 Egedal 240
## 25 Frederikssund 250
## 26 Greve 253
## 27 Køge 259
## 28 Halsnæs 260
## 29 Roskilde 265
## 30 Solrød 269
## 31 Gribskov 270
## 32 Odsherred 306
## 33 Holbæk 316
## 34 Faxe 320
## 35 Kalundborg 326
## 36 Ringsted 329
## 37 Slagelse 330
## 38 Stevns 336
## 39 Sorø 340
## 40 Lejre 350
## 41 Lolland 360
## 42 Næstved 370
## 43 Guldborgsund 376
## 44 Vordingborg 390
## 45 Bornholm 400
## 46 Middelfart 410
## 47 Assens 420
## 48 Faaborg-Midtfyn 430
## 49 Kerteminde 440
## 50 Nyborg 450
## 51 Odense 461
## 52 Svendborg 479
## 53 Nordfyns 480
## 54 Langeland 482
## 55 Ærø 492
## 56 Haderslev 510
## 57 Billund 530
## 58 Sønderborg 540
## 59 Tønder 550
## 60 Esbjerg 561
## 61 Fanø 563
## 62 Varde 573
## 63 Vejen 575
## 64 Aabenraa 580
## 65 Fredericia 607
## 66 Horsens 615
## 67 Kolding 621
## 68 Vejle 630
## 69 Herning 657
## 70 Holstebro 661
## 71 Lemvig 665
## 72 Struer 671
## 73 Syddjurs 706
## 74 Norddjurs 707
## 75 Favrskov 710
## 76 Odder 727
## 77 Randers 730
## 78 Silkeborg 740
## 79 Samsø 741
## 80 Skanderborg 746
## 81 Aarhus 751
## 82 Ikast-Brande 756
## 83 Ringkøbing-Skjern 760
## 84 Hedensted 766
## 85 Morsø 773
## 86 Skive 779
## 87 Thisted 787
## 88 Viborg 791
## 89 Brønderslev 810
## 90 Frederikshavn 813
## 91 Vesthimmerlands 820
## 92 Læsø 825
## 93 Rebild 840
## 94 Mariagerfjord 846
## 95 Jammerbugt 849
## 96 Aalborg 851
## 97 Hjørring 860
The sf-object is a data.frame. Each row is an sfg. This is what would normally be thought of as an observation. So one row one observation. In this case each observation is associated with an area in space defined by the polygon. Each polygon have an identifying number.
Data is merged using the merge command merge(x,y), where x and y are data.frames. Personally I prefer to work with data.tables but the sf-object cannot be transformed to a data.table without loosing its sf-class. This means that it cannot be plotted anymore using the sf-plot method.
# Load data
path_shape <- "C://Users//np83zg//OneDrive - Aalborg Universitet//Skrivebord//cars-master//rspace_osm//Learning SF//shapes"
setwd(path_shape)
mun_shp <- st_read("municipalities_DK.shp",options = "ENCODING=UTF-8")
## options: ENCODING=UTF-8
## Reading layer `municipalities_DK' from data source `C:\Users\np83zg\OneDrive - Aalborg Universitet\Skrivebord\cars-master\rspace_osm\Learning SF\shapes\municipalities_DK.shp' using driver `ESRI Shapefile'
## Simple feature collection with 98 features and 2 fields
## Geometry type: MULTIPOLYGON
## Dimension: XYZ
## Bounding box: xmin: 441676 ymin: 6049782 xmax: 892787 ymax: 6402217
## z_range: zmin: -999 zmax: 0
## Projected CRS: ETRS89 / UTM zone 32N
library(data.table)
## Warning: package 'data.table' was built under R version 4.0.5
kom_names <- read.table("kom_names.txt",header=TRUE)
kom_names <- as.data.table(kom_names)
kom_names[,kom_ny:=NULL]
setkey(kom_names,"kom")
plot(mun_shp)
mun_shp <- as.data.table(mun_shp)
class(mun_shp)
## [1] "data.table" "data.frame"
mun_shp <- merge(mun_shp,kom_names,by.x="ID_muni",by.y="kom")
mun_shp[,muni_name:=NULL]
You can plot before transforming to data.table but not after. For merging you have to transform if you wanna depend on data.table’s data-management capabilities. However, then the object is no longer sf and cannot be plotted unless transformed back to sf. But it not necessary to transform to merge because you simply merge data.frames.
path_main <- "C://Users//np83zg//OneDrive - Aalborg Universitet//Skrivebord//cars-master//rspace_osm//Learning SF"
path_shape <- "C://Users//np83zg//OneDrive - Aalborg Universitet//Skrivebord//cars-master//rspace_osm//Learning SF//shapes"
setwd(path_shape)
mun_shp <- st_read("municipalities_DK.shp",options = "ENCODING=UTF-8")
## options: ENCODING=UTF-8
## Reading layer `municipalities_DK' from data source `C:\Users\np83zg\OneDrive - Aalborg Universitet\Skrivebord\cars-master\rspace_osm\Learning SF\shapes\municipalities_DK.shp' using driver `ESRI Shapefile'
## Simple feature collection with 98 features and 2 fields
## Geometry type: MULTIPOLYGON
## Dimension: XYZ
## Bounding box: xmin: 441676 ymin: 6049782 xmax: 892787 ymax: 6402217
## z_range: zmin: -999 zmax: 0
## Projected CRS: ETRS89 / UTM zone 32N
library(data.table)
kom_names <- read.table("kom_names.txt",header=TRUE)
kom_names <- as.data.table(kom_names)
kom_names[,kom_ny:=NULL]
setkey(kom_names,"kom")
mun_shp <- merge(mun_shp,kom_names,by.x="ID_muni",by.y="kom")
library(tmap)
## Warning: package 'tmap' was built under R version 4.0.5
mun_shape <- mun_shp
mun_shape$dn <- abs(rnorm(98))
tm_shape(mun_shape) + tm_polygons("dn",palette="RdBu",title="Population growth rate") +
tm_shape(mun_shape) + tm_borders("white", lwd = 1.2)
setwd(path_main)
There are several methods for working with sf objects
methods(class = "sf")
## [1] $<- [ [[<-
## [4] aggregate as.data.frame cbind
## [7] coerce dbDataType dbWriteTable
## [10] filter identify initialize
## [13] merge plot print
## [16] rbind show slotsFromS3
## [19] st_agr st_agr<- st_area
## [22] st_as_s2 st_as_sf st_bbox
## [25] st_boundary st_buffer st_cast
## [28] st_centroid st_collection_extract st_convex_hull
## [31] st_coordinates st_crop st_crs
## [34] st_crs<- st_difference st_filter
## [37] st_geometry st_geometry<- st_inscribed_circle
## [40] st_interpolate_aw st_intersection st_intersects
## [43] st_is st_is_valid st_join
## [46] st_line_merge st_m_range st_make_valid
## [49] st_nearest_points st_node st_normalize
## [52] st_point_on_surface st_polygonize st_precision
## [55] st_reverse st_sample st_segmentize
## [58] st_set_precision st_shift_longitude st_simplify
## [61] st_snap st_sym_difference st_transform
## [64] st_triangulate st_union st_voronoi
## [67] st_wrap_dateline st_write st_z_range
## [70] st_zm transform
## see '?methods' for accessing help and source code