Buffl

Übung

SO
by Stina O.

Übung 4


Kartierung von Landbedeckungsänderungen (Störungen) in der Böhmisch-Sächsischen Schweiz


Verbesserung der Klassifikation und der finalen Karte

Ziel ist es, das Ergebnis der Klassifikation sowie die finale Karte zu verbessern. Führen Sie dazu folgende Analysen durch:

7.1 Prüfen, ob der Normalised Burn Ratio (NBR) die Ergebnisse verbessern kann

  • Berechnen Sie den Normalised Burn Ratio (NBR) (siehe z. B. Beetz et al. 2024) auf die gleiche Weise, wie zuvor der NDVI berechnet wurde.

  • Untersuchen Sie die Korrelation des NBR mit anderen Merkmalen und analysieren Sie, wie gut sich der NBR zur Trennung der Störungsklassen eignet.

  • Trainieren Sie das Random-Forest-Modell erneut, diesmal unter zusätzlicher Verwendung des NBR.

    • Vergleichen Sie die Leistungsmetriken des Modells mit den vorherigen Ergebnissen (also ohne NBR).

    • Beschreiben Sie, ob der Einsatz des NBR die Klassifikation verbessert hat.

  • Wenden Sie das trainierte Modell auf das vollständige Kompositbild an.

  • Aktualisieren Sie die CLC-Karte und diskutieren Sie die Ergebnisse.

7.2 Prüfen, ob der Difference Normalised Burn Ratio (dNBR) die Ergebnisse verbessern kann

  • Laden Sie zusätzlich die Landsat-8- und Sentinel-2-Bilder vom 10. September 2021 und erstellen Sie ein Komposit für 2021.

  • Berechnen Sie den Difference Normalised Burn Ratio (dNBR) aus dem NBR von 2021 und 2022 (siehe z. B. Beetz et al. 2024).

  • Wiederholen Sie anschließend die Analyse aus 7.1:

    • Untersuchen Sie die Modellgüte beim Training.

    • Analysieren Sie die finale Karte.

    • Hat die Verwendung von dNBR die Ergebnisse verbessert?


Angfangen allgemeinen Code zu schreiben, aber aufgehört und auf Aufgaben konzentriert.


library(terra)

library(RColorBrewer)

library(randomForest)

library(caret)

library(dplyr)

————————————————————————————————

l9_050922.r <- rast("data/l9_050922.tif")

plotRGB(l9_050922.r, stretch="lin", main="Landsat 9 (2022-09-05)")

plot(l9_050922.r)


crs <- crs(l9_050922.r)

crs # UTM-Zone 33N


s2a_050922.r <- rast("data/s2a050922_clouds.tif")

plotRGB(s2a_050922.r, stretch="lin", main="Sentinel-2a (2022-09-05)")


s2b_070922.r <- rast("data/s2b070922_clouds.tif")

plotRGB(s2b_070922.r, stretch="lin", main="Sentinel-2b (2022-09-07)")

— —- —- —- —- - — - —- —- — - - —- —— - —— ——- - ——- ——

# 1 Pakete installieren und laden --------------------------------------------------

# Notwendige Pakete installieren

source("install_packages.R")

# Notwendige Bibliotheken laden

library(terra) # für die Arbeit mit Rasterdaten

library(RColorBrewer) # für Farbpaletten

library(randomForest) # der Random-Forest-Klassifikator

library(caret) # für Training, Validierung und Regressions-/Klassifikationsalgorithmen

library(dplyr) # zur Arbeit mit und Manipulation von Data Frames

# 2 Daten einlesen und vorbereiten --------------------------------------------------

# 2.1 Satellitendaten/Karten einlesen --------------------------------------------

# Landsat-9 vom 5. September 2022

l9_050922.r <- rast("data/l9_050922.tif")

plotRGB(l9_050922.r, stretch="lin", main="Landsat 9 (2022-09-05)")

plot(l9_050922.r)

# Das Koordinatenreferenzsystem der Landsat-Daten wird für alle anderen Datensätze verwendet

crs <- crs(l9_050922.r)

crs # UTM-Zone 33N

# Sentinel-2a vom 5. September 2022

s2a_050922.r <- rast("data/s2a050922_clouds.tif")

plotRGB(s2a_050922.r, stretch="lin", main="Sentinel-2a (2022-09-05)")

# Sentinel-2b vom 7. September 2022

s2b_070922.r <- rast("data/s2b070922_clouds.tif")

plotRGB(s2b_070922.r, stretch="lin", main="Sentinel-2b (2022-09-07)")

# 2.2 Ein Kompositbild aus Landsat + Sentinel-2 erstellen -----------------------

# Das Landsat-Bild als Basis für das Komposit verwenden

composite_0922.r <- l9_050922.r

# Sentinel-2-Daten auf die Landsat-Daten resamplen

s2a_050922.r <- resample(s2a_050922.r, composite_0922.r)

s2b_070922.r <- resample(s2b_070922.r, composite_0922.r)

# Ein Komposit erstellen, indem der Mittelwert aller drei Datenquellen gebildet und kombiniert wird

composite_0922.r$BLUE <- mean(c(l9_050922.r$BLUE, s2a_050922.r$Blue, s2b_070922.r$Blue), na.rm=TRUE)

composite_0922.r$GREEN <- mean(c(l9_050922.r$GREEN, s2a_050922.r$Green, s2b_070922.r$Green), na.rm=TRUE)

composite_0922.r$RED <- mean(c(l9_050922.r$RED, s2a_050922.r$Red, s2b_070922.r$Red), na.rm=TRUE)

composite_0922.r$NIR <- mean(c(l9_050922.r$NIR, s2a_050922.r$NIR, s2b_070922.r$NIR), na.rm=TRUE)

composite_0922.r$SWIR1 <- mean(c(l9_050922.r$SWIR1, s2a_050922.r$SWIR1, s2b_070922.r$SWIR1), na.rm=TRUE)

composite_0922.r$SWIR2 <- mean(c(l9_050922.r$SWIR2, s2a_050922.r$SWIR2, s2b_070922.r$SWIR2), na.rm=TRUE)

