-
untergeekDE authoreduntergeekDE authored
aktualisiere_karten.R 21.39 KiB
#' aktualisiere_karten.R
#'
#' Die Funktionen, um die Grafiken zu aktualisieren - und Hilfsfunktionen
#'
#---- Generiere den Fortschrittsbalken ----
generiere_auszählungsbalken <- function(anz = gezaehlt,max_s = stimmbezirke_n,ts = ts) {
fortschritt <- floor(anz/max_s*100)
annotate_str <- paste0("Ausgezählt sind ",
# Container Fake-Balken
"<span style='height:24px;display: flex;justify-content: space-around;align-items: flex-end; width: 100%;'>",
# Vordere Pufferzelle 70px
"<span style='width:70px; text-align:center;'>",
anz,
"</span>",
# dunkelblauer Balken
"<span style='width:",
fortschritt,
"%; background:#002747; height:16px;'></span>",
# grauer Balken
"<span style='width:",
100-fortschritt,
"%; background:#CCC; height:16px;'></span>",
# Hintere Pufferzelle 5px
"<span style='width:5px;'></span>",
# Ende Fake-Balken
"</span>",
"<br>",
" von ",max_s,
" Stimmbezirken - ",
"<strong>Stand: ",
format.Date(ts, "%d.%m.%y, %H:%M Uhr"),
"</strong>"
)
}
generiere_auszählung_nurtext <- function(anz = gezaehlt,max_s = stimmbezirke_n,ts = ts) {
fortschritt <- floor(anz/max_s*100)
annotate_str <- paste0("Ausgezählt: ",
anz,
" von ",max_s,
" Stimmbezirken - ",
"<strong>Stand: ",
format.Date(ts, "%d.%m.%y, %H:%M Uhr"),
"</strong>"
)
}
#---- Hilfsfunktionen: Switcher generieren, Farben anpassen ----
# Funktion gibt Schwarz zurück, wenn Farbe hell ist, und Weiß, wenn sie
# relativ dunkel ist.
font_colour <- function(colour) {
# convert color to hexadecimal format and extract RGB values
hex <- substr(colour, 2, 7)
r <- strtoi(paste0("0x",substr(hex, 1, 2)))
g <- strtoi(paste0("0x",substr(hex, 3, 4)))
b <- strtoi(paste0("0x",substr(hex, 5, 6)))
# suggested by chatGPT:
# calculate the brightness of the color using the formula Y = 0.2126*R + 0.7152*G + 0.0722*B
brightness <- 0.2126 * r + 0.7152 * g + 0.0722 * b
# compare the brightness to a reference value and return the result
return(ifelse(brightness > 128,"#000000","#FFFFFF"))
}
aufhellen <- function(Farbwert, heller = 128) {
#' Funktion gibt einen um 0x404040 aufgehellten Farbwert zurück
hex <- substr(Farbwert, 2, 7)
r <- strtoi(paste0("0x",substr(hex, 1, 2))) + heller
g <- strtoi(paste0("0x",substr(hex, 3, 4))) + heller
b <- strtoi(paste0("0x",substr(hex, 5, 6))) + heller
if (r > 255) r <- 255
if (g > 255) g <- 255
if (b > 255) b <- 255
return(paste0("#",as.hexmode(r),as.hexmode(g),as.hexmode(b)))
}
# Generiere den Linktext für den Switcher
link_text <- function(id,id_colour,text) {
lt = paste0("<a target='_self' href='https://datawrapper.dwcdn.net/",
id,"' style='background:",id_colour,
"; padding:1px 3px; border-radius:3px; color:",font_colour(id_colour),
"; cursor:pointer;' rel='nofollow noopener noreferrer'>",
text,"</a> ")
return(lt)
}
# Gibt einen String mit HTML/CSS zurück.
# Nutzt die Kandidierenden-Datei
generiere_switcher <- function(switcher_df,selected = 0) {
# Ist der Switcher auf 0 - der Stärkste-Kandidaten-Übersichtskarte?
if (selected == 0) {
text <- link_text(karte_sieger_id,"#F0F0FF","<strong>Sieger nach Stadtteil</strong>")
} else {
text <- link_text(karte_sieger_id,"#333333","Sieger nach Stadtteil")
}
for (i in 1:nrow(switcher_df)) {
if (i == selected) {
switcher_df$html[i] <- link_text(switcher_df$dw_id[i],
"#F0F0FF",
paste0("<strong>",switcher_df$Name[i],"</strong>"))
} else {
switcher_df$html[i] <- link_text(switcher_df$dw_id[i],
switcher_df$Farbwert[i],
switcher_df$Name[i])
}
}
return(paste0(text,paste0(switcher_df$html,collapse="")))
}
# HTML-Code für die Tooltipp-Mouseovers generieren
# Für alle Karten: Den jeweiligen Kandidaten in den Titel,
# orientieren an top (Anzahl der Top-Kandidaten),
# Bargraph-Code generieren.
#
# Die Mouseovers stehen in den Schlüsseln
# - visualize[["tooltip"]][["title"]]
# - visualize[["tooltip"]][["body"]]
karten_titel_html <- function(kandidat_s) {
# TBD
}
karten_body_html <- function(top = 5) {
# TBD
text <- "<p style='font-weight: 700;'>Ausgezählt: {{ meldungen_anz }} von {{ meldungen_max }} Wahllokalen{{ meldungen_max != meldungen_anz ? ' - Trendergebnis' : ''}}</p>"
# Generiere String mit allen Prozent-Variablen
prozent_s <- paste0(paste0("prozent",1:top),collapse=",")
# Tabellenöffnung
text <- text %>% paste0("<table style='width: 100%; border-spacing: 0.1em; border-collapse: collapse;'><thead>")
for (i in 1:top) {
text <- text %>% paste0("<tr><th>{{ kand",i,
" }}</th><td style='width: 120px; height=16px;'>",
"<div style='width: {{ ROUND(((prozent",i,
" / MAX(",
prozent_s,
")) *100),1) }}%; background-color: {{ farbe",i,
" }}; padding-bottom: 5%; padding-top: 5%; border-radius: 5px;'></div></div></td>",
"<td style='padding-left: 3px; text-align: right; font-size: 0.8em;'>{{ FORMAT(prozent",i,
", '0.0%') }}</td></tr>")
}
# Tabelle abbinden
text <- text %>% paste0(
"</thead></table>",
"<p>Wahlberechtigte: {{ FORMAT(wahlberechtigt, '0,0') }}, abgegebene Stimmen: {{ FORMAT(stimmen, '0,0') }}, Briefwahl: {{ FORMAT(stimmen_wahlschein, '0,0') }}, ungültig {{ FORMAT(ungueltig, '0,0') }}"
)
}
# Schreibe die Switcher und die Farbtabellen in alle betroffenen Datawrapper-Karten
vorbereitung_alle_karten <- function() {
# Vorbereiten: df mit Kandidierenden und Datawrapper-ids
# Alle Datawrapper-IDs in einen Vektor extrahieren
id_df <- config_df %>%
filter(str_detect(name,"_kand[0-9]+")) %>%
mutate(Nummer = as.integer(str_extract(name,"[0-9]+"))) %>%
select(Nummer,dw_id = value)#
# Mach aus der Switcher-Tabelle eine globale Variable
switcher_df <<- kandidaten_df %>%
select(Nummer, Vorname, Name, Parteikürzel, Farbwert) %>%
left_join(id_df, by="Nummer")
text_pre <- "<strong>Wählen Sie eine Karte über die Felder:</strong><br>"
text_post <- "<br><br>Klick auf den Stadtteil zeigt, wer dort führt"
balken_text <- generiere_auszählungsbalken(gezaehlt,stimmbezirke_n,ts)
dw_intro=paste0(text_pre,generiere_switcher(switcher_df,0),text_post)
# Farbskala und Mouseover anpassen
metadata_chart <- dw_retrieve_chart_metadata(karte_sieger_id)
visualize <- metadata_chart[["content"]][["metadata"]][["visualize"]]
# Farbwerte für die Kandidierenden
# erst mal löschen
visualize[["colorscale"]][["map"]] <- NULL
visualize[["colorscale"]][["map"]] <- setNames(as.list(kandidaten_df$Farbwert),
kandidaten_df$Name)
# Karten-Tooltipp anpassen
# visualize[["tooltip"]][["title"]] <- karten_title_html(kandidat_s)
visualize[["tooltip"]][["body"]] <- karten_body_html(top)
dw_edit_chart(karte_sieger_id,intro = dw_intro, annotate = balken_text, visualize = visualize)
# dw_data_to_chart()
# dw_publish_chart(karte_sieger_id)
# Jetzt die n Choroplethkarten für alle Kandidaten
# Müssen als Kopie angelegt sein.
for (i in 1:nrow(switcher_df)) {
dw_intro <- paste0(text_pre,generiere_switcher(switcher_df,0),text_post)
titel_s <- paste0(switcher_df$Vorname[i]," ",
switcher_df$Name[i]," (",
switcher_df$Parteikürzel[i],") - ",
"Ergebnis nach Stadtteil")
kandidat_s <- paste0(switcher_df$name[i],
" (",
switcher_df$Parteikürzel[i])
metadata_chart <- dw_retrieve_chart_metadata(switcher_df$dw_id[i])
visualize <- metadata_chart[["content"]][["metadata"]][["visualize"]]
# Zwei Farben: Parteifarbe bei Pos. 1, aufgehellte Parteifarbe
# (zu RGB jeweils 0x40 addiert) bei Pos. 0
visualize[["colorscale"]][["colors"]][[2]]$color <- switcher_df$Farbwert[i]
visualize[["colorscale"]][["colors"]][[2]]$position <- 1
visualize[["colorscale"]][["colors"]][[1]]$color <- aufhellen(switcher_df$Farbwert[i])
visualize[["colorscale"]][["colors"]][[1]]$position <- 0
# Karten-Tooltipp anpassen
# visualize[["tooltip"]][["title"]] <- karten_title_html(kandidat_s)
visualize[["tooltip"]][["body"]] <- karten_body_html(top)
dw_edit_chart(switcher_df$dw_id[i],
title = titel_s,
intro = dw_intro,
visualize = visualize,
annotate = balken_text)
# dw_data_to_chart()
# dw_publish_chart(switcher_df$dw_id)
}
}
#---- Generiere und pushe die Grafiken für Social Media
generiere_socialmedia <- function() {
# Fairly straightforward. Rufe zwei Datawrapper-Karten über die API auf,
# generiere aus ihnen PNGs, benenne sie mit Zeitstempel, schiebe die auf
# den Bucket und gib einen Text mit Link zurück.
#
# Die beiden Karten sind:
# - die Aufmacher-Grafik = top_id
# - die nüchterne Balkengrafik mit allen 20 Kandidat:innen S9BbQ
#
# Die Funktion aktualisiert KEINE Daten, sondern nimmt das, was gerade im
# Datawrapper ist. Das ggf extra mit dw_data_to_chart(meine_df,social1_id,parse_dates =TRUE)
#
# Setzt gültigen Zeitstempel ts voraus!
# Erste Grafik ist sowieso aktuell und wird nur anders exportiert.
# dw_data_to_chart(tag_df,social1_id,parse_dates =TRUE)
social1_png <- dw_export_chart(social1_id,type = "png",unit="px",mode="rgb", scale = 1,
width = 640, height = 640, plain = TRUE, transparent = T)
social1_fname <- paste0("png/social1_",format.Date(ts,"%Y-%m-%d--%H%Mh"),".png")
# Zweite Grafik muss aktualisiert und vermetadatet werden
# Metadaten anpassen: Farbcodes für Parteien
metadata_chart <- dw_retrieve_chart_metadata(social2_id)
# Save the visualise path
visualize <- metadata_chart[["content"]][["metadata"]][["visualize"]]
visualize[["color-category"]][["map"]] <-
setNames(as.list(kandidaten_df$Farbwert),
paste0(kandidaten_df$Name," (",
kandidaten_df$Parteikürzel,")"))
dw_edit_chart(chart_id = social2_id, visualize = visualize)
dw_data_to_chart(kand_tabelle_df, chart_id = social2_id)
social2_png <- dw_export_chart(social2_id,type = "png",unit="px",mode="rgb", scale = 1,
width = 640, height = 640, plain = TRUE, transparent = T)
social2_fname <- paste0("png/social2_",format.Date(ts,"%Y-%m-%d--%H%Mh"),".png")
# PNG-Dateien generieren...
if (!dir.exists("png")) {dir.create("png")}
magick::image_write(social1_png,social1_fname)
magick::image_write(social2_png,social2_fname)
#...und auf den Bucket schieben.
if (SERVER) {
system(paste0('gsutil -h "Cache-Control:no-cache, max_age=0" ',
'cp ',social1_fname,' gs://d.data.gcp.cloud.hr.de/', social1_fname))
system(paste0('gsutil -h "Cache-Control:no-cache, max_age=0" ',
'cp ',social2_fname,' gs://d.data.gcp.cloud.hr.de/', social2_fname))
}
linktext <- paste0("<a href='https://d.data.gcp.cloud.hr.de/",social1_fname,
"'>Download Social-Grafik 1 (5 stärkste)</a><br/>",
"<a href='https://d.data.gcp.cloud.hr.de/",social2_fname,
"'>Download Social-Grafik 2 (alle Stimmen)</a><br/>")
return(linktext)
}
#---- Datawrapper-Grafiken generieren ----
aktualisiere_top <- function(kand_tabelle_df,top=5) {
daten_df <- kand_tabelle_df %>%
arrange(desc(Prozent)) %>%
select(`Kandidat/in`,Stimmenanteil = Prozent) %>%
head(top)
# Daten pushen
dw_data_to_chart(daten_df,chart_id = top_id)
# Intro_Text nicht anpassen.
# Balken reinrendern
balken_text <- generiere_auszählungsbalken(gezaehlt,stimmbezirke_n,ts)
# Metadaten anpassen: Farbcodes für Parteien
metadata_chart <- dw_retrieve_chart_metadata(top_id)
# Save the visualise path
visualize <- metadata_chart[["content"]][["metadata"]][["visualize"]]
# Der Schlüssel liegt unter custom-colors als Liste
visualize[["custom-colors"]] <-
setNames(as.list(kandidaten_df$Farbwert),
paste0(kandidaten_df$Name," (",
kandidaten_df$Parteikürzel,")"))
dw_edit_chart(chart_id = top_id,annotate = balken_text, visualize=visualize)
dw <- dw_publish_chart(chart_id = top_id)
}
aktualisiere_tabelle_alle <- function(kand_tabelle_df) {
# Daten und Metadaten hochladen, für die Balkengrafik mit allen
# Stimmen für alle Kandidaten
dw_data_to_chart(kand_tabelle_df, chart_id = tabelle_alle_id)
balken_text <- generiere_auszählung_nurtext(gezaehlt,stimmbezirke_n,ts)
# Metadaten anpassen: Farbcodes für Parteien
metadata_chart <- dw_retrieve_chart_metadata(tabelle_alle_id)
# Save the visualise path
visualize <- metadata_chart[["content"]][["metadata"]][["visualize"]]
visualize[["columns"]][["Prozent"]][["customColorBarBackground"]] <- NULL
visualize[["columns"]][["Stimmen"]][["customColorBarBackground"]] <-
setNames(as.list(kandidaten_df$Farbwert),
kandidaten_df$Nummer)
# Irrtümlich waren die Werte auch noch in visualize[["custom-color"]] gespeichert.
visualize[["custom-colors"]] <- NULL
visualize[["color-category"]] <- NULL
dw_edit_chart(chart_id = tabelle_alle_id, annotate = balken_text, visualize = visualize)
dw_publish_chart(chart_id = tabelle_alle_id)
}
aktualisiere_karten <- function(ergänzt_df) {
# Als erstes die Übersichtskarte
cat("Aktualisiere Karten\n")
# Die noch überhaupt nicht gezählten Bezirke ausfiltern
ergänzt_f_df <- ergänzt_df %>% filter(meldungen_anz > 0)
balken_text = generiere_auszählungsbalken(gezaehlt,stimmbezirke_n,ts)
dw_edit_chart(chart_id = karte_sieger_id, annotate = balken_text)
dw_data_to_chart(ergänzt_f_df,chart_id = karte_sieger_id)
dw <- dw_publish_chart(karte_sieger_id)
# Jetzt die Choropleth-Karten für alle Kandidierenden
for (i in 1:nrow(switcher_df)) {
dw_edit_chart(chart_id=switcher_df$dw_id[i],annotate = balken_text)
dw_data_to_chart(ergänzt_f_df, chart_id = switcher_df$dw_id[i])
dw <- dw_publish_chart(switcher_df$dw_id[i])
}
cat("Karten neu publiziert\n")
}
aktualisiere_hochburgen <- function(hochburgen_df) {
# Das ist ziemlich geradeheraus.
dw_data_to_chart(hochburgen_df, chart_id = hochburgen_id)
balken_text <- generiere_auszählung_nurtext(gezaehlt,stimmbezirke_n,ts)
# Metadaten anpassen: Farbcodes für Parteien
metadata_chart <- dw_retrieve_chart_metadata(hochburgen_id)
# Save the visualise path
visualize <- metadata_chart[["content"]][["metadata"]][["visualize"]]
# Die Farben für die Kandidaten werden in dieser Tabelle nur für die Balkengrafiken
# in der Spalte "Prozent" benötigt und richten sich nach der Nummer.
visualize[["columns"]][["Prozent"]][["customColorBarBackground"]] <-
setNames(as.list(kandidaten_df$Farbwert),
kandidaten_df$Nummer)
# Irrtümlich waren die Werte auch noch in visualize[["custom-color"]] gespeichert.
visualize[["custom-colors"]] <- NULL
dw_edit_chart(chart_id = hochburgen_id, annotate = balken_text, visualize = visualize)
dw_publish_chart(chart_id = hochburgen_id)
cat("Hochburgen-Grafik neu publiziert\n")
}
aktualisiere_ergebnistabelle <- function(stadtteildaten_df) {
# Nr des Stadtteils, Stadtteil, Wahlbeteiligung (Info), Ergebnis
# Wahlbeteiligung und Ergebnis sind jeweils HTML-Text mit den Daten
# Unleserlich, aber funktional
e_tmp_df <- stadtteildaten_df %>%
select(nr,name,meldungen_anz:ncol(.)) %>%
# Nach Stadtteil sortieren
arrange(name) %>%
mutate(sort = row_number())
# Nochmal ansetzen, um als erste Zeile das Gesamtergebnis einzusetzen
ergebnistabelle_df <- e_tmp_df %>% summarize(nr = 0, sort = 0,
name = "GESAMTERGEBNIS",
across(meldungen_anz:ncol(.), ~sum(.,na.r =FALSE))) %>%
bind_rows(e_tmp_df) %>%
# Mit den Kandidaten-Namen anreichern
# Ins Langformat umformen, Nummer ist die Kandidatennummer
pivot_longer(cols=starts_with("D"),names_to = "Nummer", values_to = "Stimmen") %>%
mutate(Prozent = if_else(Stimmen == 0,0,Stimmen / gueltig * 100)) %>%
# D1... in Integer umwandeln
mutate(Nummer = as.numeric(str_extract(Nummer,"[0-9]+"))) %>%
left_join(kandidaten_df %>% select (Nummer, Vorname, Name, Parteikürzel),
by = "Nummer") %>%
mutate(`Kandidat/in` = paste0(Name," (",Parteikürzel,")")) %>%
# Kandidaten-Tabelle wieder zurückpivotieren
select(-Vorname, -Name, -Parteikürzel, -Nummer) %>%
# Nach Stadtteil gruppieren
group_by(sort,nr,name) %>%
# Zusätzliche Variable: Stadtteil gezählt oder TREND?
mutate(trend = meldungen_anz < meldungen_max) %>%
# Big bad summary - jeweils Daten aus den Spalten generieren
summarize(Stadtteil = paste0("<strong>",first(name),
"</strong><br><br>",
# TREND oder ERGEBNIS?
if_else(first(trend),
paste0("TREND: ",
first(meldungen_anz)," von ",
first(meldungen_max)," Stimmbezirken ausgezählt"),
#...oder alles ausgezählt?
paste0("Alle ",
first(meldungen_max),
" Stimmbezirke ausgezählt"))),
Wahlbeteiligung = paste0("Wahlberechtigt: ",
# Wenn noch nicht ausgezählt, leer lassen
if_else(first(trend),"",
first(wahlberechtigt) %>%
format(big.mark = ".", decimal.mark =",")),
"<br>",
"abg. Stimmen: ",first(stimmen) %>% format(big.mark = ".", decimal.mark =","),
" (",
# Nicht gezählte Bezirke haben 0 Wahlberechtigte
if_else(first(trend),"--",
(first(stimmen)/first(wahlberechtigt) *100) %>%
round(digits=1) %>% format(decimal.mark=",", nsmall = 1)),
"%)<br>",
"davon Briefwahl: ",
first(stimmen_wahlschein) %>% format(big.mark = ".", decimal.mark =","),
" (",
# Falls noch nicht alles ausgezählt, keinen Prozentwert angeben
if_else(first(trend),
"--",
(first(stimmen_wahlschein) / first(stimmen) * 100) %>%
round(digits = 1) %>% format(decimal.mark=",", nsmall = 1)),
"%)<br>",
"<br>Ungültig: ",
first(ungueltig) %>% format(big.mark = ".", decimal.mark =","),
"<br>Gültige Stimmen: ",
first(gueltig) %>% format(big.mark = ".", decimal.mark =",")),
# Hier geschachtelte paste0-Aufrufe:
# Der innere baut einen Vektor mit allen Kandidaten plus Ergebnissen
# Der äußere fügt diesen Vektor zu einem String zusammen (getrennt durch <br>)
Ergebnis = paste0(paste0("<strong>",`Kandidat/in`,"</strong>: ",
Stimmen %>% format(big.mark=".",decimal.mark=","),
" (",
Prozent %>% round(1) %>% format(decimal.mark=",", nsmall=1),"%)"),
collapse="<br>")
) %>%
ungroup() %>%
arrange(sort) %>%
select(-name,-sort)
dw_data_to_chart(ergebnistabelle_df %>% select(-nr), chart_id = tabelle_stadtteile_id)
# Trendergebnis? Schreibe "Trend" oder "Endergebnis" in den Titel
gezählt <- e_tmp_df %>% pull(meldungen_anz) %>% sum(.)
stimmbezirke_n <- e_tmp_df %>% pull(meldungen_max) %>% sum(.)
ts <- stadtteildaten_df %>% pull(zeitstempel) %>% first()
titel_s <- paste0(ifelse(gezählt < stimmbezirke_n,"TREND: ",""),
"Ergebnisse nach Stadtteil")
dw_edit_chart(chart_id = tabelle_stadtteile_id,title = titel_s,
annotate=generiere_auszählung_nurtext(gezählt,stimmbezirke_n,ts))
dw_publish_chart(tabelle_stadtteile_id)
cat("Ergebnistabelle nach Stadtteil publiziert\n")
return(ergebnistabelle_df)
}