Skip to content

Instantly share code, notes, and snippets.

@tobwil
Forked from marcelrebmann/incidence.js
Created October 22, 2020 10:41
Show Gist options
  • Save tobwil/2ecfc6843839b4d629f1d7d55a831169 to your computer and use it in GitHub Desktop.
Save tobwil/2ecfc6843839b4d629f1d7d55a831169 to your computer and use it in GitHub Desktop.
COVID-19 Inzidenz-Widget für iOS innerhalb Deutschlands 🇩🇪
// 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