4.2. Vorbereitung der Daten: Einlesen und Bereinigung#

Bevor wir mit dem Bau eines Dashboards mit R Shiny beginnen können, müssen wir die Daten einholen und bearbeiten. Dazu werden verschiedene Datensätze – der Berliner Baumkataster sowie manuell dokumentierte Gießdaten – zusammengeführt und so aufbereitet, dass sie für weitere Analysen und Visualisierungen verwendet werden können.

Story

Amir möchte sich zunächst einen schnellen Überblick verschaffen: Wie werden Bäume in Berlin gegossen, und wie engagieren sich die Bürger:innen dabei? Bei seiner Recherche stößt er auf die Plattform Gieß den Kiez. Besonders beeindruckt ihn, wie anschaulich die Daten dort visualisiert sind (s. Abb. 4.1). Dies möchte er für seine eigene R-Shiny-Anwendung übernehmen.

Das Dashboard des Projektes "Gieß des Kiez" mit Visualisierungen zur Bewässerung von Bäumen in Berlin.

Fig. 4.3 Screenshot des Dashoards des Projekts Gieß den Kiez vom 13.03.2026.#

genauer beschreiben, was er will (weil GdK so toll ist): Karte, Zeitverlauf etc.* Das erklärt dann auch, warum er die Daten braucht, die er folgend einliest

Dazu benötigt er folgende Daten:

4.2.1. Daten laden, aggregieren und vorbereiten#

Laden der Baumkatasterdaten

Die Berliner Baumdaten werden über eine WFS-Schnittstelle (Web Feature Service) bezogen. Dabei werden sowohl Anlagenbäume als auch Straßenbäume geladen. Dies geschieht mit dem Befehl st_read.

# 1. Baumbestandsdaten laden
anlagenbaeume <- st_read("WFS:https://gdi.berlin.de/services/wfs/baumbestand", layer = "baumbestand:anlagenbaeume")
strassenbaeume <- st_read("WFS:https://gdi.berlin.de/services/wfs/baumbestand", layer = "baumbestand:strassenbaeume")

Laden und Bereinigung der Gießdaten

Die Gießdaten stammen aus einer CSV-Datei des Projekts „Gieß den Kiez“. Sie werden eingelesen und anschließend bereinigt, d. h.:

  • Ungültige oder fehlende Koordinaten (Längen-/Breitengrad) werden entfernt.

  • Datensätze ohne Straßenname oder mit fehlerhaften Gattungsbezeichnungen (z. B. numerische Werte) werden ausgeschlossen.

# 2. Bewässerungsdaten laden und bereinigen
df_clean <- read.csv("data/giessdenkiez_bewässerungsdaten.csv", sep = ";", stringsAsFactors = FALSE, fileEncoding = "UTF-8") %>%
  drop_na(lng, lat, bewaesserungsmenge_in_liter) %>%
  filter(strname != "Undefined" & strname != "" & !str_detect(gattung_deutsch, "[0-9]"))

Vereinheitlichung und Zusammenführung der Baumdaten

Die beiden Baumdatenquellen werden vereinheitlicht (gemeinsames Koordinatensystem EPSG:4326) und zusammengeführt. Danach werden die Koordinaten explizit extrahiert und die Geometriedaten, also die technische Standortinformation jedes Baumes in einem speziellen räumlichen Format (z. B. die geom-Spalte mit Einträgen wie c(" 394532.3", "5811461.0")) entfernt, um die Dateigröße zu reduzieren und die Weiterverarbeitung zu erleichtern.

# 3. Bäume zusammenführen
baumbestand <- bind_rows(anlagenbaeume, strassenbaeume) %>%
  st_transform(crs = 4326)

# 4. Geometrie extrahieren
coords <- st_coordinates(baumbestand$geom)
baumbestand$lng <- coords[, "X"]
baumbestand$lat <- coords[, "Y"]

Harmonisierung und Verknüpfung der Daten

Die eindeutige Baumkennung gisid wird so angepasst, dass sie mit der id aus den Gießdaten übereinstimmt (Unterstrich wird zu Doppelpunkt). Dadurch können die beiden Datensätze über einen sogenannten “Left Join” zusammengeführt werden.

# 5. Geometrie entfernen
baumbestand <- st_drop_geometry(baumbestand)

# 6. gisid anpassen (Unterstrich → Doppelpunkt)
baumbestand$gisid <- str_replace_all(baumbestand$gisid, "_", ":")

# 7. Bäume und Bewässerungsdaten mergen (über gisid = id)
df_merged <- baumbestand %>%
  left_join(df_clean %>% select(id, bewaesserungsmenge_in_liter, timestamp),
            by = c("gisid" = "id"))

Speichern der kombinierten Daten

Die aufbereiteten Daten werden als CSV-Datei gespeichert. Eine Ausgabe relevanter Kennzahlen (z. B. Anzahl verknüpfter Bäume) dient der Kontrolle.

# 8. Ergebnis speichern
write.csv2(df_merged, "data/df_merged_final.csv", row.names = FALSE, fileEncoding = "UTF-8")

Wenn Sie möchten, schauen Sie sich den gesamten Code als einen Block an, bevor wir Baumdaten den Berliner Bezirken zuordnen:

4.2.2. Geografische Zuordnung zu Berliner Bezirken#

Zielsetzung

