Skip to Content

Transforming spatial objects

Changing between coordinate reference systems with the R package sp

General note: migration to the more actively developed sf package is currently advised by the sp maintainer. The sp package, used in this tutorial, is still maintained in order to support the newest versions of the GDAL and PROJ backends.

Sometimes we have a layer in one coordinate reference system (CRS) and need to transform it into another coordinate reference system. The first thing we need to do is identifying both coordinate reference systems. Let’s create an example and identify the coordinate reference system with wkt(). We used the coordinates posted on the contact page of NGI.

library(sp)
library(leaflet)
library(widgetframe)
## Loading required package: htmlwidgets
ngi <- data.frame(x = 650381.78, y = 667603.12)
coordinates(ngi) <- ~x + y
wkt(ngi)  # show the CRS of an sp object
## Warning in wkt(ngi): CRS object has no comment

## NULL

NULL indicates that the coordinate reference system isn’t set. So we need to set it manually. In this case we know it is “Lambert 2008”. We need to know the related ‘WKT2 string’. The WKT2 string (well known text) is a recent open standard by the Open Geospatial Consortium to represent a coordinate reference system (CRS) - and it replaces the older (deprecated) PROJ.4 string.

Most coordinate reference systems have an EPSG code which you can find at http://epsg.io/. The EPSG code for “Lambert 2008” is 3812. Let’s set this coordinate reference system to our dataset. CRS() defines the coordinate reference system.

proj4string(ngi) <- CRS(SRS_string = "EPSG:3812")
## Warning in showSRID(SRS_string, format = "PROJ", multiline = "NO", prefer_proj
## = prefer_proj): Discarded datum European Terrestrial Reference System 1989 in
## Proj4 definition

The proj4string() function refers to the older PROJ.4 strings to represent a CRS, which the sp package can still return for backward compatibility. The warning above demonstrates that some PROJ.4 information is not supported anymore. sp now uses the much better WKT2 representation, which it will derive directly from the EPSG-code that we provided (even though the function to assign a CRS to an object is still called proj4string()). The WKT2 string is what the geospatial GDAL and PROJ libraries (the background workhorses) work with since PROJ >= 6, and it looks like this:

wkt_lambert2008 <- wkt(ngi)
cat(wkt_lambert2008)
## PROJCRS["ETRS89 / Belgian Lambert 2008",
##     BASEGEOGCRS["ETRS89",
##         DATUM["European Terrestrial Reference System 1989",
##             ELLIPSOID["GRS 1980",6378137,298.257222101,
##                 LENGTHUNIT["metre",1]]],
##         PRIMEM["Greenwich",0,
##             ANGLEUNIT["degree",0.0174532925199433]],
##         ID["EPSG",4258]],
##     CONVERSION["Belgian Lambert 2008",
##         METHOD["Lambert Conic Conformal (2SP)",
##             ID["EPSG",9802]],
##         PARAMETER["Latitude of false origin",50.797815,
##             ANGLEUNIT["degree",0.0174532925199433],
##             ID["EPSG",8821]],
##         PARAMETER["Longitude of false origin",4.35921583333333,
##             ANGLEUNIT["degree",0.0174532925199433],
##             ID["EPSG",8822]],
##         PARAMETER["Latitude of 1st standard parallel",49.8333333333333,
##             ANGLEUNIT["degree",0.0174532925199433],
##             ID["EPSG",8823]],
##         PARAMETER["Latitude of 2nd standard parallel",51.1666666666667,
##             ANGLEUNIT["degree",0.0174532925199433],
##             ID["EPSG",8824]],
##         PARAMETER["Easting at false origin",649328,
##             LENGTHUNIT["metre",1],
##             ID["EPSG",8826]],
##         PARAMETER["Northing at false origin",665262,
##             LENGTHUNIT["metre",1],
##             ID["EPSG",8827]]],
##     CS[Cartesian,2],
##         AXIS["easting (X)",east,
##             ORDER[1],
##             LENGTHUNIT["metre",1]],
##         AXIS["northing (Y)",north,
##             ORDER[2],
##             LENGTHUNIT["metre",1]],
##     USAGE[
##         SCOPE["Engineering survey, topographic mapping."],
##         AREA["Belgium - onshore."],
##         BBOX[49.5,2.5,51.51,6.4]],
##     ID["EPSG",3812]]

We could verify the correctness of this position by plotting it on a map. Here we use the leaflet package which requires the data to be in the “WGS84” coordinate reference system. Therefore we use spTransform() to do this transformation. “WGS84” has EPSG code 4326.

ngi_ll <- spTransform(ngi, CRS(SRS_string = "EPSG:4326"))
cat(wkt(ngi_ll))
## GEOGCRS["WGS 84 (with axis order normalized for visualization)",
##     DATUM["World Geodetic System 1984",
##         ELLIPSOID["WGS 84",6378137,298.257223563,
##             LENGTHUNIT["metre",1]],
##         ID["EPSG",6326]],
##     PRIMEM["Greenwich",0,
##         ANGLEUNIT["degree",0.0174532925199433],
##         ID["EPSG",8901]],
##     CS[ellipsoidal,2],
##         AXIS["geodetic longitude (Lon)",east,
##             ORDER[1],
##             ANGLEUNIT["degree",0.0174532925199433,
##                 ID["EPSG",9122]]],
##         AXIS["geodetic latitude (Lat)",north,
##             ORDER[2],
##             ANGLEUNIT["degree",0.0174532925199433,
##                 ID["EPSG",9122]]]]
leaflet(ngi_ll) %>%
  addTiles() %>%
  addMarkers() # %>%

  #frameWidget()

Note: run the code to see the interactive map.

CRS EPSG
WGS 84 4326
Belge 1972 / Belgian Lambert 72 31370
ETRS89 / Belgian Lambert 2008 3812
WGS 84 / Pseudo-Mercator 3857

Relevant projections for Belgian data. CRS = Coordinate reference system, EPSG = CRS code in the EPSG dataset.