Skip to content

Instantly share code, notes, and snippets.

@Tekelua
Forked from rphl/incidence.js
Last active Oct 24, 2020
Embed
What would you like to do?
COVID-19 Inzidenz-Widget für iOS innerhalb Deutschlands 🇩🇪 (Kreis/Stadt + Bundesland + Trend)
// Licence: Robert Koch-Institut (RKI), dl-de/by-2-0
const outputFields = 'GEN,cases_per_100k,cases7_per_100k,cases7_bl_per_100k,last_update,BL';
const apiUrl = (location) => `https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_Landkreisdaten/FeatureServer/0/query?where=1%3D1&outFields=${outputFields}&geometry=${location.longitude.toFixed(3)}%2C${location.latitude.toFixed(3)}&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&returnGeometry=false&outSR=4326&f=json`
const apiUrlStates = 'https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/Coronaf%E4lle_in_den_Bundesl%E4ndern/FeatureServer/0/query?where=1%3D1&outFields=LAN_ew_GEN,cases7_bl_per_100k&returnGeometry=false&outSR=4326&f=json'
const apiUrlNewCases = 'https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_COVID19/FeatureServer/0/query?f=json&where=NeuerFall%20IN(1%2C%20-1)&returnGeometry=false&spatialRel=esriSpatialRelIntersects&outFields=*&outStatistics=%5B%7B%22statisticType%22%3A%22sum%22%2C%22onStatisticField%22%3A%22AnzahlFall%22%2C%22outStatisticFieldName%22%3A%22value%22%7D%5D&resultType=standard&cacheHint=true'
const BUNDESLAENDER_SHORT = {
'Baden-Württemberg': 'BadenW',
'Bayern': 'Bayern',
'Berlin': 'Berlin',
'Brandenburg': 'BrandB',
'Bremen': 'Bremen',
'Hamburg': 'Hamburg',
'Hessen': 'Hessen',
'Mecklenburg-Vorpommern': 'MeckV',
'Niedersachsen': 'NiederS',
'Nordrhein-Westfalen': 'NordRW',
'Rheinland-Pfalz': 'RheinLP',
'Saarland': 'SaarL',
'Sachsen': 'Sachsen',
'Sachsen-Anhalt': 'SachsA',
'Schleswig-Holstein': 'SchlesH',
'Thüringen': 'ThürinG'
};
const widget = await createWidget()
if (!config.runsInWidget) {
await widget.presentSmall()
}
Script.setWidget(widget)
Script.complete()
async function createWidget(items) {
const data = await getData()
const list = new ListWidget()
const header = list.addText("🦠 Inzidenz")
header.font = Font.mediumSystemFont(13)
if (data) {
const weekData = saveLoadData(data, data.areaName)
if (!data.shouldCache) {
list.addSpacer(6)
const loadingIndicator = list.addText("Ort wird ermittelt...")
loadingIndicator.font = Font.mediumSystemFont(13)
loadingIndicator.textOpacity = 0.5
}
list.addSpacer()
// INCIDENCE
const incidenceLabel = list.addStack()
incidenceLabel.layoutHorizontally()
incidenceLabel.centerAlignContent()
incidenceLabel.useDefaultPadding()
// 1
data.incidence = (data.incidence >= 100) ? parseInt(data.incidence) : data.incidence
const label = incidenceLabel.addText(data.incidence + '' + getIncidenceTrend(data, weekData))
label.font = Font.boldSystemFont(25)
label.textColor = data.incidence >= 100 ? new Color("8B0000") : data.incidence >= 50 ? new Color("FF0000") : data.incidence >= 35 ? new Color("FFD700") : new Color("32CD32")
// 2
const labelBL = incidenceLabel.addText(data.incidenceBL + getIncidenceBLTrend(data, weekData) + '\n' + data.nameBL)
labelBL.font = Font.mediumSystemFont(9)
labelBL.textColor = Color.gray()
// CITYNAME
const areaLabel = list.addText(data.areaName)
areaLabel.font = Font.mediumSystemFont(14)
list.addSpacer()
// UPDATED
const formatedCases = new Number(data.cases).toLocaleString('de-DE')
const labelUpdated = list.addText(`${data.updated.substr(0, 10)} (+${formatedCases})`)
labelUpdated.font = Font.systemFont(10)
labelUpdated.textColor = Color.gray()
if (data.shouldCache) {
list.refreshAfterDate = new Date(Date.now() + 60 * 60 * 1000)
}
} else {
list.addSpacer()
const errorLabel = list.addText("Daten nicht verfügbar. \nWidget öffnen für Reload...")
errorLabel.font = Font.mediumSystemFont(12)
errorLabel.textColor = Color.gray()
}
return list
}
async function getData() {
try {
let dataCases = await new Request(apiUrlNewCases).loadJSON()
const cases = dataCases.features[0].attributes.value
let dataStates = await new Request(apiUrlStates).loadJSON()
const incidencePerState = dataStates.features.map((f) => { return { BL: BUNDESLAENDER_SHORT[f.attributes.LAN_ew_GEN], incidence: f.attributes.cases7_bl_per_100k } })
const averageIncidence = incidencePerState.reduce((a, b) => a + b.incidence, 0) / incidencePerState.length
const location = await getLocation()
let data = await new Request(apiUrl(location)).loadJSON()
const attr = data.features[0].attributes
const res = {
incidence: parseFloat(attr.cases7_per_100k.toFixed(1)),
incidenceBL: parseFloat(attr.cases7_bl_per_100k.toFixed(1)),
areaName: attr.GEN,
nameBL: BUNDESLAENDER_SHORT[attr.BL],
shouldCache: true,
updated: attr.last_update,
incidencePerState: incidencePerState,
averageIncidence: parseFloat(averageIncidence.toFixed(1)),
cases: cases
}
return res
} catch (e) {
return null
}
}
async function getLocation() {
try {
if (args.widgetParameter) {
const fixedCoordinates = args.widgetParameter.split(",").map(parseFloat)
return {
latitude: fixedCoordinates[0],
longitude: fixedCoordinates[1]
}
} else {
Location.setAccuracyToThreeKilometers()
return await Location.current()
}
} catch (e) {
return null;
}
}
function getIncidenceTrend(data, weekdata) {
let incidenceTrend = ' ';
if (Object.keys(weekdata).length > 0) {
const prevData = getDataForDate(weekdata);
if (prevData) {
incidenceTrend = (data.incidence < prevData.incidence) ? '↓' : '↑'
}
}
return incidenceTrend
}
function getIncidenceBLTrend(data, weekdata) {
let incidenceBLTrend = ' ';
if (Object.keys(weekdata).length > 0) {
const prevData = getDataForDate(weekdata);
if (prevData) {
incidenceBLTrend = (data.incidenceBL < prevData.incidenceBL) ? '↓' : '↑'
}
}
return incidenceBLTrend
}
function getDataForDate(weekdata, yesterday = true, datestr = '') {
let dateKey;
let dayOffset = 1
const today = new Date();
const todayDateKey = `${today.getDate()}.${today.getMonth() + 1}.${today.getFullYear()}`
if (typeof weekdata[todayDateKey] === 'undefined') {
dayOffset = 2
}
if (yesterday) {
today.setDate(today.getDate() - dayOffset);
dateKey = `${today.getDate()}.${today.getMonth() + 1}.${today.getFullYear()}`
} else {
dateKey = datestr;
}
if (typeof weekdata[dateKey] !== 'undefined') {
return weekdata[dateKey]
}
return false
}
// LIMIT TO 7 DAYS
function saveLoadData (newData, suffix = '') {
const updated = newData.updated.substr(0, 10);
const loadedData = loadData(suffix)
if (loadedData) {
loadedData[updated] = newData
const loadedDataKeys = Object.keys(loadedData);
const lastDaysKeys = loadedDataKeys.slice(Math.max(Object.keys(loadedData).length - 7, 0))
let loadedDataLimited = {}
lastDaysKeys.forEach(key => {
loadedDataLimited[key] = loadedData[key]
})
try {
let fm = FileManager.iCloud()
let path = fm.joinPath(fm.documentsDirectory(), 'covid19' + suffix + '.json')
fm.writeString(path, JSON.stringify(loadedDataLimited))
console.log('iCloud: save')
} catch (e) {
let fm = FileManager.local()
let path = fm.joinPath(fm.documentsDirectory(), 'covid19' + suffix + '.json')
fm.writeString(path, JSON.stringify(loadedDataLimited))
console.log('Local: save')
}
return loadedData
}
return {}
}
function loadData(suffix) {
try {
let fm = FileManager.iCloud()
let path = fm.joinPath(fm.documentsDirectory(), 'covid19' + suffix + '.json')
if (fm.fileExists(path)) {
let data = fm.readString(path)
console.log('iCloud: read')
return JSON.parse(data)
}
} catch (e) {
let fm = FileManager.local()
let path = fm.joinPath(fm.documentsDirectory(), 'covid19' + suffix + '.json')
if (fm.fileExists(path)) {
let data = fm.readString(path)
console.log('Local: read')
return JSON.parse(data)
}
}
return {};
}
@Tekelua

