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');
for (const elevator of elevatorNodes) {
elevator.querySelector('.osm').insertAdjacentHTML("beforeend", '
loading...
')
}
for (const elevator of elevatorNodes) {
const osmContainer = elevator.querySelector('.osmTags');
const nodeId = elevator.querySelector('.osm').dataset.nodeid;
const osmData = await fetch(`https://overpass-api.de/api/interpreter?data=[out:json];node(${nodeId});out%20body;`, {});
const responseData = await osmData.json();
if (responseData.elements.length) {
const node = responseData.elements[0];
const tags = node['tags'];
console.log(tags)
if (tags['highway'] === 'elevator') {
let osmTemplate = '';
osmTemplate += ``;
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'}`;
}
osmContainer.innerHTML = ''; // clear loading state
osmContainer.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;
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 += `- ${levelDescription}
`;
}
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}
${previewTemplate}
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());
})
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)