Files
shiny-app/ui.R
2026-02-06 15:42:47 +01:00

473 lines
24 KiB
R
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
library(bsicons)
source("global.R")
ui <- function() {
page_fillable(
h1("Polizeiliche Kriminalstatistik Hamburg",
style = "color: #003064; background: #dae5ed; padding: 20px 0px 20px 20px; background-clip: padding-box; font-family: Lato,Arial,Helvetica,sans-serif;font-weight: 400", #text-align: center
class = "display-4"
),
tags$head(
tags$style(
HTML("
.selectize-dropdown-content .option {
border-bottom: 1px solid #dee2e6; /* Die Trennlinie */
padding: 8px 10px; /* Abstand zwischen Text und Linie */
}
.legend {
background: white !important; /* Der Hintergrund der Legenden-Box */
opacity: 1 !important; /* Die Box selbst ist nicht transparent */
}
/* Erzwingt den Dropdown-Pfeil auch bei Multiple Inputs */
.selectize-control.multi .selectize-input:after {
content: ' ';
display: block;
position: absolute;
top: 50%;
right: 15px;
margin-top: -3px;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #333 transparent transparent transparent;
}
/* Platz für den Pfeil lassen, damit Text nicht darunter rutscht */
.selectize-control.multi .selectize-input {
padding-right: 35px !important;
}
/* 1. GRUND-LAYOUT */
body {
background-color: #f4f7f9 !important; /* Ganz leichtes Blaugrau für Tiefe */
transform-origin: 0 0; /* Align to top-left for math accuracy */
width: 1920px; /* Match your dev monitor */
height: 1080px;
overflow: hidden;
position: absolute;
}
.shiny-plot-output {
width: inherit !important;
height: inherit !important;
max-width: none !important;
}
#app-container {
width: 1920px;
height: 1080px;
}
/* 2. DIE KARTE (HHMAP) */
#hhmap {
border-radius: 20px !important;
box-shadow: 0 12px 35px rgba(0,0,0,0.1) !important;
border: 5px solid white !important;
overflow: hidden !important;
}
/* 3. ALLE CARDS (Diagramme & Container) */
.card {
border-radius: 20px !important;
border: none !important;
box-shadow: 0 10px 30px rgba(149, 157, 165, 0.15) !important;
background-color: #ffffff !important;
transition: box-shadow 0.3s ease;
}
/* 4. SIDEBAR & INPUTS */
.sidebar {
background-color: #ffffff !important;
border-right: none !important;
box-shadow: 5px 0 25px rgba(0,0,0,0.03) !important;
border-radius: 0 25px 25px 0 !important;
}
.selectize-input {
border-radius: 12px !important;
border: 1px solid #e2e8f0 !important;
box-shadow: inset 1px 1px 3px rgba(0,0,0,0.02) !important;
padding: 10px 15px !important;
}
.selectize-input.focus {
border-color: #3182bd !important;
box-shadow: 0 0 0 3px rgba(49, 130, 189, 0.1) !important;
}
/* 5. BUTTONS (Falls du welche hast) */
.btn {
border-radius: 12px !important;
font-weight: 600 !important;
box-shadow: 0 4px 6px rgba(0,0,0,0.05) !important;
transition: background-color 0.2s, box-shadow 0.2s !important;
}
.btn:active {
transform: scale(0.98); /* Drück-Effekt */
}
/* 1. Das gesamte Accordion-Container-Design */
.accordion {
border: none !important;
background: transparent !important;
}
/* 2. Die einzelnen Accordion-Items (die 'Streifen') */
.accordion-item {
border: none !important;
margin-bottom: 15px !important; /* Abstand zwischen den Elementen für den Schweb-Effekt */
border-radius: 15px !important; /* Abgerundete Ecken für jedes Item */
box-shadow: 0 2px 10px rgba(0,0,0,0.05) !important; /* Weicher Schatten */
overflow: hidden;
background-color: #ffffff !important;
transition: box-shadow 0.2s ease;
}
.accordion-item:hover {
transform: none !important;
box-shadow: 0 6px 20px rgba(0,0,0,0.08) !important;
}
/* Erzeugt dauerhafte Fettschrift für alle Accordion-Titel */
.accordion-button {
font-weight: 600 !important; /* Extra fett */
font-size: 16px !important;
color: #1a1a1a !important;
background-color: #ffffff !important;
border: none !important;
box-shadow: none !important;
}
/* Wenn das Accordion ausgeklappt ist */
.accordion-button:not(.collapsed) {
font-weight: 600 !important;
background-color: #fcfcfc !important;
color: #000000 !important;
}
/* 4. Der Inhaltsbereich (wenn es offen ist) */
.accordion-body {
padding: 20px !important;
font-size: 14px;
line-height: 1.6;
color: #555;
background-color: #ffffff !important;
border-top: 1px solid #f0f0f0 !important; /* Dezente Trennung zum Header */
}
/* Speziell für geschachtelte Accordions (die inneren Ebenen) */
.accordion .accordion {
margin-top: 10px;
padding: 10px;
background-color: #fcfcfc !important; /* Leichter Kontrast zur äußeren Card */
border-radius: 15px;
}
/* Den Text in den Unterpunkten etwas eleganter machen */
.accordion-body p {
color: #444;
line-height: 1.7;
font-size: 15px;
padding: 5px;
}
.accordion-button h4 {
font-weight: 600 !important;
margin: 0 !important;
transition: color 0.2s ease; /* Macht den Farbwechsel beim Klicken weicher */
color: #1a1a1a !important;
}
/* Den aktiven Header der Unterpunkte hervorheben */
.accordion-button:not(.collapsed) h4 {
color: #2b74a9 !important;
}
/* 4. Der Hover-Effekt (Damit es beim Drüberfahren reagiert) */
.accordion-button:hover,
.accordion-button:hover h4 {
color: #666666 !important; /* Ein weiches Dunkelgrau beim Hovern */
}
")
)
),
tags$script(HTML("
$(document).on('shiny:connected', function() {
var targetWidth = 1920;
var targetHeight = 1080;
function scaleApp() {
var windowWidth = $(window).width();
var windowHeight = $(window).height();
// Calculate both ratios
var widthRatio = windowWidth / targetWidth;
var heightRatio = windowHeight / targetHeight;
// Use the smaller ratio to ensure the app fits entirely
var scale = Math.min(widthRatio, heightRatio);
$('body').css({
'transform': 'scale(' + scale + ')',
'width': targetWidth + 'px',
'height': targetHeight + 'px'
});
}
$(window).on('resize', scaleApp);
scaleApp();
});"
)),
navset_card_tab(
nav_panel("Karte",
page_sidebar(
layout_columns(
leafletOutput("hhmap"),
card(
div(
# h3 oder div dient als Block-Element und richtet seinen Inhalt (das span) rechtsbündig aus
style = "text-align: right; width: 100%; padding: 5px 10px 0 0;",
# Der Text wird in ein span verpackt und erhält die Border.
# Ein span nimmt nur den Platz ein, den der Inhalt benötigt (inline).
tags$span(
"2024",
style = "
border: 1px solid rgba(40,70,94,0.1);
border-radius: 5px;
padding: 2px 8px;
background-color: #eeeeee;
"
# padding ist der abstand an leerer Fläche in der reihenfolge top right bottom left
# farbe rgba ist die exakte Farbe der Trennlinien und Card-Umrandungen aus der App
)
),
div(
style ="padding: 0 15px; margin-top: 0px; overflow: hidden; display: block;",
h5("Statistisch am häufigsten polizeilich registrierte Straftaten",
style = "font-weight: 600; margin-top: 0px; margin-bottom: 25px; letter-spacing: -0.5px;"
),
uiOutput("txt_map_selection_bezirk"),
uiOutput("txt_map_selection_stadtteil"),
plotlyOutput("grph_top3", width = "484px", height = "667px")
)
),
col_widths = breakpoints(
sm = c(12, 12), # Auf kleinen Bildschirmen: untereinander (100% Breite)
md = c(8, 4) # Auf Desktop: nebeneinander (8 zu 4 Aufteilung)
)
),
sidebar = sidebar(
radioButtons(
inputId = "rd_maptype",
label = "Kartentyp",
choices = c("Bezirke", "Stadtteile"),
selected = "Bezirke"
),
selectizeInput(
inputId = "search",
label = tags$span(icon("search"),"Suche"),
choices = NULL,
selected = NULL,
multiple = FALSE, # Hier wahrscheinlich nur Einzelauswahl gewünscht
options = list(
placeholder = "Anfangen zu tippen...",
openOnFocus = FALSE,
allowEmptyOption = TRUE,
selectOnTab = FALSE,
plugins = list('dropdown_header')
)
),
tags$hr(style = "border-top: 2px solid #d7dbde; margin: 20px 0 0 0;"), # The divider line
div(
style = "margin-top: 20px; margin-bottom: 0px;",
tags$h5(
"Geografische Verteilung nach Fallzahlen",
style = "font-weight: 600; line-height: 1.1; margin-bottom: 5px;")
),
selectizeInput(
inputId = "heatmap",
label = "Wähle eine Straftat:",
choices = list_of_crimes,
selected = NULL,
options = list(
placeholder = "Keine Auswahl",
plugins = list('clear_button')
)
)
)
)
),
nav_panel("Interaktiver Vergleich",
layout_sidebar(
sidebar = sidebar(
title = "Vergleichs-Optionen",
width = 400,
uiOutput("vergleichs_inputs"),
selectizeInput(
"vergleich_location",
"Zu vergleichende Orte",
choices = auswahlmöglichkeiten,
multiple = TRUE
),
selectizeInput(
"vergleich_straftat",
"Zu vergleichende Straftaten",
choices = list_of_crimes,
multiple = TRUE
),
# 3. Gemeinsamer Input: Das Jahr
selectizeInput(
"vergleichs_jahr",
"Jahr wählen:",
choices = c(2024, 2023),
selected = 2024
)
),
card(
card_header(uiOutput("vergleichs_titel")), # Dynamischer Titel
plotlyOutput("vergleich_balkendiagramm")
)
)
),
nav_panel(
title = "Weiterführende Informationen",
div(
class = "accordion-scroll-container",
accordion(
open = FALSE, #schließt alle Panels beim Start
accordion_panel(
title = h3("Raubdelikte", style = "margin: 0;"),
value = "headline_raub",
accordion(
open = FALSE,
accordion_panel(
title = h4("Raub nach §§ 249ff. StGB", style = "margin: 0;"),
value = "raub_249",
p("Raubdelikte sind Straftaten, bei denen jemand eine fremde bewegliche Sache wegnimmt, indem er Gewalt anwendet oder mit Gewalt droht, um sie sich oder einem Dritten rechtswidrig zuzueignen.")
),
accordion_panel(
title = h4("Räuberische Erpressung nach § 255 StGB", style = "margin: 0;"),
value = "raub_255",
p("Räuberische Erpressung ist eine schwere Form der Erpressung, bei der Gewalt gegen eine Person oder die Drohung mit gegenwärtiger Lebens- oder Gesundheitsgefahr eingesetzt wird, um eine Vermögensverfügung zu erzwingen, wodurch der Täter wie ein Räuber (§ 249 StGB) bestraft wird, also mit mindestens einem Jahr Freiheitsstrafe.")
),
accordion_panel(
title = h4("Räuberischer Angriff auf Kraftfahrer nach § 316a StGB", style = "margin: 0;"),
value = "raub_316a",
p("Ein räuberischer Angriff auf Kraftfahrer ist ein Verbrechen, bei dem ein Täter unter Ausnutzung der besonderen Situation im Straßenverkehr Gewalt gegen den Fahrer oder Mitfahrer anwendet, um einen Raub, räuberischen Diebstahl oder eine räuberische Erpressung zu begehen.")
),
accordion_panel(
title = h4("Sonstige Raubüberfälle auf Straßen, Wegen oder Plätzen", style = "margin: 0;"),
value = "sonst_raub_249",
p("Sonstige Raubüberfälle auf Straßen, Wegen oder Plätzen gehören zu Raubdelikten in diesem Fall ebenfalls zur Straßenkriminalität. Die allgemeine Raubdefinition ergibt sich aus § 249 StGB.")
)
)
),
accordion_panel(
title = h3("Körperverletzung", style = "margin: 0;"),
value = "headline_körperverletzung",
accordion(
open = FALSE,
accordion_panel(
title = h4("Körperverletzung insgesamt", style = "margin: 0;"),
value = "körperverletzung_223",
p("Unter Körperverletzung versteht sich nach § 223 I HS 1 StGB, wenn eine andere Person körperlich misshandelt oder an der Gesundheit geschädigt wird. Unter den Deliktsummenschlüssel 'Körperverletzung insgesamt' fallen auch unter anderem gefährliche und schwere Körperverletzung.")
),
accordion_panel(
title = h4("Gefährliche Körperverletzung", style = "margin: 0;"),
value = "g_körperverletzung",
p("Gefährliche Körperverletzung ist nach § 224 I StGB eine Körperverletzung durch die Beibringung von Gift oder anderen gesundheitsschädlichen Stoffen, mittels einer Waffe oder eines anderen gefährlichen Werkzeugs, mittels eines hinterlistigen Überfalls, mit einem anderen Beteiligten gemeinschaftlich oder mittels einer das Leben gefährdenden Behandlung.")
),
accordion_panel(
title = h4("Schwere Körperverletzung", style = "margin: 0;"),
value = "s_körperverletzung",
p("Schwere Körperverletzung hat nach § 226 I StGB zur Folge, dass die verletzte Person das Sehvermögen auf einem Auge oder beiden Augen, das Gehör, das Sprechvermögen oder die Fortpflanzungsfähigkeit, ein wichtiges Glied des Körpers verliert oder dauernd nicht mehr gebrauchen kann oder in erheblicher Weise dauernd entstellt wird oder in Siechtum, Lähmung oder geistige Krankheit oder Behinderung verfällt.")
)
)
),
accordion_panel(
title = h3("Gewaltkriminalität", style = "margin: 0;"),
value = "headline_gewaltkriminalität",
accordion(
open = FALSE,
title = NULL,
value = "gewaltkriminalität",
p("Unter Gewaltkriminalität sind mehrere Deliktsbereiche umfasst. Dazu zählen Mord nach § 211 StGB, Totschlag und Tötung auf Verlangen nach §§ 212, 213, 216 StGB, Vergewaltigung, sexuelle Nötigung und sexueller Übergriff im besonders schweren Fall einschl. mit Todesfolge nach §§ 177, 178 StGB, Raub, räuberische Erpressung und räuberischer Angriff auf Kraftfahrer gem. §§ 249 bis 252, 255, 316a StGB, Körperverletzung mit Todesfolge gem. §§ 227, 231 StGB, Gefährliche und schwere Körperverletzung, Verstümmelung weiblicher Genitalien gem. §§ 224, 226, 226a, 231 StGB, Erpresserischer Menschenraub gem. § 239a StGB, Geiselnahme gem. § 239b StGB und Angriff auf den Luft- und Seeverkehr gem. § 316c StGB.")
)
),
accordion_panel(
title = h3("Diebstahl", style = "margin: 0;"),
value = "headline_diebstahl_insg",
accordion(
open = FALSE,
accordion_panel(
title = h4("Diebstahl insgesamt", style = "margin: 0;"),
value = "headline_diebstahl",
p("Unter Diebstahl insgesamt werden alle Formen des Diebstahls zusammengefasst, bei denen eine fremde bewegliche Sache in der Absicht weggenommen wird, sie sich oder einem Dritten rechtswidrig zuzueignen (Grundtatbestand § 242 StGB). Der Summenschlüssel bündelt sowohl einfache als auch qualifizierte Diebstahlsdelikte, etwa mit Waffen, bei Bandentätigkeit oder unter Ausnutzung besonderer Umstände.")
),
accordion_panel(
title = h4("Wohnungseinbruchsdiebstahl", style = "margin: 0;"),
value = "headline_wohnungseinbruchsdiebstahl",
p("Der Wohnungseinbruchsdiebstahl nach § 244 Absatz 1 Nummer 3 StGB erfasst Fälle, in denen der Täter in eine Wohnung einbricht, einsteigt oder mit einem falschen Schlüssel bzw. unter Ausnutzung eines anderen unbefugten Zugangs eindringt, um dort zu stehlen.")
),
accordion_panel(
title = h4("Diebstahl von Kraftwagen", style = "margin: 0;"),
value = "headline_diebstahl_kfz",
p("Diebstahl von Kraftwagen umfasst die rechtswidrige Wegnahme von Kraftfahrzeugen wie Pkw, Lkw oder Motorrädern, die zum selbständigen Fahren bestimmt sind.")
),
accordion_panel(
title = h4("Diebstahl an/aus Kraftfahrzeugen", style = "margin: 0;"),
value = "headline_diebstahl_aus_kfz",
p("Beim Diebstahl an oder aus Kraftfahrzeugen werden keine Fahrzeuge selbst entwendet, sondern Gegenstände, die an ihnen angebracht oder in ihnen enthalten sind, etwa Felgen, Navigationsgeräte oder im Fahrzeug zurückgelassene Wertsachen.")
),
accordion_panel(
title = h4("Diebstahl von Fahrrädern", style = "margin: 0;"),
value = "headline_diebstahl_fahrräder",
p("Der Diebstahl von Fahrrädern erfasst die unbefugte Wegnahme von Fahrrädern aller Art, einschließlich EBikes, soweit diese rechtlich nicht als Kraftfahrzeuge eingestuft sind.")
),
accordion_panel(
title = h4("Ladendiebstahl", style = "margin: 0;"),
value = "headline_ladendiebstahl",
p("Ladendiebstahl beschreibt Diebstähle in oder an Verkaufsräumen von Handelsbetrieben, bei denen Waren ohne Bezahlung an sich genommen werden.")
),
accordion_panel(
title = h4("Taschendiebstahl", style = "margin: 0;"),
value = "headline_taschendiebstahl",
p("Taschendiebstahl umfasst Diebstähle aus Kleidung oder direkt am Körper getragenen Behältnissen wie Taschen, Rucksäcken oder Geldbörsen, die meist unbemerkt in Menschenmengen oder öffentlichen Verkehrsmitteln begangen werden.")
)
)
),
accordion_panel(
title = h3("Sachbeschädigung", style = "margin: 0;"),
value = "headline_sachbeschädigung",
accordion(
open = FALSE,
title = NULL,
value = "sachbeschädigung",
p("Sachbeschädigung gem. § 303 I StGB ist die rechtswidrige Beschädigung oder Zerstörung einer fremden Sache. Darunter zählen auch digitale Daten und die Datenverarbeitung sowie Bauwerke und wichtige Arbeitsmittel.")
)
),
accordion_panel(
title = h3("Straftaten gegen das Aufenthaltsgesetz, das Asylgesetz und das Freizügigkeitsgesetz/EU", style = "margin: 0;"),
value = "headline_aufenth",
accordion(
open = FALSE,
title = NULL,
value = "aufenth",
p("Hierunter fallen die in § 95 Aufenthaltsgesetz (AufenthG) genannten Fälle. Unter anderem zählen dazu Personen, die sich ohne erforderlichen Aufenthaltstitel im Bundesgebiet aufhalten. Wer illegal einreist, gegen behördlichen Ausreise-, Aufenthalts- oder Meldeanordnung widerspricht und falsche oder unvollständige Angaben im Zusammenhang des Aufenthaltsrecht oder Asylrechts tätigt.")
)
),
accordion_panel(
title = h3("Rauschgiftdelikte", style = "margin: 0;"),
value = "headline_rauschgift",
accordion(
open = FALSE,
title = NULL,
value = "rauschgift",
p("Rauschgiftdelikte sind Straftaten, die gegen das Betäubungsmittelgesetz (BtMG) verstoßen, aber nicht unter die spezielleren Tatbestände des § 29 BtMG fallen. Dies betrifft insbesondere Fälle, in denen eine Person über 21 Jahren einer Person unter 18 Jahren Betäubungsmittel unerlaubt gibt, verabreicht oder zum unmittelbaren Konsum überlässt. Darüber hinaus betrifft es die Fälle, in denen Betäubungsmittel als Mitglied einer Bande gehandelt werden oder die Abgabe oder das Überlassen von Betäubungsmitteln zum leichtfertigen Tod einer Person führt.")
)
),
accordion_panel(
title = h3("Allgemeine Verstöße gem. § 29 BtMG -Konsumentendelikte-", style = "margin: 0;"),
value = "headline_konsumenten",
accordion(
open = FALSE,
title = NULL,
value = "konsumenten",
p("Zu den allgemeinen Verstößen gegen § 29 BtMG zählen unter anderem die Fälle des Absatzes 1 Satz 1 Nr. 1. Dazu gehört, wer Betäubungsmittel unerlaubt anbaut, herstellt, abgibt, erwirbt, sich auf sonstige Weise verschafft oder unerlaubt für deren Erwerb oder Gebrauch wirbt.")
)
),
accordion_panel(
title = h3("Illegaler Handel mit / Schmuggel von Rauschgiften gem. § 29 BtMG", style = "margin: 0;"),
value = "headline_schmuggel",
accordion(
open = FALSE,
title = NULL,
value = "schmuggel",
p("Illegalen Handel mit/Schmuggel von Rauschgiften betriebt, wer gemäß § 29 I S. 1 Nr. 1 BtMG unerlaubt mit Betäubungsmitteln Handel treibt, einführt, ausführt, veräußert und in den Verkehr bringt.")
)
)
)
)
)
)
)
}