plot(composite_0922.r)

plotRGB(composite_0922.r, stretch="lin", main="L9 + S2 Komposit (2022-09)")

# 2.3 Corine Land Cover+ Backbone-Karte einlesen und reprojizieren --------------

# Rasterkarte einlesen

clc.r <- rast("data/CLCplus_RASTER_2021_010m_03035/CLCplus_RASTER_2021_010m_03035.tif")

# Legende einlesen

clc.lgd <- read.csv("data/CLCplus_RASTER_2021_010m_03035/CLCplus_RASTER_2021_010m_03035_legend.csv", sep=",")

clc.lgd

# Legende dem Raster zuweisen

levels(clc.r) <- clc.lgd

coltab(clc.r) <- clc.lgd[,-2]

plot(clc.r)

# CLC-Karte projizieren

clc.r <- project(clc.r, crs, method="near")

# Auf das Ausmaß des Untersuchungsgebiets zuschneiden

clc.r <- crop(clc.r, ext(composite_0922.r))

plot(clc.r)

# 2.4 Felddaten einlesen und in räumliche Objekte umwandeln -----------------------

# Felddaten zu Störungen und Composite Burn Index (CBI) einlesen

field.df <- read.csv("data/Beetz_etal_field_surveys2022-2023.csv")

field.df$disturbance <- as.factor(field.df$disturbance)

summary(field.df)

plot(field.df) # Dies plottet ein normales data.frame

# Die CBI-Felddaten von einem data.frame in einen räumlichen Vektor umwandeln

# Die Funktion erkennt automatisch die Spalten lon und lat als Koordinaten

field.v <- vect(field.df, crs="+proj=longlat +datum=WGS84")

plot(field.v, "disturbance") # Plottet nun den räumlichen Vektor in geografischen Koordinaten

# Projektion der Felddaten vom geografischen Koordinatensystem in das

# Koordinatensystem der Satellitendaten (Landsat-9)

field.v <- project(field.v, crs)

plot(field.v, "disturbance") # Plottet den räumlichen Vektor in UTM-Koordinaten

# 2.5 Daten gemeinsam in einer Karte darstellen ------------------------------------

# Felddaten über die Landbedeckungskarte legen

plot(clc.r, plg=list(title="Landbedeckung"), mar=c(2, 2, 2, 12))

plot(field.v, "disturbance", add=TRUE, col=c("black", "darkblue", "grey"),

plg=list(x="bottomright", bg="white", bty="o", title="Störung", cex=0.8))

# Landsat-Bild im Bereich der CBI-Felddaten darstellen

plotRGB(composite_0922.r, r=4, g=3, b=2, stretch="lin", axes=TRUE,

ext=ext(440000, 455000, 5632000, 5642000), mar=c(2, 2, 2, 1))

plot(field.v, "CBI", add=TRUE, col=brewer.pal(9, "Greys"),

plg=list(x="bottomleft", bg="white", bty="o", title="Brandschwere (CBI)", cex=0.8))

# Kannst du die Brandfläche des Feuers von 2022 erkennen?

# 3 Trainingsdatensatz für den Klassifikator erstellen -------------------------------

# Eine Kopie der Felddaten erstellen

data.df <- field.df

# Die Bänder des Landsat-Bildes zum Felddatensatz hinzufügen

xy <- crds(field.v) # Koordinaten aus den Felddaten extrahieren

data.df <- cbind(data.df, extract(composite_0922.r, xy))

data.df <- na.omit(data.df)

data.df$disturbance <- as.factor(data.df$disturbance)

table(data.df$disturbance) # Zeigt, wie viele Punkte wir pro Klasse haben

summary(data.df)

# Spektrale Indizes als potenzielle Merkmale berechnen

# Normalisierter Differenz-Vegetationsindex (NDVI)

NDVI <- function(x) (x$NIR - x$RED) / (x$NIR + x$RED)

data.df$NDVI <- NDVI(data.df)

summary(data.df)

# Betrachte die folgenden Plots und Ergebnisse

# --> Diskutiere, welches Merkmal geeignet wäre, um Brandflächen

# und Waldbestände mit abgestorbenen Fichten zu klassifizieren.

boxplot(NIR ~ disturbance, data=data.df)

boxplot(SWIR2 ~ disturbance, data=data.df)

boxplot(NDVI ~ disturbance, data=data.df)

cor(data.df[,9:15]) # Korrelation zwischen Merkmalen

# 4 Random-Forest-Klassifikator trainieren und validieren ------------------------------

# Im Folgenden verwenden wir die Funktion train() aus dem caret-Paket,

# um einen Random Forest zu trainieren, und trainControl(),

# um das Training mit Cross-Validation durchzuführen.

# Cross-Validation-Kontrolle für das Training einrichten

ctrl <- trainControl(

method = "cv", # Cross-Validation verwenden

number = 10, # Anzahl der Folds (k)

summaryFunction = multiClassSummary # Multi-Klassen-Leistungsmetriken verwenden

)

# Training des Random-Forest-Modells durchführen

model <- train(

disturbance ~ BLUE + GREEN + RED + NIR + SWIR1 + SWIR2 + NDVI,

# Klassifiziert die Störung basierend auf den Bändern und spektralen Indizes

data = data.df, # Verwendeter Datensatz

method = "rf", # Random Forest aus dem randomForest-Paket verwenden

trControl = ctrl # Gewählte Kontrollstrategie verwenden

)

model

confusionMatrix(model, norm="none") # Konfusionsmatrix ausgeben

# 5 Random-Forest-Modell auf das gesamte Untersuchungsgebiet anwenden --------------------

# NDVI für das gesamte Landsat-Bild berechnen

composite_0922.r$NDVI <- NDVI(composite_0922.r)

plot(composite_0922.r$NDVI) # Was siehst du? Was könnte problematisch sein?

# Das trainierte Random-Forest-Modell auf das gesamte Bild anwenden

