Created
May 3, 2021 18:05
-
-
Save IchHabRecht/4c990cfd1ee469579b535f9ebdd906f9 to your computer and use it in GitHub Desktop.
iOS Scriptable Widget für aktuellen 7-Tage-Inzidenzwert + realer 7-Tage-Inzidenzwert der letzten 5 Tage
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
// Licence: Robert Koch-Institut (RKI), dl-de/by-2-0 | |
// Thanks to @kevinkub (https://github.com/kevinkub), @rphl (https://github.com/rphl) and @tzschies (https://github.com/tzschies) for their inspiring work on this widget. | |
// See https://gist.github.com/kevinkub/46caebfebc7e26be63403a7f0587f664, https://gist.github.com/rphl/0491c5f9cb345bf831248732374c4ef5 and https://gist.github.com/tzschies/563fab70b37609bc8f2f630d566bcbc9. | |
class IncidenceWidget { | |
constructor() { | |
this.previousDaysToShow = 31 | |
this.apiUrlDistricts = (location) => `https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_Landkreisdaten/FeatureServer/0/query?where=1%3D1&outFields=RS,GEN,cases7_bl_per_100k,cases7_per_100k,BL,EWZ&geometry=${location.longitude.toFixed(3)},${location.latitude.toFixed(3)}&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&returnGeometry=false&outSR=4326&f=json` | |
this.apiUrlDistrictsHistory = (districtId) => `https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/Covid19_RKI_Sums/FeatureServer/0//query?where=IdLandkreis%3D${districtId}&objectIds=&time=&resultType=none&outFields=AnzahlFall,SummeFall,Meldedatum&returnIdsOnly=false&returnUniqueIdsOnly=false&returnCountOnly=false&returnDistinctValues=false&cacheHint=false&orderByFields=Meldedatum+DESC&groupByFieldsForStatistics=&outStatistics=&having=&resultOffset=&resultRecordCount=12&sqlFormat=none&f=json` | |
this.stateToAbbr = { | |
'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' | |
} | |
this.incidenceColors = [ | |
new Color('#dd0085', 1), // > 500 | |
new Color('#671212', 1), // > 250 | |
new Color('#951214', 1), // > 100 | |
new Color('#d43624', 1), // > 50 | |
new Color('#ffb534', 1), // > 25 | |
new Color('#fff380', 1), // > 5 | |
new Color('#fffccd', 1), // < 6 | |
new Color('#adadad', 1), // NaN | |
]; | |
} | |
async run() { | |
let widget = await this.createWidget() | |
if (!config.runsInWidget) { | |
await widget.presentSmall() | |
} | |
Script.setWidget(widget) | |
Script.complete() | |
} | |
async createWidget(items) { | |
let data = await this.getData() | |
// Basic widget setup | |
let widget = new ListWidget() | |
widget.setPadding(0, 0, 0, 0) | |
let textStack = widget.addStack() | |
textStack.setPadding(14, 10, 14, 10) | |
textStack.layoutVertically() | |
// Header | |
let header = textStack.addText("🦠 7-Tage-Inzidenz".toUpperCase()) | |
header.font = Font.mediumSystemFont(13) | |
textStack.addSpacer() | |
if (data.error) { | |
// Error handling | |
let loadingIndicator = textStack.addText(data.error.toUpperCase()) | |
textStack.setPadding(14, 10, 14, 10) | |
loadingIndicator.font = Font.mediumSystemFont(13) | |
loadingIndicator.textOpacity = 0.5 | |
let spacer = textStack.addStack() | |
spacer.addSpacer() | |
} else { | |
// Enable caching | |
widget.refreshAfterDate = new Date(Date.now() + 60601000) | |
// Main stack for value and area name | |
let incidenceStack = textStack.addStack() | |
incidenceStack.layoutVertically() | |
let valueStack = incidenceStack.addStack() | |
let incidenceValueLabel = valueStack.addText(data.incidence + data.trend) | |
incidenceValueLabel.font = Font.boldSystemFont(24) | |
incidenceValueLabel.textColor = this.getColorByIncidence(data.incidence) | |
// Chip for displaying state data | |
valueStack.addSpacer() | |
let stateStack = valueStack.addStack() | |
stateStack.backgroundColor = this.incidenceColors[7] | |
stateStack.cornerRadius = 4 | |
stateStack.setPadding(2, 4, 2, 4) | |
let stateText = stateStack.addText(data.incidenceBySide + "\n" + data.areaNameBySide) | |
stateText.font = Font.mediumSystemFont(9) | |
stateText.textColor = Color.white() | |
stateText.centerAlignText() | |
stateText.lineLimit = 2 | |
valueStack.addSpacer() | |
incidenceStack.addText(data.areaName) | |
textStack.addSpacer() | |
let historyStack = textStack.addStack() | |
historyStack.spacing = 4 | |
for (var i = 0; i < 5; i++) { | |
var casesSum = data.timeline.features.slice(i + 1, i + 8).reduce((sum, feature) => sum + feature.attributes.AnzahlFall, 0) | |
var incidence = Math.round(casesSum / data.population * 100000) | |
var incidenceDate = new Date(data.timeline.features[i].attributes.Meldedatum) | |
var incidenceDay = incidenceDate.getDate() | |
var incidenceDayOfWeek = incidenceDate.getDay() | |
var incidenceRectStack = historyStack.addStack() | |
incidenceRectStack.backgroundColor = this.getColorByIncidence(incidence) | |
incidenceRectStack.cornerRadius = 4 | |
incidenceRectStack.setPadding(2, 4, 2, 4) | |
var incidenceRectText = incidenceRectStack.addText(('00' + incidence).slice(Math.max(3, incidence.toString().length) * -1) + "\n" + ('0' + incidenceDay).slice(Math.max(2, incidenceDay.toString().length) * -1)) | |
incidenceRectText.font = Font.mediumSystemFont(9) | |
incidenceRectText.textColor = incidenceDayOfWeek == 0 || incidenceDayOfWeek == 6 | |
? Color.lightGray() : incidence > 50 ? Color.white() : Color.black() | |
incidenceRectText.centerAlignText() | |
incidenceRectText.lineLimit = 2 | |
} | |
} | |
return widget | |
} | |
async getData() { | |
try { | |
let location = await this.getLocation() | |
if (location) { | |
let currentData = await new Request(this.apiUrlDistricts(location)).loadJSON() | |
let attr = currentData.features[0].attributes | |
let historicalData = await new Request(this.apiUrlDistrictsHistory(attr.RS)).loadJSON() | |
let casesToday7 = historicalData.features.slice(0, 7).reduce((sum, feature) => sum + feature.attributes.AnzahlFall, 0) | |
let casesYesterday7 = historicalData.features.slice(1, 8).reduce((sum, feature) => sum + feature.attributes.AnzahlFall, 0) | |
let trend = (casesToday7 == casesYesterday7) ? '→' : (casesToday7 > casesYesterday7) ? '↑' : '↓'; | |
return { | |
incidence: attr.cases7_per_100k.toFixed(0), | |
areaName: attr.GEN, | |
trend: trend, | |
incidenceBySide: attr.cases7_bl_per_100k.toFixed(0), | |
areaNameBySide: this.stateToAbbr[attr.BL], | |
population: attr.EWZ, | |
timeline: historicalData | |
} | |
} | |
return {error: "Standort nicht verfügbar."} | |
} catch (e) { | |
return {error: "Fehler bei Datenabruf: " + e.message} | |
} | |
} | |
async getLocation() { | |
try { | |
if (args.widgetParameter) { | |
let fixedCoordinates = args.widgetParameter.split(",").map(parseFloat) | |
return { | |
latitude: fixedCoordinates[0], | |
longitude: fixedCoordinates[1] | |
} | |
} else { | |
Location.setAccuracyToThreeKilometers() | |
return await Location.current() | |
} | |
} catch (e) { | |
return null | |
} | |
} | |
getColorByIncidence(incidence) { | |
return incidence > 500 | |
? this.incidenceColors[0] | |
: incidence > 250 | |
? this.incidenceColors[1] | |
: incidence > 100 | |
? this.incidenceColors[2] | |
: incidence > 50 | |
? this.incidenceColors[3] | |
: incidence > 25 | |
? this.incidenceColors[4] | |
: incidence > 5 | |
? this.incidenceColors[5] | |
: incidence >= 0 ? this.incidenceColors[6] : this.incidenceColors[7] | |
} | |
getDateString(addDays) { | |
addDays = addDays || 0 | |
return new Date(Date.now() + addDays * 24 * 60 * 60 * 1000).toISOString().substring(0, 10) | |
} | |
} | |
await new IncidenceWidget().run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment