
Geo-referenced basemaps
Source:vignettes/articles/georeferenced-basemaps.Rmd
georeferenced-basemaps.RmdIn simulariatools 3.1.0, we enhanced the wrapper function
contourPlot2() to interpret and utilize geographical
information from a geo-referenced basemap file.
The primary motivation for this new feature is to minimize errors
that may arise from potential misalignment between the basemap and the
data matrix to be plotted. In previous versions, it was possible for the
contour plot to be overlaid on the wrong basemap. Now, by using a
GeoTIFF basemap, we can safely assume that the contour plot will be
accurately aligned with it without providing the plot bounding box via
xlim and ylim arguments. Nonetheless, we must
still be careful with the Coordinate Reference System (CRS) of the data.
Since the data.frame() to be plotted is not CRS aware, we
need to be sure that the coordinates x and y
are in the same reference system as the GeoTIFF file.
Here is a simple example of this new feature in action. Let’s
consider a sample dataset of yearly average odour units of an imaginary
source, produced by the dispersion model CALPUFF. To import the data
into the project, we use importSurferGrd() from
simulariatools even though there are other ways to
accomplish this task. The computational domain of the numerical
simulation has lower left coordinates (388000, 4969000) in the UTM32
CRS, an extent of 6000 m in both directions and a grid size of 200
m.
calpuff_file <- "./ou_8760hr_testcase.grd"
ou_testcase <- importSurferGrd(calpuff_file, k = 1000)
#> [1] Z min = 0.3609
#> [1] Z max = 4.697
head(ou_testcase)
#> x y z
#> 1 388000 4969000 0.38817
#> 2 388200 4969000 0.43803
#> 3 388400 4969000 0.48059
#> 4 388600 4969000 0.51562
#> 5 388800 4969000 0.54536
#> 6 389000 4969000 0.57245There are many ways to obtain a geo-referenced basemap file. Here we
download the image from the Italian geoportal by using the
downloadBasemap() function of simulariatools.
In its most recent version, the function automatically tries to download
a geo-referenced TIFF file (GeoTIFF).
downloadBasemap(
file = "./basemap.tiff",
xSW = 388000,
ySW = 4969000,
xExt = 6000,
yExt = 6000
)In order to analyze the file content, it is convenient to use the
rast() function of the terra package:
terra::rast("basemap.tiff")
#> class : SpatRaster
#> size : 1024, 1024, 3 (nrow, ncol, nlyr)
#> resolution : 5.859375, 5.859375 (x, y)
#> extent : 388000, 394000, 4969000, 4975000 (xmin, xmax, ymin, ymax)
#> coord. ref. : WGS 84 / UTM zone 32N (EPSG:32632)
#> source : basemap.tiff
#> colors rgb : 1, 2, 3
#> names : basemap_1, basemap_2, basemap_3As we can see, this basemap exactly overlaps with the dispersion simulation computational domain.
Now we can quickly plot the odour levels using the bounding box defined by the GeoTIFF file:
contourPlot2(
ou_testcase,
basemap = "basemap.tiff",
nticks = 7,
levels = c(1, 2, 3, 4, 5),
fill = FALSE
)
In this case, the bounding box defined by the basemap file and the
extent defined by the ou_testcase data.frame are
the same. There is no need to explicitly specify the plot bounding box,
nor to assume that the basemap has the correct extent.
We can use the new xlim and ylim arguments
to crop the contour plot:
contourPlot2(
ou_testcase,
basemap = "basemap.tiff",
xlim = c(389000, 393000),
ylim = c(4970000, 4974000),
levels = c(1, 2, 3, 4, 5),
fill = FALSE
)
As we can easily check by looking at the odour level contour lines, the plot is still correctly aligned with the underlying image, without the need to pass through a new basemap file.
To test the new function even further, we can use a basemap covering a larger geographical area. In this case, we use an already prepared GeoTIFF file created by QGIS with a map from OpenStreetMap, with an extent of 40 km × 40 km:
terra::rast("basemap_large.tiff")
#> class : SpatRaster
#> size : 2481, 2481, 4 (nrow, ncol, nlyr)
#> resolution : 16.12253, 16.12253 (x, y)
#> extent : 370000, 410000, 4960000, 5000000 (xmin, xmax, ymin, ymax)
#> coord. ref. : WGS 84 / UTM zone 32N (EPSG:32632)
#> source : basemap_large.tiff
#> names : basemap_large_1, basemap_large_2, basemap_large_3, basemap_large_4contourPlot2() reads the extent of the larger basemap
and correctly plots the data in ou_testcase on the map:
contourPlot2(
ou_testcase,
basemap = "basemap_large.tiff",
levels = c(1, 2, 3, 4, 5)
) +
labs(x = NULL, y = NULL)