disturbance.r <- predict(composite_0922.r, model, type="raw", na.rm=TRUE)

plot(disturbance.r, mar=c(2, 2, 2, 7))

# Was hältst du von diesem Klassifikationsergebnis?

# Was hat gut funktioniert? Was nicht?

# 6 CLC-Karte mit den zwei Störungsklassen aktualisieren ------------------------

# Die CLC-Karte mit neuen Klassen für Brandflächen und abgestorbene Fichten aktualisieren

clc_new.r <- resample(clc.r, composite_0922.r, method="near") # CLC-Karte auf Landsat-Auflösung resamplen

# Aktualisierung für die Brandfläche

clc_new.r[clc_new.r == "Woody needle leaved trees" & disturbance.r == "burnt"] <- 91

clc_new.r[clc_new.r == "Woody Broadleaved deciduous trees" & disturbance.r == "burnt"] <- 91

clc_new.r[clc_new.r == "Permanent herbaceous" & disturbance.r == "burnt"] <- 91

clc_new.r[clc_new.r == "Non and sparsely vegetated" & disturbance.r == "burnt"] <- 91

clc_new.r[clc_new.r == "Low-growing woody" & disturbance.r == "burnt"] <- 91

# Aktualisierung für abgestorbene Fichtenbestände

clc_new.r[clc_new.r == "Woody needle leaved trees" & disturbance.r == "deadSpruce"] <- 92

# Neue Legende und Farbtabelle erstellen

clc_new.lgd <- rbind(clc.lgd,

data.frame(i=91, class="Brandfläche", color="#000000"),

data.frame(i=92, class="Abgestorbene Fichte", color="#FF7777"))

levels(clc_new.r) <- clc_new.lgd

coltab(clc_new.r) <- clc_new.lgd[,-2]

plot(clc_new.r, mar=c(2, 2, 2, 9))

# Was hältst du von dieser 'finalen' Karte?

# Was hat gut funktioniert? Was hat nicht gut funktioniert?


# 1 Notwendige Pakete installieren

source("install_packages.R")

# Notwendige Bibliotheken laden

library(terra) # zum Arbeiten mit Rasterdaten

library(RColorBrewer) # für Farbpaletten

library(randomForest) # Random-Forest-Klassifikator

library(caret) # für Trainings-, Validierungs- und Regressionsalgorithmen

library(dplyr) # zum Arbeiten und Manipulieren von Data Frames


# 2 Daten einlesen und vorbereiten ---------------------------------------------

# 2.1 Satellitendaten / Karten einlesen -----------------------------------------

# Landsat-9 vom 5. September 2022, plottet: RGB, lineare Kontraststreckung, Titel

l9_050922.r <- rast("data/l9_050922.tif")

plotRGB(l9_050922.r, stretch="lin", main="Landsat 9 (2022-09-05)")

plot(l9_050922.r)


# Das Koordinatenreferenzsystem der Landsat-Daten wird für alle weiteren Datensätze verwendet

crs <- crs(l9_050922.r)

crs # UTM-Zone 33N


# Sentinel-2a vom 5. September 2022

s2a_050922.r <- rast("data/s2a050922_clouds.tif")

plotRGB(s2a_050922.r, stretch="lin", main="Sentinel-2a (2022-09-05)")


# Sentinel-2b vom 7. September 2022

s2b_070922.r <- rast("data/s2b070922_clouds.tif")

plotRGB(s2b_070922.r, stretch="lin", main="Sentinel-2b (2022-09-07)")

Aufgaben:

# Verbessern Sie das Ergebnis der Klassifikation und der finalen Karte.

# Führen Sie dazu die folgenden Analysen (7.1 und 7.2) durch:

------------------

7.1

NBR <- function(x) (x$NIR - x$SWIR2) / (x$NIR + x$SWIR2)

data.df$NBR <- NBR(data.df)

summary(data.df)

—————————————————————————————————-

a)

boxplot(NIR ~ disturbance, data=data.df)

boxplot(SWIR2 ~ disturbance, data=data.df)

boxplot(NBR ~ disturbance, data=data.df)

cor(data.df[,9:16]) # Korrelation zwischen den Merkmalen

—————————————————————————————————-

b)

model2 <- train(

disturbance ~ BLUE + GREEN + RED + NIR + SWIR1 + SWIR2 + NDVI + NBR,

data = data.df,

method = "rf",

trControl = ctrl

)

model2

confusionMatrix(model2, norm="none")

—————————————————————————————————

c)

print(model$results)

print(model2$results)


composite_0922.r$NBR <- NBR(composite_0922.r)

plot(composite_0922.r$NBR)

disturbance2.r <- predict(composite_0922.r, model2, type="raw", na.rm=TRUE)

plot(disturbance2.r, mar=c(2, 2, 2, 7))


composite_0922.r$NBR <- NBR(composite_0922.r)

plot(composite_0922.r$NBR)

disturbance2.r <- predict(composite_0922.r, model2, type="raw", na.rm=TRUE)

plot(disturbance2.r, mar=c(2, 2, 2, 7))


—————————————————————————————————-

d)

clc_new2.r <- resample(clc.r, composite_0922.r, method="near") # resample the CLC map to the Landsat image

clc_new2.r[clc_new2.r == "Woody needle leaved trees" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Woody Broadleaved deciduous trees" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Permanent herbaceous" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Non and sparsely vegetated" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Low-growing woody" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Woody needle leaved trees" & disturbance2.r == "deadSpruce"] <- 92

clc_new2.lgd <- rbind(clc.lgd,

data.frame(i=91, class="Burnt area", color="#000000"),

data.frame(i=92, class="Dead spruce", color="#FF7777"))

levels(clc_new2.r) <- clc_new2.lgd

coltab(clc_new2.r) <- clc_new2.lgd[,-2]

plot(clc_new2.r, mar=c(2, 2, 2, 9))

# Berechnen Sie den Normalized Burn Ratio (NBR) (siehe z. B. Beetz et al. 2024) auf

# die gleiche Weise, wie oben der NDVI berechnet wurde:

# NBR <- function(x) ...

# data.df$NBR <- NBR(data.df)

- - - - - - - - - - -

—> NBR berechnet

—————————————————————————————————

a)

# Untersuchen Sie die Korrelation des NBR mit anderen Merkmalen und wie gut sich

# der NBR zur Trennung der Störungsklassen eignet.

— - - - - - - - - - -

~ bedeutet „in Abhängigkeit von“.

NIR ~ disturbance zeigt die Verteilung des NIR-Bandes für jede Störungsklasse (disturbance).

erstellt Boxplot (mit Minimum, Median, Maximum, Ausreißer)

—————————————————————————————————

b)

# Trainieren Sie das Random-Forest-Modell erneut und verwenden Sie zusätzlich den NBR.

- - - - - - - - - - - - -

model2 <- train(

disturbance ~ BLUE + GREEN + RED + NIR + SWIR1 + SWIR2 + NDVI + NBR, # klassifiziert die Störung basierend auf den Bändern und spektralen Indizes

data = data.df, # der zu verwendende Datensatz

method = "rf", # Verwendung von Random Forest aus dem randomForest-Paket

trControl = ctrl # Verwendung der gewählten Kontrollparameter

)

model2

confusionMatrix(model2, norm="none") #prüfen, wie gut das Modell die Klassen richtig vorhersagt

————————————————————————————————

c)

Ziel: sehen, ob die Aufnahme von NBR die Klassifikation verbessert hat.

#Vergleich der Leistungsmetriken des Modelltrainings mit den vorherigen Ergebnissen (d.h. ohne NBR).

print(model$results) #model = ursprüngliches Modell ohne NBR

print(model2$results) #model2 = neues Modell mit NBR

#print(...$results) zeigt die Leistungskennzahlen des Modells an


# Anwendung des trainierten Modells auf das gesamte Kompositbild

composite_0922.r$NBR <- NBR(composite_0922.r) #berechnet

plot(composite_0922.r$NBR) #berechnet den Normalized Burn Ratio (NBR) für das gesamte Satellitenbild-Raster composite_0922.r

#plot(composite_0922.r$NBR) erzeugt eine Bildschirmdarstellung des neu berechneten NBR-Bands als Rastergrafik. Die Standard-Farbskala zeigt typischerweise hohe Werte (gesunde Vegetation) in Grün-/Gelbtönen und niedrige Werte (potenzielle Störungen/Brand) in Rot-/Dunkeltönen, was eine schnelle visuelle Einschätzung der Störungsverteilung ermöglicht.

#Vorhersage der Störungen:

disturbance2.r <- predict(composite_0922.r, model2, type="raw", na.rm=TRUE) #wendet das trainierte Random-Forest-Modell model2 (das nun NBR enthält) auf das gesamte Raster an.

plot(disturbance2.r, mar=c(2, 2, 2, 7))#rendert die vorhergesagte Störungskarte mit angepassten Randeinstellungen (mardefiniert Abstände: unten 2, links 2, oben 2, rechts 7 Zeilen für eine Legende)

—————————————————————————————————-

d) # Aktualisiere die CLC-Karte und diskutiere die Ergebnisse.

clc_new2.r <- resample(clc.r, composite_0922.r, method="near")

Funktion: Passt die Auflösung der ursprünglichen CLC-Karte (clc.r) an die des Landsat-Bildes (composite_0922.r) an. method="near" verwendet Nearest Neighbor-Interpolation (schnell, erhält Klassenwerte).

2. Klassenaktualisierung für Brandflächen (Klasse 91)

clc_new2.r[clc_new2.r == "Woody needle leaved trees" & disturbance2.r == "burnt"]

<- 91 # ... weitere Vegetationsklassen

Funktion: Ersetzt 5 verschiedene Vegetationsklassen (Nadelwald, Laubwald, Kräuter, etc.) durch neue Klasse 91 ("Burnt area"), wo die Störungsklassifikation "burnt" anzeigt.

3. Klassenaktualisierung für abgestorbene Fichten (Klasse 92)

clc_new2.r[clc_new2.r == "Woody needle leaved trees" & disturbance2.r == "deadSpruce"] <- 92

Funktion: Ersetzt Nadelwaldklassen speziell bei "deadSpruce"-Störung durch neue Klasse 92.

4. Neue Legendeneinträge hinzufügen

clc_new2.lgd <- rbind(clc.lgd, data.frame(i=91, class="Burnt area", color="#000000"), data.frame(i=92, class="Dead spruce", color="#FF7777"))

Funktion: Erweitert die bestehende Legende um zwei neue Klassen:

  • 91 = "Burnt area" (Schwarz #000000)

  • 92 = "Dead spruce" (Rot #FF7777)

5. Legende und Farbtabelle zuweisen

levels(clc_new2.r) <- clc_new2.lgd coltab(clc_new2.r) <- clc_new2.lgd[,-2]

Funktion:

  • levels() weist die vollständige Legende (inkl. Namen) zu

  • coltab() weist Farben zu (ohne Klassennamen-Spalte)

6. Finale Visualisierung

plot(clc_new2.r, mar=c(2, 2, 2, 9))

Funktion: Zeigt die aktualisierte CLC-Karte mit neuen Brand- (schwarz) und Totholz-Klassen (rot). mar=c(2,2,2,9) gibt extra Platz rechts für die erweiterte Legende.

Aufgaben:

# Verbessern Sie das Ergebnis der Klassifikation und der finalen Karte.

# Führen Sie dazu die folgenden Analysen (7.1 und 7.2) durch:

------------------

7.1

NBR <- function(x) (x$NIR - x$SWIR2) / (x$NIR + x$SWIR2)

data.df$NBR <- NBR(data.df)

summary(data.df)

—————————————————————————————————-

a)

boxplot(NIR ~ disturbance, data=data.df)

boxplot(SWIR2 ~ disturbance, data=data.df)

boxplot(NBR ~ disturbance, data=data.df)

cor(data.df[,9:16]) # Korrelation zwischen den Merkmalen

—————————————————————————————————-

b)