This comment has been minimized.

Copy link
Owner Author

@Tekelua Tekelua commented Oct 22, 2020

Rein kosmetische Änderungen zum wunderbaren Fork von @rphl

  • Nicht-Kapitalisierung von Titel und Landkreisen
  • Längere Kürzel der Bundesländer, teils Verwendung des ganzen Namens
  • Leichte Verkleinerung der Schriftgröße der Inzidenzen und Namen um auch Hamburg ganz ausschreiben zu können
  • Zusätzliche dunkelrote Farbe bei 7-Tage-Werten von mehr als 100/100.000 im Landkreis
  • Änderung der drei anderen Anzeigefarben zu LimeGreen, Gold und Rot via Hexcodes
    (- Entfernen der Leerzeichen in der gesamtdeutschen Infektionsentwicklung)
@Tekelua

This comment has been minimized.

Copy link
Owner Author

@Tekelua Tekelua commented Oct 22, 2020

Dark
Light

@TheSpirit

This comment has been minimized.

Copy link

@TheSpirit TheSpirit commented Oct 22, 2020

ich hätte die werte gerne untereinander. was muss ich da im code ändern?
Danke

@Tekelua

This comment has been minimized.

Copy link
Owner Author

@Tekelua Tekelua commented Oct 22, 2020

ich hätte die werte gerne untereinander. was muss ich da im code ändern?
Danke

Du müsstest in Zeile 55 das layoutHorizontally() zu layoutVertically() ändern. Dann wahrscheinlich noch die Reihenfolge von Landkreis und Bundesland vertauschen, dass die Werte zusammen stehen.

Schau dir sonst auch mal die Forks von @marcelrebmann oder @MK-2001 an, da steht es untereinander:
https://gist.github.com/marcelrebmann/64494e453deaf26cab3c14c9ff711e1e
https://gist.github.com/MK-2001/356bbcf8734e2a2aee44bac1b29c2fdb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment