Orthophotomap is a raster, orthogonal and cartometric representation of the terrain surface created by digital processing of aerial or satellite images. During the orthorectification, geometric distortions resulting from the land relief are removed by using digital elevation models (DEM). An orthophotomap is georeferenced, and therefore, allows to determine geographic coordinates for each of its cells.
Orthophotomaps’ properties:
The purpose of this vignette is to assess the vegetation condition of the selected area. It can be done based on remote sensing data (multispectral orthophotomap) and a simple vegetation index.
NDVI (Normalized Difference Vegetation Index) is a simple indicator of vegetation that uses the red and the near infrared bands. Its main application is monitoring and forecasting of agricultural production. It is calculated using the following formula:
\[NDVI = \frac {NIR - RED} {NIR + RED}\]
Its value ranges from -1 to 1. The higher the value, the higher the biomass level. Values close to 0 and below are related to water, bare soil surfaces or buildings.
# attach packages
library(sf)
library(stars)
library(rgugik)
The analysis area is the Krajkowo nature reserve located in the Greater Poland voivodeship. It was established in 1958 in order to protect the breeding places of birds, especially the grey heron and the great black cormorant, and to protect the landscape of the Warta oxbow.
Data on nature reserves can be found in General Geographic Databases.
We can obtain them using the geodb_download()
function.
Let’s do that.
# 17.6 MB
geodb_download("wielkopolskie", outdir = "./data")
If you run into any problem with the download, remember that you can
pass another download method from download.file()
as a
function argument.
geodb_download(req_df, outdir = "./data", method = "wget")
The downloaded database consists of many files in the GML
(Geography Markup Language) format. A brief description of the
structure of this database can be found here.
The table with the nature reserves is in the
“PL.PZGIK.201.30__OT_TCRZ_A.xml” file. We can use the
sf package and its read_sf()
function to
load it.
= read_sf("data/PL.PZGiK.201.30/BDOO/PL.PZGIK.201.30__OT_TCRZ_A.xml") reserves
Let’s check the structure of our data.
ncol(reserves)
## [1] 28
nrow(reserves)
## [1] 110
In simple terms, it is a spatial table consisting of 110 observations (rows) and 28 variables (columns). The names of the objects are located in the nazwa column, which allow us to select the Krajkowo reserve only.
# selection by attribute
= reserves[reserves$nazwa == "Krajkowo", ] krajkowo
We can display it in two basic ways:
plot()
function and directly specifying the
column with the object geometry:
plot(krajkowo$geometry)
plot()
and st_geometry()
functions that obtain geometry from the vector layer. In the first case,
we need to know the name of the column with geometries
(e.g. geometry
, geom
, etc.), while in the
second case, the geometry is selected automatically (it is the safer and
preferable way).plot(st_geometry(krajkowo), axes = TRUE, main = "Krajkowo reserve")
We can also calculate the area of this polygon.
= st_area(krajkowo) # [m^2]
krajkowo_area ::set_units(krajkowo_area, "ha") # convert to [ha] units
## 165.2744 [ha]
The st_area()
function returned the area in m^2, and
after the conversion we got the result of 165 ha.
Now let’s move on to the stage of downloading the orthophotomap. We
use the ortho_request()
function that show us which images
are available for the analyzed area. We need to provide our Krajkowo
polygon as the argument of this function.
= ortho_request(krajkowo) req_df
We can display the resulting table using the code below.
# display the first 10 rows and the first 6 columns
1:10, 1:6] req_df[
## sheetID year resolution composition sensor CRS
## 1 N-33-142-B-d-4-2 2004 0.50 B/W Analog PL-1992
## 2 N-33-142-B-d-4-4 2004 0.50 B/W Analog PL-1992
## 3 N-33-142-B-d-4-4 2010 0.25 RGB Digital PL-1992
## 4 N-33-142-B-d-4-2 2010 0.25 RGB Digital PL-1992
## 5 N-33-142-B-d-4-4 2010 0.25 CIR Digital PL-1992
## 6 N-33-142-B-d-4-2 2010 0.25 CIR Digital PL-1992
## 7 N-33-142-B-d-4-2 2016 0.25 CIR Digital PL-1992
## 8 N-33-142-B-d-4-2 2016 0.25 RGB Digital PL-1992
## 9 N-33-142-B-d-4-4 2016 0.25 CIR Digital PL-1992
## 10 N-33-142-B-d-4-4 2016 0.25 RGB Digital PL-1992
To complete our task, we need to obtain a near infrared data. So in
the next step, we select those rows for which the
composition
column has the value of “CIR”.
# select IR images and overwrite the req_df object
= req_df[req_df$composition == "CIR", ] req_df
Then let’s sort the table according to the year the photo was taken, with the most recent images at the top of the table.
= req_df[order(-req_df$year), ] req_df
Let’s display the table again and select the newest compositions.
c(1:5, 9)] req_df[,
## sheetID year resolution composition sensor seriesID
## 7 N-33-142-B-d-4-2 2016 0.25 CIR Digital 69837
## 9 N-33-142-B-d-4-4 2016 0.25 CIR Digital 69837
## 22 N-33-142-B-d-4-2 2013 0.25 CIR Digital 69903
## 24 N-33-142-B-d-4-4 2013 0.25 CIR Digital 69903
## 5 N-33-142-B-d-4-4 2010 0.25 CIR Digital 69763
## 6 N-33-142-B-d-4-2 2010 0.25 CIR Digital 69763
= req_df[req_df$year == 2016, ] req_df
Note that the result has a pair of objects (images). This means that
our Krajkowo reserve is depicted in two photos within one series.
Therefore, the seriesID
column is used to combine smaller
images into a larger mosaic.
c(1:5, 9)] req_df[,
## sheetID year resolution composition sensor seriesID
## 7 N-33-142-B-d-4-2 2016 0.25 CIR Digital 69837
## 9 N-33-142-B-d-4-4 2016 0.25 CIR Digital 69837
The tile_download()
function is used to download
orthophotomaps by taking our selected table as the main argument. We can
also specify the output folder with the outdir
argument.
# 61.9 MB
tile_download(req_df, outdir = "./data")
## 1/2
## 2/2
Let’s load the downloaded orthophotomaps using the
read_stars()
function from the stars
package, which allows working with spatiotemporal arrays. In our case,
it is a raster consisting of three bands (NIR, R, G) for only one point
in time. We don’t need to load the entire data array into memory - we
can read the file’s metadata instead by using the proxy
argument.
= read_stars("data/69837_329609_N-33-142-B-d-4-2.TIF", proxy = TRUE)
img1 = read_stars("data/69837_329613_N-33-142-B-d-4-4.TIF", proxy = TRUE) img2
Now we can perform two operations: rasters merging and cropping to
the reserve area. The use of a proxy
allows to get the
result almost immediately, while processing the entire image
(proxy = FALSE
) would take several minutes. Images have
their own specific Coordinate Reference Systems, so let’s make sure it
is the correct one after merging. It should be EPSG 2180 in this
case.
= st_mosaic(img1, img2)
img st_crs(img) = 2180 # overwrite CRS to be sure
= st_crop(img, krajkowo) img
Let’s display the effect using the plot()
function, and
define the input bands with the rgb
argument. It creates a
composition consisting of three bands: NIR, R and G in our case. The
composition below is shown in infrared, not in natural colors, which may
be misinterpreted from the rgb
argument name.
plot(img, rgb = c(1, 2, 3), main = NULL)
In the last step, we calculate the NDVI using the near infrared (1) and red (2) bands.
= function(img) (img[1] - img[2]) / (img[1] + img[2])
calc_ndvi = st_apply(img, MARGIN = c("x", "y"), FUN = calc_ndvi)
ndvi plot(ndvi, main = "NDVI", col = hcl.colors(10, palette = "RdYlGn"))
A surprising observation is the relatively low NDVI values for the forest area. There are two reasons for this, i.e. the photos are taken in mid-March (before the start of the growing season) and probably have not been calibrated. For this reason, a better source of data for analysis may be satellite images, which are calibrated spectrally and obtained continuously (if no cloudiness occurs).