const internalData = { lastUpdate: 0, stations: [], }; let geolocationPermission = false; let geolocation = [null, null]; Object.defineProperty(String.prototype, 'capitalize', { value: function () { return this.charAt(0).toUpperCase() + this.toLowerCase().slice(1); }, enumerable: false }); async function loadElevators() { const res = await fetch('https://www.hvv.de/elevators', {referrer: ""}); const data = await res.json(); const stations = data['stations']; stations.sort(sortStations) internalData.lastUpdate = new Date(data['lastUpdate']); let stationIndex = 0; for (const station of stations) { const stationName = station['mainSubStation']['stationName']; const lines = new Set(); const elevators = []; for (const elevatorKey of Object.keys(station.elevators)) { const elevatorApi = station.elevators[elevatorKey]; const elevatorLines = []; for (let line of elevatorApi.lines) { line = line.replace(/[()]/g, ""); lines.add(line); elevatorLines.push({ line: line, type: getType(line), }); } // try to detect levels from description let levels = []; if (elevatorApi.description.search('<->') >= 0) { levels = elevatorApi.description.split('<->').map(level => level.trim()); } else if (elevatorApi.description.search('<>') >= 0) { levels = elevatorApi.description.split('<>').map(level => level.trim()); } else if (elevatorApi.description.search('/ ') >= 0) { levels = elevatorApi.description.split('/ ').map(level => level.trim()); } const elevator = { working: elevatorApi['state'] === 1, stateUnavailable: elevatorApi['state'] === -1, dimensions: { width: elevatorApi['cabinWidth'], length: elevatorApi['cabinLength'], door: elevatorApi['doorWidth'], }, description: elevatorApi['description'], label: elevatorApi['label'], type: elevatorApi['type'].replace('_', ' ').capitalize(), braille: elevatorApi['tasterType'] === 'UNBEKANNT' ? -1 : elevatorApi['tasterType'] === 'KOMBI' || elevatorApi['tasterType'] === 'BRAILLE', speaker: elevatorApi['tasterType'] === 'UNBEKANNT' ? -1 : elevatorApi['tasterType'] === 'KOMBI', lines: elevatorLines, levels: levels, instCause: elevatorApi['instCause'], osmNodeId: elevatorApi['osmId'], } elevators.push(elevator); } let stationState = { unavailable: 0, working: 0, outOfOrder: 0, } for (const elevator of elevators) { if (elevator.stateUnavailable) { stationState.unavailable++; } else if (elevator.working) { stationState.working++; } else { stationState.outOfOrder++; } } const stationLines = Array.from(lines); const stationTypes = Array.from(getTypes(stationLines)); internalData.stations[stationIndex++] = { name: stationName, state: stationState, lines: stationLines, types: stationTypes, elevators: elevators, } } localStorage.setItem("internal_data", JSON.stringify(internalData)); } const dateTimeStyle = new Intl.DateTimeFormat('de-DE', { dateStyle: 'medium', timeStyle: 'medium', timeZone: 'Europe/Berlin', }) async function loadOsmData() { const nodeIdList = []; for (const station of internalData.stations) { for (const elevator of station.elevators) { nodeIdList.push(elevator.osmNodeId) } } const osmData = await fetch(`https://overpass-api.de/api/interpreter?data=[out:json];node(id:${nodeIdList.join(',')});out%20body;`, {}); const osmJson = await osmData.json(); if (!osmJson.hasOwnProperty('elements')) return; localStorage.setItem("osm_data", JSON.stringify(osmJson)); } function distance([lat1, lon1], [lat2, lon2]) { if (!lat1 || !lat2 || !lon1 || !lon2) return null const R = 6371e3; // metres const phi1 = lat1 * Math.PI / 180; // φ, λ in radians const phi2 = lat2 * Math.PI / 180; const dPhi = (lat2 - lat1) * Math.PI / 180; const dLambda = (lon2 - lon1) * Math.PI / 180; const a = Math.sin(dPhi / 2) * Math.sin(dPhi / 2) + Math.cos(phi1) * Math.cos(phi2) * Math.sin(dLambda / 2) * Math.sin(dLambda / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; // in metres } function registerGeolocationWatcher() { navigator.geolocation.watchPosition((pos) => { geolocation = [pos.coords.latitude, pos.coords.longitude] }, (e) => { console.log(e) }, { enableHighAccuracy: true, timeout: 5000, maximumAge: 0, }) } function checkGeolocationPermission() { navigator.permissions.query({name: "geolocation"}).then((result) => { geolocationPermission = result.state if (result.state === 'granted') { registerGeolocationWatcher() } }); } function allowGeolocation() { navigator.geolocation.getCurrentPosition(() => { console.log('success') }, () => { console.log('error') }); checkGeolocationPermission() registerGeolocationWatcher() } checkGeolocationPermission() document.querySelector('#stationsNearMe').addEventListener('click', allowGeolocation) function sortStations(stationA, stationB) { const nameA = stationA.mainSubStation.stationName.toUpperCase(); // ignore upper and lowercase const nameB = stationB.mainSubStation.stationName.toUpperCase(); // ignore upper and lowercase if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } // names must be equal return 0; } function getType(line) { const type = line.replace(/[^A-z]/g, ""); switch (type) { case 'RE': case 'RB': case 'R': case 'DB': return 'R'; case 'S': return 'S'; case 'U': return 'U'; case 'A': return 'A'; } } function getTypes(lines) { const types = new Set(); for (const line of lines) { types.add(getType(line)) } return types; } function renderData() { const ls = JSON.parse(localStorage.getItem("internal_data")); if (!ls) return; internalData.lastUpdate = ls['lastUpdate']; internalData.stations = ls['stations']; const osmData = JSON.parse(localStorage.getItem("osm_data")); if (!internalData || !internalData.stations.length) { console.error('No Data available!') return; } document.querySelector('#loadElevators').innerHTML = 'Daten aktualisieren'; const dateContainer = document.querySelector('#lastUpdated'); dateContainer.innerHTML = dateTimeStyle.format(new Date(internalData.lastUpdate)); const listContainer = document.querySelector('#stationList'); //clear list before update listContainer.innerHTML = ''; const stations = internalData['stations']; for (const stationIndex in stations) { const station = stations[stationIndex]; let elevatorsTemplate = ''; let previewTemplate = ''; for (const elevator of station.elevators) { let linesTemplate = '