-
-
Save tobwil/2ecfc6843839b4d629f1d7d55a831169 to your computer and use it in GitHub Desktop.
COVID-19 Inzidenz-Widget für iOS innerhalb Deutschlands 🇩🇪
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Variables used by Scriptable. | |
// These must be at the very top of the file. Do not edit. | |
// icon-color: deep-gray; icon-glyph: magic; | |
// Licence: Robert Koch-Institut (RKI), dl-de/by-2-0 | |
const apiUrl = (location) => `https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_Landkreisdaten/FeatureServer/0/query?where=1%3D1&outFields=BL,cases,cases7_per_100k,cases7_bl_per_100k,GEN,county,last_update&geometry=${location.longitude.toFixed(3)}%2C${location.latitude.toFixed(3)}&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&returnGeometry=false&outSR=4326&f=json` | |
const ABSOLUTE_CASES_API = 'https://cdn.marcelrebmann.de/covid' | |
const WIDGET_MODE = { | |
INCIDENCE: "INCIDENCE", | |
INFECTIONS: "INFECTIONS" | |
} | |
const INCIDENCE_CRITICAL = 50; | |
const INCIDENCE_WARN = 35; | |
const INZIDENZ_HEADER = `🦠 INZIDENZ`; | |
const INFECTIONS_HEADER = `🦠 INFEKTIONEN`; | |
const BUNDESLAENDER_SHORT = { | |
'Baden-Württemberg': 'BW', | |
'Bayern': 'BY', | |
'Berlin': 'BE', | |
'Brandenburg': 'BB', | |
'Bremen': 'HB', | |
'Hamburg': 'HH', | |
'Hessen': 'HE', | |
'Mecklenburg-Vorpommern': 'MV', | |
'Niedersachsen': 'NI', | |
'Nordrhein-Westfalen': 'NRW', | |
'Rheinland-Pfalz': 'RP', | |
'Saarland': 'SL', | |
'Sachsen': 'SN', | |
'Sachsen-Anhalt': 'ST', | |
'Schleswig-Holstein': 'SH', | |
'Thüringen': 'TH' | |
}; | |
const getIncidenceColor = (incidence) => { | |
if (incidence >= INCIDENCE_CRITICAL) { | |
return Color.red(); | |
} else if (incidence >= INCIDENCE_WARN) { | |
return Color.orange(); | |
} else { | |
return Color.green(); | |
} | |
} | |
const getInfectionTrend = (data) => { | |
if (data.cases_new_today > data.cases_new_yesterday) { | |
return "▲"; | |
} else if (data.cases_new_today === data.cases_new_yesterday) { | |
return "▶︎"; | |
} else if (data.cases_new_today < data.cases_new_yesterday) { | |
return "▼"; | |
} else { | |
return ""; | |
} | |
} | |
const getInfectionTendColor = (infections) => { | |
if (infections > 0) { | |
return Color.red(); | |
} else if (infections < 0) { | |
return Color.green(); | |
} else { | |
return Color.gray(); | |
} | |
} | |
const generateLandkreisName = (data, customLandkreisName) => { | |
if (customLandkreisName) { | |
return customLandkreisName; | |
} | |
return data.isKreisfreieStadt ? `${data.landkreisName} (Stadt)` : data.landkreisName; | |
} | |
async function loadIncidenceData(location) { | |
const data = await new Request(apiUrl(location)).loadJSON(); | |
if (!data || !data.features || !data.features.length) { | |
return null; | |
} | |
const attributes = data.features[0].attributes; | |
return { | |
lastUpdated: attributes.last_update, | |
incidenceLandkreis: attributes.cases7_per_100k.toFixed(1), | |
incidenceBundesland: attributes.cases7_bl_per_100k.toFixed(1), | |
casesLandkreis: attributes.cases, | |
bundeslandName: BUNDESLAENDER_SHORT[attributes.BL], | |
isKreisfreieStadt: !!attributes.county.match(/^SK \w+$/), | |
landkreisName: attributes.GEN | |
}; | |
} | |
async function loadInfectionsData() { | |
const data = await new Request(ABSOLUTE_CASES_API).loadJSON(); | |
if (!data) { | |
return null; | |
} | |
return data; | |
} | |
const createIncidenceWidget = (widget, data, customLandkreisName) => { | |
const header = widget.addText(INZIDENZ_HEADER) | |
header.font = Font.mediumSystemFont(13) | |
widget.addSpacer() | |
const mainContent = widget.addStack() | |
mainContent.layoutHorizontally() | |
mainContent.useDefaultPadding() | |
mainContent.centerAlignContent() | |
const incidenceLabel = mainContent.addText(`${data.incidenceLandkreis}`) | |
incidenceLabel.font = Font.boldSystemFont(24) | |
incidenceLabel.textColor = getIncidenceColor(data.incidenceLandkreis) | |
const casesLandkreisLabel = mainContent.addText(` (${data.casesLandkreis})`) | |
casesLandkreisLabel.font = Font.systemFont(14) | |
casesLandkreisLabel.textColor = Color.gray() | |
const landkreisNameLabel = widget.addText(generateLandkreisName(data, customLandkreisName)) | |
landkreisNameLabel.minimumScaleFactor = 0.7 | |
widget.addSpacer() | |
const footer = widget.addStack() | |
footer.layoutHorizontally() | |
footer.useDefaultPadding() | |
const bl_incidence = footer.addText(`${data.incidenceBundesland}`) | |
bl_incidence.font = Font.boldSystemFont(14) | |
bl_incidence.textColor = getIncidenceColor(data.incidenceBundesland) | |
const bundeslandLabel = footer.addText(` ${data.bundeslandName}`) | |
bundeslandLabel.font = Font.systemFont(14) | |
bundeslandLabel.textColor = Color.gray() | |
const updateLabel = widget.addText(`Daten: ${data.lastUpdated.substr(0, 10)}`) | |
updateLabel.font = Font.systemFont(10) | |
updateLabel.textColor = Color.gray() | |
} | |
const createInfectionsWidget = (widget, infectionsData) => { | |
const infectionsDiff = infectionsData.cases_new_today - infectionsData.cases_new_yesterday; | |
const headerLabel = widget.addText(INFECTIONS_HEADER); | |
headerLabel.font = Font.mediumSystemFont(13); | |
widget.addSpacer() | |
if (!infectionsData) { | |
widget.addText("Keine Fallzahlen verfügbar."); | |
return; | |
} | |
const newInfectionsStack = widget.addStack(); | |
newInfectionsStack.addSpacer(); | |
const label = newInfectionsStack.addText("NEUE GESTERN"); | |
label.font = Font.systemFont(14); | |
label.textColor = Color.gray(); | |
newInfectionsStack.addSpacer(); | |
widget.addSpacer(1); | |
const casesStack = widget.addStack(); | |
casesStack.addSpacer(); | |
const casesLabel = casesStack.addText(`${infectionsData.cases_new_today}`); | |
casesLabel.font = Font.boldSystemFont(24); | |
casesStack.addSpacer(); | |
const casesDifferenceStack = widget.addStack(); | |
casesDifferenceStack.addSpacer(); | |
const casesTrendIcon = casesDifferenceStack.addText(getInfectionTrend(infectionsData)); | |
casesTrendIcon.font = Font.systemFont(14); | |
casesTrendIcon.textColor = getInfectionTendColor(infectionsDiff); | |
const casesDiffLabel = casesDifferenceStack.addText(` (${infectionsDiff >= 0 ? '+' : ''}${infectionsDiff})`); | |
casesDiffLabel.font = Font.systemFont(14); | |
casesDiffLabel.textColor = Color.gray(); | |
casesDifferenceStack.addSpacer(); | |
widget.addSpacer() | |
const footer = widget.addStack(); | |
footer.addSpacer(); | |
const date = new Date(infectionsData.rki_updated); | |
const day = date.getDate(); | |
const month = date.getMonth() + 1; | |
const year = date.getFullYear(); | |
const updateLabel = footer.addText(`Daten: ${day < 10 ? '0' : ''}${day}.${month < 10 ? '0' : ''}${month}.${year}`); | |
updateLabel.font = Font.systemFont(10); | |
updateLabel.textColor = Color.gray(); | |
footer.addSpacer(); | |
} | |
let widget = await createWidget() | |
if (!config.runsInWidget) { | |
await widget.presentSmall() | |
} | |
Script.setWidget(widget) | |
Script.complete() | |
async function createWidget() { | |
let location = {}; | |
let customLandkreisName; | |
let widgetMode = WIDGET_MODE.INCIDENCE; | |
const params = args.widgetParameter ? args.widgetParameter.split(",") : undefined; | |
if (!params) { | |
Location.setAccuracyToThreeKilometers() | |
location = await Location.current() | |
} | |
if (params && params[0] === "INF") { | |
widgetMode = WIDGET_MODE.INFECTIONS; | |
} | |
if (params && params[0] !== "INF") { | |
location = { | |
latitude: parseFloat(params[0]), | |
longitude: parseFloat(params[1]) | |
} | |
customLandkreisName = params[2]; | |
} | |
const widget = new ListWidget(); | |
switch (widgetMode) { | |
case WIDGET_MODE.INCIDENCE: | |
const incidenceData = await loadIncidenceData(location) | |
if (!incidenceData) { | |
widget.addText("Keine Ergebnisse für den aktuellen Ort gefunden.") | |
return widget; | |
} | |
createIncidenceWidget(widget, incidenceData, customLandkreisName); | |
break; | |
case WIDGET_MODE.INFECTIONS: | |
const infectionsData = await loadInfectionsData(); | |
if (!infectionsData) { | |
widget.addText("Fallzahlen konnten nicht geladen werden.") | |
return widget; | |
} | |
createInfectionsWidget(widget, infectionsData); | |
break; | |
default: | |
widget.addText("Keine Daten.") | |
return widget; | |
} | |
return widget; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment