---
title: ""

format: 
  html:
    fig-width: 9
    fig-height: 8
    fig-dpi: 300
    # embed-resources: TRUE # Entwicklung
    # theme: "null" # Entwicklung
    embed-resources: FALSE # Veröffentlichung
    theme: none # Veröffentlichung
    header-includes: |
     <link rel="stylesheet" href="https://media.sciencemediacenter.de/static/fonts/circular/web.css">
     <script>document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("a").forEach(function(a){a.setAttribute("target","_blank");});});</script>

mainfont: CircularSMCWeb

execute: 
  echo: FALSE
  output: asis
  warning: FALSE
  error: FALSE
---

```{r}
#| label: pakete_und_daten
#| output: false
library(tidyverse)
library(SMChelpR)
library(scales)
library(DT)
library(plotly)
library(htmltools)
library(mapview)


## wichtige Story-Parameter
storyNo <- 26038
file_name_code <- "2026_Mindestmenge_Knie.qmd"
caption_test <-
  "Quelle: ___, Berechnungen/Darstellung: Science Media Center Germany."
Datumsprefix_files <- FALSE # FALSE, wenn keine regelmäßige Aussendung / keine tagesaktuellen Daten

##############################

imagepath <-
  file.path("reports", storyNo)
dir.create(imagepath, showWarnings = FALSE, recursive = TRUE)

## workspace directory in RStudio interaktiv
if (rstudioapi::isAvailable()) {
  setwd(dirname(rstudioapi::getActiveDocumentContext()$path))
}

if (is.na(file_name_code)) {
  stop("Dateiname der qmd nicht gesetzt")
}
if (!file.exists(file_name_code)) {
  stop("qmd existiert nicht. Dateiname falsch?")
}


file.copy(
  from = file_name_code,
  to = file.path(
    imagepath,
    paste0(
      today()[Datumsprefix_files],
      "_"[Datumsprefix_files],
      file_name_code
    )
  ),
  overwrite = TRUE
)
```

```{r}
#| label: set-graphic-parameters
#| output: false
options(scipen = 999) # set scientific notation to be off
SMC_theme_ggplot()

if (exists("caption_")) {
  stop("Caption wurde nicht angepasst")
}


data_url <- paste0(
  "https://media.sciencemediacenter.de/reports/",
  storyNo,
  "/data/report_data"
)
```

```{r}
#| label: load configuration
#| output: false
# should probably go into SMChelpR package

#' Format Population Count for GeoJSON Output
#'
#' Formats a numeric population count with an HTML thin-space as thousand
#' separator. Returns `"keine Daten"` for `NA` values.
#'
#' Aligned with `format_EWZ()` in the Python API
#' (`intermediate_scripts/number_formatting.py`):
#' uses `<span style="font-size:50%;"> </span>` as thousand separator
#' and comma as decimal separator. Integer values use 0 decimals; non-integer
#' values use 2 decimals.
#'
#' @param x Numeric vector of population counts.
#'
#' @return Character vector of formatted population strings (may contain HTML).
#'
#' @examples
#' format_ewz(1471508)
#' format_ewz(NA)
#'
#' @export
format_ewz <- function(x) {
  small_space <- '<span style="font-size:50%;"> </span>'
  mapply(
    function(val) {
      if (is.na(val)) {
        return("keine Daten")
      }
      if (val == floor(val)) {
        formatC(
          val,
          format = "f",
          digits = 0,
          big.mark = small_space,
          decimal.mark = ","
        )
      } else {
        formatC(
          val,
          format = "f",
          digits = 1,
          big.mark = small_space,
          decimal.mark = ","
        )
      }
    },
    x,
    USE.NAMES = FALSE
  )
}

format_SMC <- function(x, digits = 1) {
  small_space <- '<span style="font-size:50%;"> </span>'
  mapply(
    function(val) {
      if (is.na(val)) {
        return("keine Daten")
      }
      if (val == floor(val)) {
        formatC(
          val,
          format = "f",
          digits = 0,
          big.mark = small_space,
          decimal.mark = ","
        )
      } else {
        formatC(
          val,
          format = "f",
          digits = digits,
          big.mark = small_space,
          decimal.mark = ","
        )
      }
    },
    x,
    USE.NAMES = FALSE
  )
}


#' Format DT columns using SMC number formatting
#'
#' Pipe-compatible wrapper that applies SMC-style number formatting
#' (thin-space thousand separator, comma decimal mark) to specified columns
#' of a DT datatable widget. Numeric sorting and filtering are preserved
#' because the JS renderer only affects the `"display"` type — raw numeric
#' values are returned for all other DataTables operations.
#'
#' Requires `escape = FALSE` in the `datatable()` call so the HTML
#' thin-space `<span>` is rendered correctly.
#'
#' Can be called multiple times on the same table with different columns
#' and parameters — each call appends a separate `columnDefs` entry.
#'
#' For styling (color, font weight, alignment, etc.), use DT's built-in
#' `formatStyle()` which can be chained after `formatSMC()`.
#' For percentages or currency symbols, see `formatPercentage()` and
#' `formatCurrency()`.
#'
#' @param table A DT datatable widget object.
#' @param columns Character vector of column names to format.
#' @param digits Integer or `NULL`. Fixed number of decimal places.
#'   `NULL` (default) auto-detects: 0 for integer values, 2 for
#'   non-integer values.
#' @param decimal_mark Character. Decimal separator. Default `","`.
#' @param big_mark Character. Thousands separator. The special value
#'   `"thin_space"` (default) uses an HTML thin-space
#'   (`<span style="font-size:50%;"> </span>`), matching
#'   `SMCReichbR::format_ewz`. Any other string is used literally
#'   (e.g. `"."`, `" "`).
#' @param na_text Character. Display text for `NA`/`null` values.
#'   Default `"keine Daten"`.
#'
#' @return The modified datatable widget.
#'
#' @examples
#' \dontrun{
#' data |>
#'     datatable(escape = FALSE) |>
#'     # SMC number formatting (thin-space separator, auto decimals)
#'     formatSMC(columns = c("Status Quo", "Mindestmenge 2028")) |>
#'     # Same formatting, but styled red + bold via DT::formatStyle
#'     formatSMC(columns = "Zusätzlich") |>
#'     formatStyle("Zusätzlich", color = "red", fontWeight = "bold")
#' }
formatSMC <- function(
  table,
  columns,
  digits = NULL,
  decimal_mark = ",",
  big_mark = "thin_space",
  na_text = "keine Daten"
) {
  # -- resolve big_mark --
  separator <- if (identical(big_mark, "thin_space")) {
    '<span style="font-size:50%;"> </span>'
  } else {
    big_mark
  }

  # -- escape strings for safe JS interpolation --
  js_esc <- function(s) gsub("'", "\\\\'", s)

  # -- digits logic: NULL -> auto-detect in JS, otherwise fixed --
  if (is.null(digits)) {
    digits_js <- "var decimals = (data === Math.floor(data)) ? 0 : 1;"
  } else {
    digits_js <- sprintf("var decimals = %d;", as.integer(digits))
  }

  # -- assemble the JS render function --
  js_lines <- c(
    "function(data, type, row, meta) {",
    "  if (type !== 'display') return data;",
    sprintf(
      "  if (data === null || data === undefined) return '%s';",
      js_esc(na_text)
    ),
    paste0("  ", digits_js),
    "  var parts = data.toFixed(decimals).replace('.', ',').split(',');",
    sprintf("  var sep = '%s';", gsub("\"", "\\\\\"", separator)),
    sprintf("  var decMark = '%s';", js_esc(decimal_mark)),
    "  parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, sep);",
    "  return parts.join(decMark);",
    "}"
  )

  js_render <- DT::JS(paste(js_lines, collapse = "\n"))

  # -- resolve column indices (0-indexed for DataTables JS) --
  col_indices <- which(names(table$x$data) %in% columns) - 1

  # -- append to existing columnDefs --
  existing_defs <- table$x$options$columnDefs %||% list()
  new_def <- list(list(targets = col_indices, render = js_render))
  table$x$options$columnDefs <- c(existing_defs, new_def)

  table
}
```

::: m-news-single__ce
- in den kommenden Jahren dürfte sich die Zahl der Kliniken, die orthopädische Eingriffe am Kniegelenk durchführen, durch neue gesetzliche Mindestfallzahlen verringern; damit verbunden sind längere Fahrzeiten zu den übrigbleibenden Häusern
- eine SMC-Analyse simuliert mit Blick auf Knieoperationen die neue Kliniklandschaft bis 2030 mit Erreichbarkeiten
- für die meisten Menschen bedeuten die neuen Mindestfallzahlen demnach keine spürbar längeren Fahrzeiten; zudem verbessert sich die Versorgung insgesamt

Seit Beginn dieses Jahres gelten in Deutschland neue Regeln für Mindestfallzahlen bei Knieoperationen. Ausgangspunkt ist ein [Entscheid](https://www.g-ba.de/presse/pressemitteilungen-meldungen/1303/){target="_blank"} des Gemeinsamen Bundesausschusses (G-BA). Bis 2030 sollen nun schrittweise bei drei Eingriffen am Knie die von den Kliniken jährlich mindestens zu erbringenden Fallzahlen erhöht werden. Dabei geht es um den vollständigen Einsatz eines künstlichen Kniegelenks (Knie-Tep), um einen Teilersatz des Gelenks durch eine sogenannte Schlittenprothese und um den Ersatz eines zuvor eingesetzten künstlichen Kniegelenks (Revision). Vor allem der Einsatz eines Knie-Teps zählt zu den häufigsten orthopädischen Eingriffen: 2024 wurden laut dem [Endoprothesenregister Deutschland](https://www.eprd.de/fileadmin/user_upload/Dateien/Publikationen/Berichte/AnnualReport2025_2026-03-31_F.pdf) beim Kniegelenk 173.252 Erstimplantationen und 17.456 Folgeeingriffe durchgeführt.

Für Knie-Teps gibt es bereits eine gesetzlich vorgeschriebene Mindestmenge von 50 Eingriffen pro Jahr und Klinik. Für die anderen Eingriffe bisher nicht. 2026 und 2027 bleiben diese Regeln noch bestehen. 2028 und 2029 gilt dann übergangsweise eine Mindestfallzahl von 100 Knie-Teps pro Jahr und ab 2030 müssen die Kliniken mindestens 150 solcher Fälle pro Jahr erbringen. Die neuen Mindestmengen für das Einsetzen einer Schlittenprothese sowie für Revisionseingriffe in Höhe von 20 beziehungsweise 25 gelten erstmalig ab dem Jahr 2028. Erfüllen die Kliniken die Mindestfallzahlen nicht, dürfen sie die Eingriffe nicht länger mit den gesetzlichen Krankenkassen abrechnen.

Das SMC hat nun untersucht, wie viele der Kliniken zuletzt schon die neuen Mindestmengen erfüllten und welche nicht. Eine Fahrzeitenanalyse zeigt, inwieweit sich die Erreichbarkeit der Kliniken für Patientinnen und Patienten verändert, wenn die Eingriffe nur noch in Häusern erfolgen, die die neuen Mindestmengen erreichen.

**Fallzahlen und Fahrzeiten können über die folgende interaktive Karte nachverfolgt werden. Sie soll vorwiegend als journalistische Recherchehilfe dienen, doch sie kann Patientinnen und Patienten auch konkret eine Orientierung vor einer Kniegelenkoperation bieten.**

<a href="reports/26038/data/knie-operationen.html?mtm_campaign=knie-op-karte-report&mtm_kwd=2026-04-23" target="_blank" style="display:inline-block;padding:10px 20px;background-color:#D7C455;color:white;font-weight:bold;text-decoration:none;border-radius:4px;">Interaktive Karte öffnen →</a>


```{r}
#| label: read kliniken
#| output: false
load(url(file.path(
  data_url,
  "knie_data.RData"
)))

# load("data/nobackup/knie_data.RData")

### tep

n_kliniken_mit_eingriffen_am_knie <- knie_data |>
  select(Standortnummer) |>
  distinct() |>
  nrow()

n_kliniken_mit_tep <- knie_data |>
  filter(Fallzahl_OPS_max_knie_tep > 0) |>
  distinct() |>
  nrow()

n_kliniken_mit_tep_ueber_50 <- knie_data |>
  filter(Fallzahl_OPS_max_knie_tep >= 50) |>
  distinct() |>
  nrow()
prozent_kliniken_mit_tep_ueber_50 <- n_kliniken_mit_tep_ueber_50 /
  n_kliniken_mit_tep *
  100

n_kliniken_mit_tep_unter_50 <- n_kliniken_mit_tep - n_kliniken_mit_tep_ueber_50
prozent_kliniken_mit_tep_unter_50 <- n_kliniken_mit_tep_unter_50 /
  n_kliniken_mit_tep *
  100

n_kliniken_mit_tep_ueber_100 <- knie_data |>
  filter(Fallzahl_OPS_max_knie_tep >= 100) |>
  distinct() |>
  nrow()
prozent_kliniken_mit_tep_ueber_100 <- n_kliniken_mit_tep_ueber_100 /
  n_kliniken_mit_tep *
  100
n_kliniken_mit_tep_unter_100 <- n_kliniken_mit_tep -
  n_kliniken_mit_tep_ueber_100
prozent_kliniken_mit_tep_unter_100 <- n_kliniken_mit_tep_unter_100 /
  n_kliniken_mit_tep *
  100

n_kliniken_mit_tep_ueber_150 <- knie_data |>
  filter(Fallzahl_OPS_max_knie_tep >= 150) |>
  distinct() |>
  nrow()
prozent_kliniken_mit_tep_ueber_150 <- n_kliniken_mit_tep_ueber_150 /
  n_kliniken_mit_tep *
  100
n_kliniken_mit_tep_unter_150 <- n_kliniken_mit_tep -
  n_kliniken_mit_tep_ueber_150
prozent_kliniken_mit_tep_unter_150 <- n_kliniken_mit_tep_unter_150 /
  n_kliniken_mit_tep *
  100

### uni

n_kliniken_mit_uni <- knie_data |>
  filter(Fallzahl_OPS_max_knie_uni > 0) |>
  distinct() |>
  nrow()

n_kliniken_mit_uni_ueber_20 <- knie_data |>
  filter(Fallzahl_OPS_max_knie_uni >= 20) |>
  distinct() |>
  nrow()
prozent_kliniken_mit_uni_ueber_20 <- n_kliniken_mit_uni_ueber_20 /
  n_kliniken_mit_uni *
  100
n_kliniken_mit_uni_unter_20 <- n_kliniken_mit_uni - n_kliniken_mit_uni_ueber_20
prozent_kliniken_mit_uni_unter_20 <- n_kliniken_mit_uni_unter_20 /
  n_kliniken_mit_uni *
  100

### revision

n_kliniken_mit_revision <- knie_data |>
  filter(Fallzahl_OPS_max_knie_revision > 0) |>
  distinct() |>
  nrow()

n_kliniken_mit_revision_ueber_25 <- knie_data |>
  filter(Fallzahl_OPS_max_knie_revision >= 25) |>
  distinct() |>
  nrow()
prozent_kliniken_mit_revision_ueber_25 <- n_kliniken_mit_revision_ueber_25 /
  n_kliniken_mit_revision *
  100
n_kliniken_mit_revision_unter_25 <- n_kliniken_mit_revision -
  n_kliniken_mit_revision_ueber_25
prozent_kliniken_mit_revision_unter_25 <- n_kliniken_mit_revision_unter_25 /
  n_kliniken_mit_revision *
  100
```

### Status quo

Laut den neuesten [Qualitätsberichten](https://qb-referenzdatenbank.g-ba.de/#/suche) aus dem Jahr 2024 gab es in Deutschland zuletzt `r format_SMC(n_kliniken_mit_eingriffen_am_knie)` Kliniken, die einen oder mehrere der genannten Eingriffe am Knie durchgeführt haben (Knie-Tep: `r format_SMC(n_kliniken_mit_tep)` Kliniken, Schlittenprothese: `r format_SMC(n_kliniken_mit_uni)`, Revision: `r format_SMC(n_kliniken_mit_revision)`). Für den Einsatz einer Schlittenprothese und für die Kniegelenksrevision gab es bisher keine Mindestmenge. Im Fall der Knie-Teps erfüllten zuletzt `r format_SMC(n_kliniken_mit_tep_ueber_50)` Kliniken (`r format_SMC(prozent_kliniken_mit_tep_ueber_50)` Prozent) die bisherige Mindestmenge von 50 Eingriffen pro Jahr. `r format_SMC(n_kliniken_mit_tep_unter_50)` Kliniken (`r format_SMC(prozent_kliniken_mit_tep_unter_50)` Prozent) erreichten diese Fallzahl nicht. Dies kann mehrere Gründe haben. So können die Kliniken auch [Ausnahmeregelungen](https://www.g-ba.de/downloads/62-492-4067/Mm-R_2025-12-18_iK-2026-01-01.pdf) bei den Landesbehörden erwirken, zum Beispiel bei Neuausrichtungen oder personellen Änderungen. Grundsätzlich sind Mindestmengen für die Kliniken aber verbindlich.

```{r}
#| label: read fahrzeiten - über Mindestmenge zuletzt
#| output: false
# polygon_data_Knie_TEP_ueber_Mindestmenge_2024 == 2024 in Qualitätsberichten
# polygon_data_Knie_TEP_ueber_Mindestmenge_zuletzt_2024 == Mindestmenge 50
load(url(file.path(
  data_url,
  "polygon_data_Knie_TEP_ueber_Mindestmenge_zuletzt_2024.RData"
))) # -> polygon_data

# load(
#   "data/nobackup/polygon_data_Knie_TEP_ueber_Mindestmenge_zuletzt_2024.RData"
# )

n_Einwohner_total <- sum(polygon_data$Einwohner_Gesamt, na.rm = TRUE)

n_Einwohner_tep_ueber_30min <- sum(
  polygon_data$Anzahl_Betroffene_30min,
  na.rm = TRUE
)

n_Einwohner_tep_ueber_30min_prozent <- n_Einwohner_tep_ueber_30min /
  n_Einwohner_total *
  100

n_Einwohner_tep_unter_30min <- n_Einwohner_total - n_Einwohner_tep_ueber_30min

n_Einwohner_tep_unter_30min_prozent <- n_Einwohner_tep_unter_30min /
  n_Einwohner_total *
  100

n_Einwohner_tep_unter_30min_prozent_zuletzt <- n_Einwohner_tep_unter_30min_prozent

n_Einwohner_tep_ueber_50min <- sum(
  polygon_data$Anzahl_Betroffene_50min,
  na.rm = TRUE
)

n_Einwohner_tep_ueber_50min_prozent <- n_Einwohner_tep_ueber_50min /
  n_Einwohner_total *
  100


n_Einwohner_tep_unter_50min <- n_Einwohner_total - n_Einwohner_tep_ueber_50min

n_Einwohner_tep_unter_50min_prozent <- n_Einwohner_tep_unter_50min /
  n_Einwohner_total *
  100


n_Einwohner_tep_ueber_60min <- sum(
  polygon_data$Anzahl_Betroffene_60min,
  na.rm = TRUE
)

n_Einwohner_tep_ueber_60min_prozent <- n_Einwohner_tep_ueber_60min /
  n_Einwohner_total *
  100

n_Einwohner_tep_unter_60min <- n_Einwohner_total - n_Einwohner_tep_ueber_60min

n_Einwohner_tep_ueber_60min_prozent <- n_Einwohner_tep_ueber_60min /
  n_Einwohner_total *
  100
n_Einwohner_tep_unter_60min_prozent <- n_Einwohner_tep_unter_60min /
  n_Einwohner_total *
  100
```

Für die allermeisten Einwohnerinnen und Einwohner sind Kliniken, die die aktuelle Mindestmenge von 50 Eingriffen pro Jahr erfüllen, schnell mit dem Auto erreichbar: `r format_SMC(n_Einwohner_tep_unter_30min_prozent)` Prozent benötigen unter 30 Minuten bis zur nächstgelegenen Klinik, die Knie-Teps einsetzt. `r format_SMC(n_Einwohner_tep_ueber_50min_prozent, digits=2)` Prozent fahren länger als 50 und nur `r format_SMC(n_Einwohner_tep_ueber_60min_prozent, digits=2)` Prozent länger als 60 Minuten.

### Kliniklandschaft 2028/2030

Ab dem Jahr 2028 gelten für die Kliniken verbindliche neue Mindestmengen. So müssen die Kliniken dann übergangsweise zunächst 100 Knie-Teps pro Jahr durchführen, um diesen Eingriff überhaupt abrechnen zu dürfen. Von den insgesamt `r format_SMC(n_kliniken_mit_tep)` Kliniken, die diese Operation anbieten, erreichten zuletzt bereits `r format_SMC(n_kliniken_mit_tep_ueber_100)` (`r format_SMC(prozent_kliniken_mit_tep_ueber_100)` Prozent) die neue Mindestmenge von 100 – insgesamt `r format_SMC(n_kliniken_mit_tep_unter_100)` Kliniken (`r format_SMC(prozent_kliniken_mit_tep_unter_100)` Prozent) lagen darunter.

Beim Einsatz einer Schlittenprothese liegt die neue Mindestmenge 2028 bei 20 pro Jahr. Das erreichten zuletzt schon `r format_SMC(n_kliniken_mit_uni_ueber_20)` Kliniken (`r format_SMC(prozent_kliniken_mit_uni_ueber_20)` Prozent) – insgesamt `r format_SMC(n_kliniken_mit_uni_unter_20)` Kliniken (`r format_SMC(prozent_kliniken_mit_uni_unter_20)` Prozent) lagen darunter. Bei der Kniegelenksrevision erfüllten bereits `r format_SMC(n_kliniken_mit_revision_ueber_25)` Kliniken (`r format_SMC(prozent_kliniken_mit_revision_ueber_25)` Prozent) die neue Mindestmenge von 25 Eingriffen pro Jahr, und `r format_SMC(n_kliniken_mit_revision_unter_25)` Kliniken (`r format_SMC(prozent_kliniken_mit_revision_unter_25)` Prozent) taten dies nicht.

Ab dem Jahr 2030 gilt für die Knie-Teps die volle neue Mindestmenge von 150 Eingriffen pro Jahr. `r format_SMC(n_kliniken_mit_tep_ueber_150)` Kliniken (`r format_SMC(prozent_kliniken_mit_tep_ueber_150)` Prozent) erreichten diese Fallzahl zuletzt bereits, `r format_SMC(n_kliniken_mit_tep_unter_150)` (`r format_SMC(prozent_kliniken_mit_tep_unter_150)` Prozent) Kliniken jedoch nicht. Mit dem Wegfall einiger Kliniken aus der Versorgung, würden sich die Fahrzeiten für die Bevölkerung geringfügig erhöhen.


[***Kliniken unterhalb und überhalb der künftigen Knie-Tep-Mindestmenge von 150 Fällen pro Jahr***]{.underline}


```{r}
#| label: map placeholder
library(mapgl)

geojson_dir <- "data/maps/geojson"

germany_mask_sf <- sf::read_sf(file.path(geojson_dir, "germany_mask.geojson"))
germany_outline_sf <- sf::read_sf(file.path(
  geojson_dir,
  "germany_outline.geojson"
))
landesgrenzen_sf <- sf::read_sf(file.path(
  geojson_dir,
  "landesgrenzen_polygone.geojson"
))
# polygon_data_status_quo <- sf::read_sf(file.path(
#   geojson_dir,
#   "status_quo_tep.geojson"
# ))
polygon_data_mindestmenge_150 <- sf::read_sf(file.path(
  geojson_dir,
  "mindestmenge_150_tep.geojson"
))

marker_unter_150 <- sf::read_sf(file.path(
  geojson_dir,
  "kh_unter_150_tep.geojson"
))

marker_ueber_150 <- sf::read_sf(file.path(
  geojson_dir,
  "kh_ueber_150_tep.geojson"
))

style_obj <- jsonlite::fromJSON(
  "https://tileserver.smc.report/styles/smc_customized/style.json",
  simplifyVector = FALSE
)

cols <- c(
  "#eff3ff",
  "#c6dbef",
  "#9ecae1",
  "#6baed6",
  "#4292c6",
  "#2171b5",
  "#084594"
)

fahrzeit_scale_expr <- step_expr(
  column = "Mittlere_Gewichtete_Fahrzeit",
  base = cols[1],
  values = c(10, 20, 30, 40, 50, 60),
  stops = cols[2:7],
  na_color = "#d3d3d3"
)

map <- maplibre(
  style = style_obj,
  projection = "mercator",
  center = c(10.4, 51.2),
  zoom = 5,
  minZoom = 4,
  maxZoom = 10,
  maxBounds = list(c(-10, 42), c(25, 58)),
  height = "700px"
) |>
  add_fill_layer(
    id = "germany-mask",
    source = germany_mask_sf,
    fill_color = "#ffffff",
    fill_opacity = 1
  ) |>
  add_fill_layer(
    id = "status_quo_tep",
    source = polygon_data_mindestmenge_150,
    fill_color = fahrzeit_scale_expr,
    fill_opacity = 0.6,
    fill_outline_color = "transparent",
    tooltip = get_column("label"),
    hover_options = list(
      fill_color = "#D7C455",
      fill_opacity = 0.6
    )
  ) |>
  add_line_layer(
    id = "germany-outline",
    source = germany_outline_sf,
    line_color = "#888888",
    line_width = 1.5
  ) |>
  add_line_layer(
    id = "state-borders",
    source = landesgrenzen_sf,
    line_color = "#555555",
    line_width = 1
  ) |>
  add_circle_layer(
    id = "kh_unter_150_tep",
    source = marker_unter_150,
    circle_color = "#cf2158",
    circle_radius = 4,
    circle_stroke_color = "white",
    circle_stroke_width = 1,
    tooltip = "label",
    hover_options = list(
      circle_radius = 6
    )
  ) |>
  add_circle_layer(
    id = "kh_ueber_150_tep",
    source = marker_ueber_150,
    circle_color = "#00429d",
    circle_radius = 4,
    circle_stroke_color = "white",
    circle_stroke_width = 1,
    tooltip = "label",
    hover_options = list(
      circle_radius = 6
    )
  ) |>
  add_categorical_legend(
    legend_title = "Fahrzeit",
    values = c(
      "0 - 10 Minuten",
      "10 - 20 Minuten",
      "20 - 30 Minuten",
      "30 - 40 Minuten",
      "40 - 50 Minuten",
      "50 - 60 Minuten",
      "\u00fcber 60 Minuten",
      "Keine Daten"
    ),
    colors = c(cols, "#d3d3d3"),
    position = "bottom-right",
    width = "210px",
    style = list(
      background_color = "#ffffff",
      background_opacity = 0.92,
      border_radius = 8,
      border_color = "#eeeeee",
      border_width = 1,
      padding = 10,
      shadow = TRUE,
      shadow_size = 8,
      shadow_color = "rgba(0,0,0,0.12)"
    ),
    margin_bottom = 24
  ) |>
  add_navigation_control(position = "top-right") |>
  add_fullscreen_control(position = "top-right") |>
  htmlwidgets::onRender(
    "
    function(el, x) {
      var map = this.getMap();
      map.on('load', function() {
        var labelAndBorderIds = [
          'germany-outline', 'state-borders',
          'label-place-village', 'label-place-town', 'label-place-city',
          'label-place-statecapital', 'label-place-capital',
          'label-boundary-state',
          'label-boundary-country-small', 'label-boundary-country-medium',
          'label-boundary-country-large',
          'label-street-primary', 'label-street-trunk', 'label-motorway-shield'
        ];
        labelAndBorderIds.forEach(function(id) {
          if (map.getLayer(id)) map.moveLayer(id);
        });
        if (map.getLayer('germany-mask')) map.moveLayer('germany-mask');
        ['kh_unter_150_tep', 'germany-outline', 'state-borders'].forEach(function(id) {
          if (map.getLayer(id)) map.moveLayer(id);
        });
      });
    }
    "
  )

map
```


```{r}
#| label: map data + caption
filename <- "Knie_Tep_Status_Quo_150"

caption_karte_1 <- "Quelle: Qualitätsberichte der Krankenhäuser (G-BA, 2024), BKG, Zensus 2022\nBerechnungen/Darstellung: Science Media Center Germany."

data_choropleth <- tibble(
  fileformat = c("geojson", "csv", "geojson", "csv"),
  filename_suffix = c("Kliniken", "Kliniken", "Fahrzeiten", "Fahrzeiten")
)

images_choropleth <- tibble(
  fileformat = c("html", "png"),
  filename_suffix = c("")
)

htmlpfad <- file.path(imagepath, paste0(filename, ".html"))
pngpfad <- file.path(imagepath, paste0(filename, ".png"))

datenpfad_markers <- file.path(imagepath, paste0(filename, "_Kliniken.geojson"))
datenpfad_polygons <- file.path(
  imagepath,
  paste0(filename, "_Fahrzeiten.geojson")
)
datenpfad_markers_csv <- file.path(imagepath, paste0(filename, "_Kliniken.csv"))
datenpfad_polygons_csv <- file.path(
  imagepath,
  paste0(filename, "_Fahrzeiten.csv")
)

mapshot(map, url = htmlpfad)

mapshot2(
  map,
  file = pngpfad,
  vwidth = 750,
  vheight = 750,
  delay = 10,
  zoom = 10,
  selector = ".maplibregl-map"
)

polygon_data_mindestmenge_150 |>
  sf::st_drop_geometry() |>
  write_csv(datenpfad_polygons_csv)

marker_unter_150 |>
  select(-c("GültigBis")) |>
  bind_rows(marker_ueber_150 |> select(-c("GültigBis"))) |>
  sf::st_drop_geometry() |>
  write_csv(datenpfad_markers_csv)

polygon_data_mindestmenge_150 |>
  sf::st_write(datenpfad_polygons, delete_dsn = TRUE, quiet = TRUE)

marker_unter_150 |>
  select(-c("GültigBis")) |>
  bind_rows(marker_ueber_150 |> select(-c("GültigBis"))) |>
  sf::st_write(datenpfad_markers, delete_dsn = TRUE, quiet = TRUE)

image_helper_light(
  filename = filename,
  filepath = imagepath,
  data = data_choropleth,
  images = images_choropleth,
  captions = caption_karte_1
)
```

*Die Karte zeigt alle Kliniken, die aktuell unter der künftigen Knie-Tep-Mindestmenge von 150 Fällen pro Jahr liegen (rot) und alle Kliniken, die darüber liegen (blau). Die roten Kliniken würden also eigentlich aus der Versorgung fallen. Die blaue Schattierung der Gemeinden gibt die voraussichtlichen Fahrzeiten 2030 an, wenn nur noch die blauen Kliniken an der Versorgung teilnehmen.*

[***Fahrzeitendifferenz nach Einführung der neuen Mindestmengen***]{.underline}

```{r}
#| label: Tabelle 1
#| output: true
read_csv("data/fahrzeiten_tabelle.csv") |>
  rename(
    "Betroffene (Mindest-menge 2030)" = "Betroffene (Mindestmenge 2030)"
  ) |>
  datatable(
    escape = FALSE,
    rownames = FALSE,
    options = list(
      searching = FALSE,
      paging = FALSE,
      autoWidth = TRUE,
      info = FALSE
    )
  ) |>
  formatSMC(
    columns = c(
      "Betroffene (Fallzahl 2024)",
      "Betroffene (Mindest-menge 2030)",
      "Betroffene (Differenz 2030 - 2024)"
    ),
    na_text = "-"
  )
```

<center>

</center>

*Diese Tabelle zeigt, wie viele Einwohnerinnen und Einwohner zuletzt beziehungsweise 2030 je nach Art des Eingriffs von Fahrzeiten unter und über 30 sowie unter und über 50 und 60 Minuten betroffen wären.*

Würden ab 2030 tatsächlich ausschließlich Kliniken an der Versorgung teilnehmen, die zuletzt schon die neue Mindestmenge von 150 Knie-Teps erfüllten, wären 188.526 Einwohnerinnen und Einwohner zusätzlich von Fahrzeiten über einer Stunde betroffen. Insgesamt wären es 208.162. Im Fall der Knie-Schlittenprothese wären es zusätzlich 448.322 Einwohnerinnen und Einwohner (insgesamt 507.506) und für eine mindestmengengerechte Knie-Revision müssten zusätzlich 312.077 Personen länger als eine Stunde fahren (insgesamt 332.304).

Je nach Gemeinde wären die Fahrzeitenverlängerungen teils sehr unterschiedlich. So kommt es in einigen wenigen Gemeinden in den nächsten fünf Jahren voraussichtlich zwar zu einem Fahrzeitenplus von fast einer Stunde, allerdings betrifft dies oftmals nur wenige Personen. Grundsätzlich sind wie zu erwarten eher dünn besiedelte Regionen betroffen, etwa in Mecklenburg-Vorpommern, in den östlichen Bundesländern allgemein sowie in Teilen von Niedersachsen und Bayern. In NRW ergeben sich demnach kaum nennenswerte Fahrzeitenveränderungen.

[***Fahrzeiten in den Gemeinden am Beispiel Knie-Tep***]{.underline}

```{r}
#| label: Tabelle 2
#| output: true
read_csv("data/fahrzeitenaenderung_gemeinde_tabelle.csv") |>
  rename("Gemeinde" = "Gemeindename") |>
  rename("Einwohner: innen (Gesamt)" = "Einwohner:innen (Gesamt)") |>
  datatable(
    escape = FALSE,
    rownames = FALSE,
    options = list(
      pageLength = 10,
      lengthMenu = c(10, 25, 50, 100),
      autoWidth = TRUE
    )
  ) |>
  formatSMC(
    columns = c(
      "Fahrzeit (Fallzahl 2024)",
      "Fahrzeit (Mindestmenge 2030)",
      "Fahrzeit (Differenz 2030 - 2024)",
      "Einwohner: innen (Gesamt)"
    )
  ) |>
  formatSMC(columns = c("Einwohner:innen (Gesamt)"))
```

<center>

</center>

*Die Tabelle zeigt die mittleren Fahrzeiten in Minuten in den Gemeinden zur nächstgelegenen Klinik, die Knie-Teps durchführt. Angegeben ist der Status quo, das Mindestmengen-Szenario ab 2030, die Differenz sowie die jeweilige Zahl der Einwohnerinnen und Einwohner. Die Tabelle lässt sich nach Gemeinden durchsuchen.*

```{r}
#| label: Auslese ueber Mindestmenge
#| output: true
load(url(file.path(
  data_url,
  "polygon_data_Knie_TEP_2024.RData"
))) # -> polygon_data

n_Einwohner_total <- sum(polygon_data$Einwohner_Gesamt, na.rm = TRUE)

load(url(file.path(
  data_url,
  "polygon_data_Knie_TEP_ueber_Mindestmenge_2024.RData"
))) # -> polygon_data

# n_Einwohner_total <- sum(polygon_data$Einwohner_Gesamt, na.rm = TRUE)

n_Einwohner_tep_ueber_30min <- sum(
  polygon_data$Anzahl_Betroffene_30min,
  na.rm = TRUE
)

n_Einwohner_tep_ueber_30min_prozent <- n_Einwohner_tep_ueber_30min /
  n_Einwohner_total *
  100

n_Einwohner_tep_unter_30min <- n_Einwohner_total - n_Einwohner_tep_ueber_30min

n_Einwohner_tep_unter_30min_prozent <- n_Einwohner_tep_unter_30min /
  n_Einwohner_total *
  100
```

Obwohl voraussichtlich mehrere Kliniken in den kommenden Jahren aus der Kniegelenksversorgung fallen, würden 2030 immer noch `r format_SMC(n_Einwohner_tep_unter_30min_prozent)` Prozent aller Einwohnerinnen und Einwohner eine Klinik mit entsprechender Knie-Tep-Mindestmenge in unter 30 Minuten erreichen. Noch einmal zum Vergleich: Zuletzt waren es `r format_SMC(n_Einwohner_tep_unter_30min_prozent_zuletzt)` Prozent. Für die meisten Menschen bedeuten die neuen Mindestfallzahlen also keine spürbar längeren Fahrzeiten. Und selbst dort, wo die Fahrzeiten länger werden, spricht mit Blick auf die Behandlungsqualität vieles für den Weg in die weiter entfernte Klinik mit zum Teil deutlich höheren Fallzahlen.

### Spezialisierung und Fallzahlen

Die Mindestmengenregelung soll sicherstellen, dass bestimmte medizinische Eingriffe nur noch dort stattfinden, wo ausreichend Erfahrung vorhanden ist, um die Qualität der Behandlung und die Sicherheit der Patientinnen und Patienten zu erhöhen. Für Kniegelenkoperationen ist eine bessere Behandlungsqualität bei höheren Fallzahlen dem Institut für Qualität und Wirtschaftlichkeit im Gesundheitswesen (IQWiG) zufolge [nachgewiesen](https://www.iqwig.de/download/v21-01_zusammenhang-leistungsmenge-und-qualitaet-bei-implantation-von-knie-teps_rapid-report_v2-0.pdf). Und aktuelle [Statistiken](https://www.eprd.de/fileadmin/user_upload/Dateien/Publikationen/Berichte/Short-presentation-AnnualReport2025_2026_03_31_F.pdf) aus dem freiwilligen Endoprothesenregister Deutschland, an das 2025 insgesamt 721 Kliniken Daten übermittelten, zeigen bei Hüft- und Kniegelenksoperationen abhängig von der Häufigkeit der Eingriffe zum Beispiel unterschiedlich stark steigende Raten von Revisionsoperationen nach dem Einsetzen der ersten Prothese.

Große wissenschaftliche Analysen, welche Fahrzeiten für Patientinnen und Patienten bei planbaren Operationen zumutbar sind, gibt es derweil nicht. Eingriffe am Kniegelenk sind in den allermeisten Fällen kein Notfall, wodurch längere Fahrzeiten hinnehmbar erscheinen, wenn dadurch die Behandlungsqualität insgesamt steigt. Einzelne, [kleinere Studien](https://link.springer.com/article/10.1186/s12913-021-06641-8) und [Reviews](https://link.springer.com/article/10.1186/s12889-020-8333-5) zeigen, dass die Mehrheit der Patientinnen und Patienten längere Fahrten akzeptiert, wenn sie dafür in einer erfahreneren Klinik operiert werden.

Die Einführung der neuen Mindestmengen erfolgt nun zwar nach mehrjährigen Beratungen, allerdings bleibt die konkrete Ausgestaltung mit Blick auf die gestartete Klinikreform unklar. So sieht die Reform unter anderem die neuen Leistungsgruppen „Endoprothetik Knie“ und „Revision Knie-Endoprothetik“ vor. Den Leistungsgruppen wiederum liegen Mindestkriterien an Personal und Diagnosemöglichkeiten zugrunde.

Darüber hinaus plant die Bundesregierung, in einem Gesetzesvorhaben zur Stabilisierung der GKV-Finanzen bei möglichen Knieoperationen eine ärztliche Zweitmeinungspflicht einzuführen. Bislang besteht darauf nur ein Anrecht. Künftig könnte es also mindestens drei neue gesetzliche Hebel geben, die auf die Fallzahlen für Knieoperationen wirken: höhere Mindestmengen, Klinikreform und Zweitmeinungspflicht. Wie sie alle miteinander vereint werden, bleibt derzeit ebenfalls unklar.

### Methodik und Limitationen

#### Fahrzeiten

Unsere Berechnungen basieren aktuell noch auf einem älteren Straßennetz samt Verkehrsdaten (2017). Die Daten zur Wohnbevölkerung stammen aus dem Zensus 2022. Gebiete, in denen es seitdem größere Bauprojekte gab, können in der Fahrzeitberechnung deutlich abweichen. Insgesamt ist das deutsche Straßennetz allerdings unverändert. Wir planen, im Laufe des Jahres ein Update der Daten auf ein aktuelles Netz durchzuführen. Bei Fragen oder lokalen Unplausibilitäten kontaktieren Sie uns gerne.

Eine Erreichbarkeitsanalyse ist nur eine Facette einer guten Klinikversorgung. So wird die Kapazität einzelner Krankenhäuser in dieser Berechnung nicht berücksichtigt. Bei einer Klinikschließung kann es notwendig sein, benachbarte Kliniken oder deren Leistungsangebot zu vergrößern. Insbesondere bei der Schließung einzelner Abteilungen in einem Krankenhaus kann es zu Wechselwirkungen mit anderen Abteilungen kommen, die dadurch vielleicht nicht mehr wirtschaftlich arbeiten können. Qualität und Ausstattung der Krankenhäuser werden auch nicht direkt betrachtet, können aber ein Auswahlkriterium der Krankenhäuser für die Szenarienberechnung sein.

Im zugrundeliegenden Datensatz wird für jeden Klinikstandort die Fahrzeit zu jeder bewohnten 100-mal-100-Meter-Gitterzelle in einem großen Radius bereitgehalten. Mit der für eine spezifische Analyse ausgewählten Liste an Kliniken wird für jede bewohnte Gitterzelle in Deutschland oder der ausgewählten Region überprüft, welcher der Klinikstandorte die kürzeste Fahrzeit bietet. Um den Effekt von Ungenauigkeiten aufgrund von Datenfehlern etwas auszugleichen, werden diese Fahrzeiten auf Gemeindeebene aggregiert. Dabei wird der Mittelwert über die Wohnbevölkerung gebildet, stärker besiedelte Gitterzellen fallen also auch stärker ins Gewicht.

Die Daten zu den Fahrzeiten stammen aktuell von Tim Holthaus vom Lehrstuhl für Güterverkehr und Transportlogistik an der Universität Wuppertal. Die Fahrzeit bezieht sich auf eine Fahrt mit dem Pkw an einem Werktag zwischen sieben und elf Uhr. Grundlage für die Routenberechnung ist ein OpenStreetMap-Straßennetz, in dem für alle 100-Meter-Segmente die mittlere Geschwindigkeit aus Navigationsdaten aus dem Jahr 2017 verwendet wurde. Als Startpunkt der Routenberechnung wurde immer der Straßenknoten gewählt, der dem Mittelpunkt der Gitterzelle am nächsten liegt. Es existiert ein [Preprint](https://www.researchgate.net/publication/342211628_Evaluation_of_driven_speed_on_German_motorways_without_speed_limits_-_a_new_approach) mit einer detaillierten Beschreibung der Methodik.

#### Standorte und Fallzahlen

Die Fallzahlen zu den Knieoperationen der einzelnen Klinikstandorte hat das SMC systematisch aus den [Qualitätsberichten der Krankenhäuser](https://qb-referenzdatenbank.g-ba.de/#/suche){target="_blank"} aus dem Jahr 2024 ausgelesen. Dies sind die aktuellen verfügbaren Daten. Die Fallzahlen können heute seither höher oder auch niedriger sein. Eine Klinik, die 2024 knapp unter der aktuellen Mindestmenge lag, könnte heute darüber liegen – oder weiterhin darunter. Um die Fallzahlen aus 2024 zu ermitteln, wurden die relevanten OPS-Codes aus den Beschlüssen des G-BA ([Beschluss für Knie-Tep](https://www.g-ba.de/downloads/39-261-7611/2025-12-18_Mm-R_Aenderung-Nummer-6-Knie-TEP_BAnz.pdf), [Beschluss für Schlittenprothese](https://www.g-ba.de/downloads/39-261-7610/2025-12-18_Mm-R_Ergaenzung-Unikondylaere-Schlittenprothesen_BAnz.pdf), [Beschluss für Revisionseingriffe](https://www.g-ba.de/downloads/39-261-7612/2025-12-18_Mm-R_Ergaenzung-Revisionseingriffe_BAnz.pdf)) ausgelesen.

In den Qualitätsberichten geben die Krankenhausstandorte bei weniger als vier Fällen pro Jahr und pro Organisationseinheit aufgrund des Datenschutzes keine genaue Anzahl an. Wir rechnen dann optimistisch mit Fallzahl drei. In der Realität liegt die echte Fallzahl oft niedriger. Auf der interaktiven Karte werden die Fallzahlen zur Transparenz daher mit einem Minimal- und einem Maximalwert angegeben.

### Datengrundlage und Code

Den Code für diesen Data Report stellen wir [hier]( `r file.path(imagepath, paste0(today()[Datumsprefix_files], "_"[Datumsprefix_files], file_name_code))` ){target="_blank"} zur Verfügung. Neben Standard R-Paketen wird ein eigenes Paket verwendet, das [hier](https://github.com/sciencemediacenter/SMChelpR){target="_blank"} bereitgestellt wird. Die Daten zur Erstellung der interaktiven Karte finden sie [hier](reports/26038/mindestmenge-knie-2026-kartendaten.zip).

### Ihre Ansprechpersonen in Redaktion und SMC Lab

Wenn Sie Fragen zu diesen Daten haben oder weitere Auswertungen erhalten wollen, kann das SMC Lab Auswertungen erzeugen.

*Redaktion*\
Philipp Jacobs, Redakteur für Medizin und Lebenswissenschaften\
[redaktion\@sciencemediacenter.de](mailto:redaktion@sciencemediacenter.de){.email}

*Datenauswertung*\
Simon Essink\
[lab\@sciencemediacenter.de](mailto:lab@sciencemediacenter.de){.email}

Telefon: +49 221 8888 25-0\
E-Mail Inhalt: [redaktion\@sciencemediacenter.de](mailto:redaktion@sciencemediacenter.de){.email}\
E-Mail Datenauswertung: [lab\@sciencemediacenter.de](mailto:lab@sciencemediacenter.de){.email}
:::