Einige Bäume verfügen nicht über eine Angabe zu ihrem Bezirk. Um eine aggregierte räumliche Analyse (z. B. Gießverhalten nach Bezirk) zu ermöglichen, werden fehlende Bezirksangaben durch räumliches Verschneiden mit offiziellen Bezirkspolygonen ergänzt.

Methodik

  • Bäume ohne Bezirk werden in räumliche Objekte konvertiert (sf-Objekte).

  • Mittels eines „spatial join“ wird ermittelt, in welchem Bezirk sich jeder Baum befindet.

  • Das Ergebnis wird mit den ursprünglichen Daten wieder zusammengeführt.

Code Erklärung:

1. Die Bezirkskarte laden

bezirksgrenzen <- st_read("data/bezirksgrenzen.geojson")
  • Es wird eine digitale Karte geladen, auf der die Bezirksgrenzen Berlins eingezeichnet sind.

  • Jeder Bezirk hat dabei ein sogenanntes „Polygon“ – eine Art Umrisslinie.

2. Die Baumdaten laden

df_baeume <- read.csv("data/df_merged_final.csv", sep = ";", stringsAsFactors = FALSE)
  • Die Tabelle mit Baumdaten wird eingelesen. Jeder Eintrag beschreibt einen Baum: z. B. seine Art, Pflanzjahr und die Koordinaten, wo er steht.

  • Manche Bäume haben schon einen Bezirk eingetragen, andere nicht.

3. Koordinaten umwandeln

df_baeume <- df_baeume %>%
  mutate(
    lng = as.numeric(gsub(",", ".", lng)),
    lat = as.numeric(gsub(",", ".", lat))
  )
  • Manche Koordinaten sind falsch formatiert (mit Komma statt Punkt, z. B. „13,405“ statt „13.405“). Das wird korrigiert, damit der Computer die Zahlen richtig versteht.

4. Zwei Gruppen bilden:

df_mit_bezirk <- df_baeume %>% filter(!is.na(bezirk))
df_ohne_bezirk <- df_baeume %>% filter(is.na(bezirk) & !is.na(lng) & !is.na(lat))
  • Gruppe 1: Bäume, bei denen der Bezirk schon bekannt ist.

  • Gruppe 2: Bäume, bei denen der Bezirk fehlt, aber die Koordinaten vorhanden sind.

5. Gruppe ohne Bezirk in geografisches Format umwandeln

df_ohne_bezirk_sf <- st_as_sf(df_ohne_bezirk, coords = c("lng", "lat"), crs = 4326, remove = FALSE)
  • Die zweite Gruppe wird in ein spezielles Format (sogenannte sf-Objekte) umgewandelt.

  • Das ist notwendig, damit man mit Geodaten (Karten und Punkten auf Karten) arbeiten kann.

6. Bezirksgrenzen vorbereiten

bezirksgrenzen <- st_transform(bezirksgrenzen, crs = st_crs(df_ohne_bezirk_sf)) %>%
  rename(bezirk = Gemeinde_name)
  • Die Karte der Bezirke wird ins gleiche geografische System wie die Baumdaten gebracht (Koordinatensystem).

  • Außerdem wird der Name des Bezirksfeldes vereinfacht in „bezirk“.

7. Räumlicher Vergleich: Welcher Baum liegt in welchem Bezirk?

df_ohne_bezirk_joined <- st_join(df_ohne_bezirk_sf, bezirksgrenzen["bezirk"], left = TRUE)
  • Jetzt wird für jeden Baum ohne Bezirk geschaut, ob er innerhalb eines Bezirks liegt.

  • Dafür wird überprüft, welches Bezirks-Polygon (d. h. die digitale Umrissfläche eines Bezirks, die dessen geografische Grenzen als geschlossene Fläche auf der Karte abbildet) den jeweiligen Baum „einschließt”.

  • Dieser Vorgang heißt „spatial join“ – also ein räumliches Verbinden.

8. Ergebnis bereinigen und in normales Tabellenformat bringen

df_ohne_bezirk_filled <- df_ohne_bezirk_joined %>%
  mutate(bezirk = ifelse(is.na(bezirk.x), bezirk.y, bezirk.x)) %>%
  select(-bezirk.x, -bezirk.y) %>%
  st_drop_geometry()
  • Die berechneten Bezirksangaben werden in die Tabelle übernommen.

  • Zusätzliche technische Spalten werden entfernt.

  • Die geografischen Informationen werden wieder „fallen lassen“, damit es wieder eine normale Tabelle ist.

9. Beide Gruppen wieder zusammenfügen

df_baeume_final <- bind_rows(df_mit_bezirk, df_ohne_bezirk_filled)
  • Jetzt werden alle Bäume wieder in einer Tabelle vereint:

    • Die, die schon einen Bezirk hatten.

    • Und die, denen jetzt ein Bezirk zugeordnet wurde.

10. Neue, vollständige Tabelle speichern

write.csv2(df_baeume_final, file = "data/df_merged_final.csv", row.names = FALSE)
  • Die neue Tabelle mit allen Bäumen und Bezirken wird als Datei gespeichert.

Am Ende haben Sie wieder die Möglichkeit, sich den gesamten Code anzusehen, bevor wir im nächsten Kapitel die Entwicklungsumgebung auf den Bau des Dashboards vorbereiten.