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(); localStorage.setItem("elevator_data", JSON.stringify(data)); } const dateTimeStyle = new Intl.DateTimeFormat('de-DE', { dateStyle: 'medium', timeStyle: 'medium', timeZone: 'Europe/Berlin', }) const internalData = [] let geolocationPermission = false; let geolocation = [null, null]; async function loadOsmData() { const elevatorNodes = document.querySelectorAll('.elevator'); const nodeIdList = []; for (const elevator of elevatorNodes) { const osmContainer = elevator.querySelector('.osm'); const nodeId = osmContainer.dataset.nodeid; if (nodeId > 0) { elevator.querySelector('.osm').insertAdjacentHTML("beforeend", '
loading...
'); nodeIdList.push(nodeId) } } 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; for (const elevator of elevatorNodes) { const tagContainer = elevator.querySelector('.osmTags'); const nodeId = elevator.querySelector('.osm').dataset.nodeid; const nodes = osmJson.elements.filter(node => node.id === parseInt(nodeId)) if (!nodes.length) { tagContainer.innerHTML = 'keine Daten'; continue; } const nodeInfo = nodes[0]; if (!nodeInfo.hasOwnProperty('tags')) { tagContainer.innerHTML = 'keine Daten'; continue; } const tags = nodeInfo['tags']; if (tags['highway'] === 'elevator') { let osmTemplate = ''; osmTemplate += `
Link zur Karte
Auf Karte anzeigen
`; if (tags.hasOwnProperty('description')) { osmTemplate += `
Beschreibung
${tags['description']}
`; } if (tags.hasOwnProperty('level')) { osmTemplate += `
Ebenen
${tags['level'].split(';').sort().join(', ')}
`; } if (tags.hasOwnProperty('wheelchair')) { osmTemplate += `
Rollstühle
${tags['wheelchair'] === 'yes' ? 'Ja' : 'Nein'}
`; } if (tags.hasOwnProperty('bicycle')) { osmTemplate += `
Fahrräder
${tags['bicycle'] === 'yes' ? 'Ja' : 'Nein'}
`; } tagContainer.innerHTML = ''; // clear loading state tagContainer.insertAdjacentHTML('beforeend', osmTemplate); } } } 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 data = JSON.parse(localStorage.getItem("elevator_data")); if (!data) { console.error('No Data available!') return; } let stations = data.stations; stations.sort(sortStations) const date = new Date(data.lastUpdate); document.querySelector('#loadElevators').innerHTML = 'Daten aktualisieren'; const listContainer = document.querySelector('#stationList'); const dateContainer = document.querySelector('#lastUpdated'); dateContainer.innerHTML = dateTimeStyle.format(date); let stationIndex = 0; //clear list before update listContainer.innerHTML = ''; 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); } const stationTypes = getTypes(Array.from(lines)); let elevatorsTemplate = ''; let previewTemplate = ''; let stationState = { unavailable: 0, working: 0, outOfOrder: 0, } for (const elevator of elevators) { let linesTemplate = '
Linien: '; for (const line of elevator.lines) { linesTemplate += `${line.line}`; } linesTemplate += '
'; let levelsTemplate = '
    '; for (const levelDescription of elevator.levels) { levelsTemplate += `
  1. ${levelDescription}
  2. `; } levelsTemplate += '
'; previewTemplate += `` if (elevator.stateUnavailable) { stationState.unavailable++; } else if (elevator.working) { stationState.working++; } else { stationState.outOfOrder++; } elevatorsTemplate += `
  • ${elevator.instCause !== '' ? `
    ${elevator.instCause}
    ` : ''} ${elevator.levels.length ? levelsTemplate : elevator.description}
    Durchgang
    ${elevator.dimensions.door} cm
    Breite
    ${elevator.dimensions.width} cm
    Länge
    ${elevator.dimensions.length} cm
    Braille
    ${elevator.braille === -1 ? 'unbekannt' : elevator.braille ? `verfügbar` : 'nicht verfügbar'}
    Ansage
    ${elevator.speaker === -1 ? 'unbekannt' : elevator.speaker ? `verfügbar` : 'nicht verfügbar'}
    ${elevator.lines.length ? `${linesTemplate}` : ''}

    Daten von OpenStreetMap

  • `; } const template = `
  • ${Array.from(stationTypes).sort().map(t => `${t}`).join('')}

    ${stationName}

    Aufzüge anzeigen
  • `; listContainer.insertAdjacentHTML('beforeend', template); // immediate invocation // (function () { // const stationId = stationIndex; // listContainer.querySelector(`#station_${stationIndex}`) // .addEventListener('click', () => toggleElevatorList(stationId)) // }()); internalData[stationIndex++] = { name: stationName, state: stationState, elevators: elevators, } } } document.querySelector('#loadElevators') .addEventListener('click', (e) => { loadElevators().then(() => renderData()); }) document.querySelector('#loadOSM') .addEventListener('click', (e) => { loadOsmData(); }) renderData(); function filterData(searchString) { for (const stationIndex in internalData) { const matches = internalData[stationIndex].name.toLowerCase().search(searchString.toLowerCase()) >= 0; document.querySelector(`#station_${stationIndex}`).classList.toggle('hidden', !matches); } } document.querySelector('#searchStation').addEventListener('input', (e) => { filterData(e.target.value); }) filterData(document.querySelector('#searchStation').value)