Als ich vor einigen Jahren zum ersten Mal eine Tile Grid Map der USA sah, kam bei mir die Idee auf, eine solche im Deutschen als “Kachelkarte” oder “hexagonaler Karte” bezeichnete Karte für meine Heimatstadt Leipzig zu programmieren. Vor einigen Tagen habe ich mit dem R-Paket {tilemaps} eine Möglichkeit entdeckt, die insgesamt 63 Leipziger Ortsteile als Hexagone darzustellen. Die 63 Ortsteile verteilen sich auf 10 Stadtbezirke, welche in der nachfolgenden Karte farblich markiert sind.
Hexagonaler Stadtplan der Stadt Leipzig
“Kachelkarten” lassen sich u.a. für die Visualisierung statistischer Angaben verwenden. Die folgende Abbildung zeigt die Verteilung von Straftaten (ingesamt) auf die einzelnen Ortsteile (absolute Zahlen) aus dem Jahr 2023. Die Daten habe ich mit dem von mir entwickelten R-Paket {lisr} aus dem “Leipzig Informations-System” importiert.
Leipzig: Anzahl Straftaten im Jahr 2023
ANHANG: R-Code
Im Anhang dokumentiere ich den R-Code, mit dem die Daten importiert, umgeformt und visualisiert wurden.
knitr::opts_chunk$set(echo = FALSE,
message = FALSE,
warning = FALSE
)
# remotes::install_github("nrkoehler/lisr")
library(lisr)
library(tilemaps)
library(sf)
library(ggfittext)
library(tidyverse)
colsbz <- c(
"gold", # Nordwest
"#fe8f24", # Nord
"#ffc126", # Nordost
"steelblue1", # Altwest
"#4481fd", # West
"#f6dcb4", # Zentrum
"#dcafa9", # Ost
"seagreen3", # Suedwest
"yellowgreen", # Sued
"limegreen" # Suedost
)
df.OT <- get_lis_shapefile(shape = "Ortsteile")
df.CRIME <- get_lis_kd(kategorie_nr = 12) %>%
filter(SACHMERKMAL == "Straftaten insgesamt") %>%
select(Name = GEBIET, '2023') %>%
rename(Crime_Total = 2)
df.OT <- df.OT %>%
inner_join(df.CRIME, by = "Name")
set.seed(2011) #2022, 2017, 2011
data <- df.OT %>%
mutate(NR = as.numeric(str_sub(OT, 1, 1)),
BZ = factor(NR, levels = c(8, 9, 1, 7, 6, 0, 2, 5, 4, 3),
labels = c("Nordwest ", "Nord", "Nordost" , "Alt-West", "West", "Mitte", "Ost", "S\u00fcdwest ", 'S\u00fcd', "S\u00fcdost")
)) %>%
mutate(tile_map = generate_map(geometry, square = FALSE, flat_topped = FALSE, prop = 0.2,
interpolate = 0))
df.cent <- st_boundary(data$tile_map) %>%
st_coordinates() %>%
as_tibble() %>%
group_by(Name = L1) %>%
mutate(
XMIN = min(X),
XMAX = max(X),
YMIN = min(Y),
YMAX = max(Y)
) %>%
distinct(Name, XMIN, XMAX, YMIN, YMAX) %>%
ungroup() %>%
mutate(Name = str_replace(df.OT$Name, "-", "-\n"))
ggplot(data) +
geom_sf(aes(geometry = tile_map, fill = BZ), alpha = 1, color = "white", linewidth = 0.4) +
geom_fit_text(data = df.cent, aes(xmin = XMIN, xmax = XMAX, ymin = YMIN, ymax = YMAX, label = Name), reflow = F) +
scale_fill_manual(values = colsbz) +
theme_void() +
theme(plot.title = element_text(color="white", face = "bold",
hjust = 0.05, vjust=-2),
plot.subtitle = element_text(color="grey", hjust = 0.06, vjust=-4),
plot.caption = element_text(color="grey", vjust = 7, hjust = 0.98),
legend.position = "bottom",
legend.byrow = TRUE,
legend.text=element_text(color="white"),
plot.background = element_rect(fill="black", colour = "white")) +
labs(fill = NULL,
title = "Stadt Leipzig",
subtitle = "Stadtbezirke (10) und Ortsteile (63)",
caption = "Quelle: LIS"
)
ggplot(data) +
geom_sf(aes(geometry = tile_map, fill = Crime_Total), alpha = 1, color = "grey", linewidth = 0.4) +
geom_fit_text(data = df.cent, aes(xmin = XMIN, xmax = XMAX, ymin = YMIN, ymax = YMAX, label = Name), reflow = F) +
scale_fill_gradient(low="white", high="red2") +
theme_void() +
theme(plot.title = element_text(color="black", face = "bold",
hjust = 0.05, vjust=-2),
plot.subtitle = element_text(hjust = 0.06, vjust=-4),
plot.caption = element_text(vjust = 7, hjust = 0.98),
legend.position = "top",
legend.byrow = TRUE) +
labs(fill = "Anzahl Straftaten im Jahr 2023",
title = "Stadt Leipzig",
caption = "Quelle: LIS"
)







