Skip to content

Instantly share code, notes, and snippets.

@Tekelua
Forked from rphl/incidence.js
Last active October 24, 2020 06:39
Show Gist options
  • Save Tekelua/630f9caf55091b0ce3ae1d7fb7e539b7 to your computer and use it in GitHub Desktop.
Save Tekelua/630f9caf55091b0ce3ae1d7fb7e539b7 to your computer and use it in GitHub Desktop.
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
Copy link
Author

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