model2 <- train(

disturbance ~ BLUE + GREEN + RED + NIR + SWIR1 + SWIR2 + NDVI + NBR,

data = data.df,

method = "rf",

trControl = ctrl

)

model2

confusionMatrix(model2, norm="none")

—————————————————————————————————

c)

print(model$results)

print(model2$results)


composite_0922.r$NBR <- NBR(composite_0922.r)

plot(composite_0922.r$NBR)

disturbance2.r <- predict(composite_0922.r, model2, type="raw", na.rm=TRUE)

plot(disturbance2.r, mar=c(2, 2, 2, 7))


composite_0922.r$NBR <- NBR(composite_0922.r)

plot(composite_0922.r$NBR)

disturbance2.r <- predict(composite_0922.r, model2, type="raw", na.rm=TRUE)

plot(disturbance2.r, mar=c(2, 2, 2, 7))


—————————————————————————————————-

d)

clc_new2.r <- resample(clc.r, composite_0922.r, method="near") # resample the CLC map to the Landsat image

clc_new2.r[clc_new2.r == "Woody needle leaved trees" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Woody Broadleaved deciduous trees" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Permanent herbaceous" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Non and sparsely vegetated" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Low-growing woody" & disturbance2.r == "burnt"] <- 91

clc_new2.r[clc_new2.r == "Woody needle leaved trees" & disturbance2.r == "deadSpruce"] <- 92

clc_new2.lgd <- rbind(clc.lgd,

data.frame(i=91, class="Burnt area", color="#000000"),

data.frame(i=92, class="Dead spruce", color="#FF7777"))

levels(clc_new2.r) <- clc_new2.lgd

coltab(clc_new2.r) <- clc_new2.lgd[,-2]

plot(clc_new2.r, mar=c(2, 2, 2, 9))

# Berechnen Sie den Normalized Burn Ratio (NBR) (siehe z. B. Beetz et al. 2024) auf

# die gleiche Weise, wie oben der NDVI berechnet wurde:

# NBR <- function(x) ...

# data.df$NBR <- NBR(data.df)

- - - - - - - - - - -

—> NBR berechnet

—————————————————————————————————

a)

# Untersuchen Sie die Korrelation des NBR mit anderen Merkmalen und wie gut sich

# der NBR zur Trennung der Störungsklassen eignet.

— - - - - - - - - - -

~ bedeutet „in Abhängigkeit von“.

NIR ~ disturbance zeigt die Verteilung des NIR-Bandes für jede Störungsklasse (disturbance).

erstellt Boxplot (mit Minimum, Median, Maximum, Ausreißer)

—————————————————————————————————

b)

# Trainieren Sie das Random-Forest-Modell erneut und verwenden Sie zusätzlich den NBR.

- - - - - - - - - - - - -

model2 <- train(

disturbance ~ BLUE + GREEN + RED + NIR + SWIR1 + SWIR2 + NDVI + NBR, # klassifiziert die Störung basierend auf den Bändern und spektralen Indizes

data = data.df, # der zu verwendende Datensatz

method = "rf", # Verwendung von Random Forest aus dem randomForest-Paket

trControl = ctrl # Verwendung der gewählten Kontrollparameter

)

model2

confusionMatrix(model2, norm="none") #prüfen, wie gut das Modell die Klassen richtig vorhersagt

————————————————————————————————

c)

Ziel: sehen, ob die Aufnahme von NBR die Klassifikation verbessert hat.

#Vergleich der Leistungsmetriken des Modelltrainings mit den vorherigen Ergebnissen (d.h. ohne NBR).

print(model$results) #model = ursprüngliches Modell ohne NBR

print(model2$results) #model2 = neues Modell mit NBR

#print(...$results) zeigt die Leistungskennzahlen des Modells an


# Anwendung des trainierten Modells auf das gesamte Kompositbild

composite_0922.r$NBR <- NBR(composite_0922.r) #berechnet

plot(composite_0922.r$NBR) #berechnet den Normalized Burn Ratio (NBR) für das gesamte Satellitenbild-Raster composite_0922.r

#plot(composite_0922.r$NBR) erzeugt eine Bildschirmdarstellung des neu berechneten NBR-Bands als Rastergrafik. Die Standard-Farbskala zeigt typischerweise hohe Werte (gesunde Vegetation) in Grün-/Gelbtönen und niedrige Werte (potenzielle Störungen/Brand) in Rot-/Dunkeltönen, was eine schnelle visuelle Einschätzung der Störungsverteilung ermöglicht.

#Vorhersage der Störungen:

disturbance2.r <- predict(composite_0922.r, model2, type="raw", na.rm=TRUE) #wendet das trainierte Random-Forest-Modell model2 (das nun NBR enthält) auf das gesamte Raster an.

plot(disturbance2.r, mar=c(2, 2, 2, 7))#rendert die vorhergesagte Störungskarte mit angepassten Randeinstellungen (mardefiniert Abstände: unten 2, links 2, oben 2, rechts 7 Zeilen für eine Legende)

—————————————————————————————————-

d) # Aktualisiere die CLC-Karte und diskutiere die Ergebnisse.

clc_new2.r <- resample(clc.r, composite_0922.r, method="near")

Funktion: Passt die Auflösung der ursprünglichen CLC-Karte (clc.r) an die des Landsat-Bildes (composite_0922.r) an. method="near" verwendet Nearest Neighbor-Interpolation (schnell, erhält Klassenwerte).

2. Klassenaktualisierung für Brandflächen (Klasse 91)

clc_new2.r[clc_new2.r == "Woody needle leaved trees" & disturbance2.r == "burnt"]

<- 91 # ... weitere Vegetationsklassen

Funktion: Ersetzt 5 verschiedene Vegetationsklassen (Nadelwald, Laubwald, Kräuter, etc.) durch neue Klasse 91 ("Burnt area"), wo die Störungsklassifikation "burnt" anzeigt.

3. Klassenaktualisierung für abgestorbene Fichten (Klasse 92)

clc_new2.r[clc_new2.r == "Woody needle leaved trees" & disturbance2.r == "deadSpruce"] <- 92

Funktion: Ersetzt Nadelwaldklassen speziell bei "deadSpruce"-Störung durch neue Klasse 92.

4. Neue Legendeneinträge hinzufügen

clc_new2.lgd <- rbind(clc.lgd, data.frame(i=91, class="Burnt area", color="#000000"), data.frame(i=92, class="Dead spruce", color="#FF7777"))

Funktion: Erweitert die bestehende Legende um zwei neue Klassen:

  • 91 = "Burnt area" (Schwarz #000000)

  • 92 = "Dead spruce" (Rot #FF7777)

5. Legende und Farbtabelle zuweisen

levels(clc_new2.r) <- clc_new2.lgd coltab(clc_new2.r) <- clc_new2.lgd[,-2]

Funktion:

  • levels() weist die vollständige Legende (inkl. Namen) zu

  • coltab() weist Farben zu (ohne Klassennamen-Spalte)

6. Finale Visualisierung

plot(clc_new2.r, mar=c(2, 2, 2, 9))

Funktion: Zeigt die aktualisierte CLC-Karte mit neuen Brand- (schwarz) und Totholz-Klassen (rot). mar=c(2,2,2,9) gibt extra Platz rechts für die erweiterte Legende.

Aufgaben 7.2

Prüfen, ob das Difference Normalised Burn Ratio die Ergebnisse verbessern kann.

Zusätzlich die Landsat‑8- und Sentinel‑2-Bilder vom 10. September 2021 einlesen

und ein Komposit für 2021 erstellen.

—————————————————————————————————-

#

l8_100921.r <- rast("data/l8_100921.tif")

plotRGB(l8_100921.r, stretch="lin", main="Landsat 8 (2021-09-10)")

#

s2a_100921.r <- rast("data/s2a100921_clouds.tif")

plotRGB(s2a_100921.r, stretch="lin", main="Sentinel-2a (2021-09-10)")

#

composite_0921.r <- l8_100921.r

#

s2a_100921.r <- resample(s2a_100921.r, composite_0921.r)

#

composite_0921.r$Blue <- mean(c(l8_100921.r$Blue, s2a_100921.r$Blue), na.rm=TRUE)

composite_0921.r$Green <- mean(c(l8_100921.r$Green, s2a_100921.r$Green), na.rm=TRUE)

composite_0921.r$Red <- mean(c(l8_100921.r$Red, s2a_100921.r$Red), na.rm=TRUE)

composite_0921.r$NIR <- mean(c(l8_100921.r$NIR, s2a_100921.r$NIR), na.rm=TRUE)

composite_0921.r$SWIR1 <- mean(c(l8_100921.r$SWIR1, s2a_100921.r$SWIR1), na.rm=TRUE)

composite_0921.r$SWIR2 <- mean(c(l8_100921.r$SWIR2, s2a_100921.r$SWIR2), na.rm=TRUE)

plot(composite_0921.r)

plotRGB(composite_0921.r, stretch="lin", main="L8 + S2 composite (2021-09)")

————————————————————————————————-

composite_0921.r$NBR <- NBR(composite_0921.r)

plot(composite_0921.r$NBR)

colnames(data.df)[which(colnames(data.df) == 'NBR')] <- 'NBRpost'

data.df$NBRpre <- extract(composite_0921.r$NBR, xy[data.df$X])[,1]

colnames(data.df)[which(colnames(data.df) == 'NBR')] <- 'NBRpre'

data.df <- na.omit(data.df)

data.df$dNBR <- data.df$NBRpre - data.df$NBRpost

data.df$NBRpre <- NULL

data.df$NBRpost <- NULL

boxplot(NIR ~ disturbance, data=data.df)

boxplot(SWIR2 ~ disturbance, data=data.df)

boxplot(dNBR ~ disturbance, data=data.df)

cor(data.df[,9:16]) # correlation between features

————————————————————————————————-

model3 <- train(

disturbance ~ BLUE + GREEN + RED + NIR + SWIR1 + SWIR2 + NDVI + dNBR,

# classifies the disturbance based on the bands and spectral indices

data = data.df, # our dataset to be used

method = "rf", # use random forest from the randomForest package

trControl = ctrl # use the chosen control setup

)

model3

confusionMatrix(model3, norm="none")

————————————————————————————————-

print(model$results)

print(model2$results)

print(model3$results)

————————————————————————————————

composite_0922.r$dNBR <- composite_0921.r$NBR - composite_0922.r$NBR

plot(composite_0922.r$dNBR, range=c(-1,1))

disturbance3.r <- predict(composite_0922.r, model3, type="raw", na.rm=TRUE)

plot(disturbance3.r, mar=c(2, 2, 2, 7))

———————————————————————————————-

clc_new3.r <- resample(clc.r, composite_0922.r, method="near") # resample the CLC map to the Landsat image

clc_new3.r[clc_new3.r == "Woody needle leaved trees" & disturbance3.r == "burnt"] <- 91

clc_new3.r[clc_new3.r == "Woody Broadleaved deciduous trees" & disturbance3.r == "burnt"] <- 91

clc_new3.r[clc_new3.r == "Permanent herbaceous" & disturbance3.r == "burnt"] <- 91

clc_new3.r[clc_new3.r == "Non and sparsely vegetated" & disturbance3.r == "burnt"] <- 91

clc_new3.r[clc_new3.r == "Low-growing woody" & disturbance3.r == "burnt"] <- 91

clc_new3.r[clc_new3.r == "Woody needle leaved trees" & disturbance3.r == "deadSpruce"] <- 92

clc_new3.lgd <- rbind(clc.lgd,

data.frame(i=91, class="Burnt area", color="#000000"),

data.frame(i=92, class="Dead spruce", color="#FF7777"))

levels(clc_new3.r) <- clc_new3.lgd

coltab(clc_new3.r) <- clc_new3.lgd[,-2]

plot(clc_new3.r, mar=c(2, 2, 2, 9))

plot(clc_new.r, mar=c(2, 2, 2, 9))

plot(clc_new2.r, mar=c(2, 2, 2, 9))

plot(clc_new3.r, mar=c(2, 2, 2, 9))


a)

  1. Berechnet den NBR aus einem Multiband-Raster.

  2. Extrahiert Rasterwerte für Punktdaten.

  3. Berechnet dNBR zur Quantifizierung von Störungen.

  4. Bereinigt den Datensatz.

  5. Vergleicht Spektralbänder mittels Boxplots.

  6. Analysiert Korrelationen zwischen spektralen Merkmalen.

# Landsat-8 vom 10. September 2021

l8_100921.r <- rast("data/l8_100921.tif")

plotRGB(l8_100921.r, stretch="lin", main="Landsat 8 (2021-09-10)")

#rast() lädt eine Rasterdatei (GeoTIFF) in R. Hier wird ein Landsat 8 Bild vom 10. September 2021 geladen.

plotRGB() zeigt das Bild als Farbkomposit (RGB), stretch="lin" sorgt für lineare Kontrasterhöhung für bessere Sichtbarkeit.


#Sentinel-2a vom 10. September 2021

s2a_100921.r <- rast("data/s2a100921_clouds.tif")

plotRGB(s2a_100921.r, stretch="lin", main="Sentinel-2a (2021-09-10)")


#Erstellen eines kombinierten Bildes (Composite)

# Verwende das Landsat-Bild als Grundlage für das Kombinationsbild (Composite)

composite_0921.r <- l8_100921.r

#composite_0921.r wird als Basis das Landsat-Bild nehmen.

# Passe die Sentinel-2-Daten an die Auflösung und das Raster des Landsat-Bildes an

s2a_100921.r <- resample(s2a_100921.r, composite_0921.r)

#resample() passt die Auflösung des Sentinel-2 Bildes an die des Landsat-Bildes an, damit die Pixel direkt verglichen werden können.

#Layer zusammenführen: Ergebnis ein Landsat + Sentinel-2 Composite vom September 2021.

#Die Bänder Blue, Green, Red, NIR, SWIR1, SWIR2 werden pixelweise gemittelt, um ein kombiniertes Bild zu erzeugen.

#na.rm=TRUE ignoriert fehlende Werte.

—————————————————————————————————-

Berechne das differenzierte normalisierte Brandverhältnis (dNBR) aus den NBR-Werten von 2021 und 2022 (siehe z. B. Beetz et al. 2024 für Details).

composite_0921.r$NBR <- NBR(composite_0921.r)

plot(composite_0921.r$NBR) #NBR() berechnet den Normalized Burn Ratio (NBR).

composite_0921.r ist vermutlich ein Multiband-Raster.

Mit $NBR wird eine neue Raster-Band-Ebene namens "NBR" hinzugefügt. Das Ergebnis wird im Rasterobjekt gespeichert.

plot() visualisiert die neue NBR-Rasterebene.


colnames(data.df)[which(colnames(data.df) == 'NBR')] <- 'NBRpost'

👉 „Die Spalte NBR im Dataframe wird in NBRpost umbenannt.“

  • colnames(data.df) gibt alle Spaltennamen zurück.

  • which(...) sucht die Position der Spalte "NBR".

  • Diese wird in "NBRpost" umbenannt.

data.df$NBRpre <- extract(composite_0921.r$NBR, xy[data.df$X])[,1]

👉 „Für die angegebenen Koordinaten werden NBR-Werte aus dem Raster extrahiert und als NBRpre im Dataframe gespeichert.“

  • extract() liest Rasterwerte an bestimmten Koordinaten aus.

  • xy[data.df$X] enthält vermutlich Punktkoordinaten.

  • [,1] wählt die erste Spalte des Ergebnisses.

  • Die Werte werden als neue Spalte NBRpre gespeichert.

colnames(data.df)[which(colnames(data.df) == 'NBR')] <- 'NBRpre

Hier wird nochmal eine Spalte "NBR" in "NBRpre" umbenannt.

data.df <- na.omit(data.df)

  • Entfernt alle Zeilen mit NA-Werten.

👉 „Alle Beobachtungen mit fehlenden Werten werden aus dem Datensatz entfernt.“

data.df$dNBR <- data.df$NBRpre - data.df$NBRpost

  • Differenz zwischen Vorher- und Nachher-NBR.

  • dNBR wird oft zur Brandintensitätsanalyse verwendet.

👉 „Die Differenz zwischen NBRpre und NBRpost wird berechnet und als dNBR gespeichert.“

data.df$NBRpre <- NULL

data.df$NBRpost <- NULL

Entfernt beide Spalten aus dem Dataframe.

👉 „Die Zwischenvariablen NBRpre und NBRpost werden aus dem Datensatz gelöscht.“

boxplot(NIR ~ disturbance, data=data.df)

Formel-Schreibweise: Variable ~ Gruppierungsvariable

Zeigt die Verteilung von NIR getrennt nach disturbance-Klassen.

👉 „Es wird ein Boxplot erstellt, der die Verteilung von NIR-Werten nach Störungskategorie zeigt.“

boxplot(dNBR ~ disturbance, data=data.df)

→ Vergleich der Brandintensität zwischen Störungsklassen.

cor(data.df[,9:16])

[,9:16] wählt Spalten 9 bis 16.

cor() berechnet die Pearson-Korrelation.

Ergebnis ist eine Korrelationsmatrix.

👉 „Es wird die lineare Korrelation zwischen den Merkmalen in den Spalten 9 bis 16 berechnet.“

—————————————————————————————————-

Wiederhole anschließend die Analyse aus Abschnitt 7.1, betrachte die Modelltrainings‑Performance und untersuche die finale Karte. Hat die Verwendung des dNBR die Ergebnisse verbessert?

model3 <- train(

train()

Die Funktion stammt aus dem Paket caret. Sie trainiert ein Machine-Learning-Modell.

disturbance ~ BLUE + GREEN + RED + NIR + SWIR1 + SWIR2 + NDVI + dNBR

👉 Bedeutung:

disturbance = Zielvariable (abhängige Variable)

Alle anderen = Prädiktoren (Features)

In der Klausur:

"Es wird ein Klassifikationsmodell trainiert, das die Störungsklasse anhand spektraler Bänder und Indizes vorhersagt.“

data = data.df

Der Datensatz, der für das Training verwendet wird.

method = "rf"

  • "rf" steht für Random Forest

  • Ensemble-Methode aus vielen Entscheidungsbäumen

  • Reduziert Overfitting durch Bagging

In der Klausur:

„Es wird ein Random-Forest-Klassifikator verwendet.“

trControl = ctrl

  • ctrl enthält die Trainingsstrategie

  • z. B. Cross-Validation oder Bootstrapping

  • steuert Resampling und Modellbewertung

In der Klausur:

„Die Modellbewertung erfolgt mittels der definierten Cross-Validation-Einstellungen.“

model3

👉 Gibt Trainingsmetriken aus, z. B.:

Accuracy

Kappa

Resampling-Ergebnisse

In der Klausur:

„Die Modellleistung auf Basis der Resampling-Ergebnisse wird angezeigt.“

confusionMatrix(model3, norm="none")

Die Konfusionsmatrix zeigt:

True Positives

False Positives

True Negatives

False Negatives

norm="none" bedeutet:

→ Keine Normalisierung, absolute Zahlen

In der Klausur:Die Konfusionsmatrix zeigt die Klassifikationsleistung des Modells anhand der korrekt und falsch klassifizierten Beobachtungen.“

—————————————————————————————————-

Vergleiche die Leistungskennzahlen des Modelltrainings mit den vorherigen Ergebnissen (also ohne dNBR) und beschreibe, ob die Verwendung von dNBR den Klassifikator verbessert hat.

print(model$results)

print(model2$results)

print(model3$results)

Es werden die Trainings- und Validierungsergebnisse der drei Modelle ausgegeben, um ihre Performance zu vergleichen.“

————————————————————————————————

Wende das trainierte Modell auf das vollständige Kompositbild an.

composite_0922.r$dNBR <- composite_0921.r$NBR - composite_0922.r$NBR

🔎 Erklärung

Es wird die Differenz zwischen zwei NBR-Rastern berechnet.

composite_0921.r$NBR = NBR vor Störung

composite_0922.r$NBR = NBR nach Störung

Ergebnis = dNBR-Raster

Formel:

dNBR=NBRpre−NBRpost

dNBR=NBRpre​−NBRpost​

👉 Interpretation:

Hohe positive Werte → starke Vegetationsverluste

Werte nahe 0 → keine Veränderung

Negative Werte → Vegetationszunahme

📌 Klausurformulierung:

„Es wird ein Differenz-NBR-Raster erzeugt, das spektrale Veränderungen zwischen zwei Zeitpunkten quantifiziert.“

—————————————————————————————————-

Aktualisiere die CLC-Karte und diskutiere die Ergebnisse.

clc_new3.r <- resample(clc.r, composite_0922.r, method="near")

🔎 Erklärung

clc.r = ursprüngliche CORINE Land Cover (CLC)-Karte

composite_0922.r = Landsat-Raster

resample() passt:

Auflösung

Rastergröße

Pixelraster

an das Zielraster an.

🔹 method="near"

Nearest-Neighbour-Interpolation

Wichtig für kategoriale Daten

Verhindert Mischwerte

📌 Klausurformulierung:

„Die CLC-Karte wird mittels Nearest-Neighbour-Resampling an die räumliche Auflösung und Geometrie des Landsat-Rasters angepasst.“

clc_new3.r[clc_new3.r == "Woody needle leaved trees" & disturbance3.r == "burnt"] <- 91

🔎 Erklärung

Hier passiert logische Rastermaskierung:

Wenn:

Landbedeckung = bestimmter Vegetationstyp

UND Klassifikation = „burnt“

Dann:

Neue Klasse = 91

👉 Das bedeutet:

Bestimmte Landbedeckung wird überschrieben, wenn sie als verbrannt klassifiziert wurde.

Dasselbe wird für mehrere Vegetationstypen wiederholt.

📌 Klausur:

„Vegetationsklassen werden überschrieben, wenn das Random-Forest-Modell eine Störung (z. B. Brand) erkannt hat.“

clc_new3.r[clc_new3.r == "Woody needle leaved trees" & disturbance3.r == "deadSpruce"] <- 92

👉 Spezifische Baumart wird als neue Klasse definiert.

🔥 Wichtiges Konzept hier

Das ist eine kombinierte Klassifikation:

Basis = Landbedeckungskarte

Zusatzinformation = Störungskarte

Ergebnis = aktualisierte Landnutzungskarte

clc_new3.lgd <- rbind(clc.lgd,

data.frame(i=91, class="Burnt area", color="#000000"),

data.frame(i=92, class="Dead spruce", color="#FF7777"))

🔎 Erklärung

rbind() fügt neue Zeilen zur bestehenden Legende hinzu Neue Klassen:

91 = Burnt area

92 = Dead spruce

Mit Farbdefinition

📌 Klausur:

„Die bestehende Legendentabelle wird um neue Störungsklassen erweitert.“

levels(clc_new3.r) <- clc_new3.lgd

Weist Rasterklassen ihre Bezeichnungen zu.

coltab(clc_new3.r) <- clc_new3.lgd[,-2]

Weist Farbwerte zu.

[,-2] entfernt Spalte mit Klassennamen.

📌 Klausur:

„Den Rasterwerten werden Klassenbezeichnungen und Farbwerte zugewiesen.“

plot(clc_new3.r, mar=c(2, 2, 2, 9))

Visualisierung der finalen Landbedeckungskarte.

plot(clc_new.r, ...)

plot(clc_new2.r, ...)

plot(clc_new3.r, ...)

👉 Ziel:

Vergleich verschiedener Modellvarianten:

ohne Indizes

mit NDVI

mit NDVI + dNBR

Author

Stina O.

Information

Last changed