10cd964ede
Wer auf dem Kartenreiter die Einstellungen oeffnete und eine Stadt eingab,
bekam danach nur noch ein graues Fenster. Drei Fehler wirkten zusammen:
1. getCityCoords() rief Nominatim ohne eigenen User-Agent auf. Nominatim
beantwortet den axios-Default mit 403, und zwar fuer jede Stadt. Die
Schwesterfunktion getAddress() setzt seit jeher einen User-Agent -
deshalb funktionierten die Adressen an den Sensoren, nur die
Stadtsuche nicht. Beide nutzen jetzt dieselbe Konstante.
2. cityCoords() lieferte im Fehlerfall den String "no coordinates". Der
landete als Kartenzentrum in L.map().setView(), Leaflet warf "Invalid
LatLng object" - und weil showMap() die alte Karte vorher schon
entfernt hat, blieb der Container leer. Daher grau statt Fehlermeldung.
cityCoords() liefert jetzt {coords, err}; schlaegt die Suche fehl,
bleibt das bisherige Zentrum stehen und der Text wird angezeigt.
3. Der Proxy in noise/routes/api.js kodierte die bereits prozentkodierte
req.originalUrl ein zweites Mal. 'Goettingen' kam als 'G%c3%b6ttingen'
an. Das fiel bisher nicht auf, weil schon Punkt 1 alles abfing.
Ausserdem: Tippfehler getCityCoord.name, fehlendes encodeURI bei der
Stadtsuche, Koordinaten als Zahl statt String, und eine eigene Meldung
(ENOCITY), wenn Nominatim den Ort nicht kennt.
Geprueft ueber die volle Kette noise -> sensorapi -> Nominatim:
Stuttgart, Goettingen (Umlaut), Bad Cannstatt (Leerzeichen) liefern
Koordinaten, ein erfundener Ort eine saubere Meldung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
315 lines
12 KiB
JavaScript
315 lines
12 KiB
JavaScript
// Client side javascript
|
|
"use strict"
|
|
|
|
import * as map from './map.js'
|
|
import * as dt from './datetime.js'
|
|
import {loadAll, showChart, setNoUTC, tabtable} from './showcharts.js'
|
|
import { DateTime } from './luxon.min.js'
|
|
import {ttIndex, setCurrentTab, getCurrentTab, showError, fetchfromserver, cityCoords} from "./chart_utilities.js";
|
|
import * as spin from './spinner.js'
|
|
|
|
|
|
(async () => {
|
|
|
|
// global variables
|
|
const params = { // set defaults
|
|
center: {name: 'Stuttgart', coords: [48.778, 9.180]},
|
|
zoom: 10,
|
|
span: 1,
|
|
peak: 70,
|
|
weeks: 4,
|
|
datetime: '',
|
|
seldate: '',
|
|
refreshRate: 5,
|
|
sid: -1
|
|
}
|
|
|
|
let minDate = new Date(2023, 0, 1)
|
|
|
|
const setting = [
|
|
{
|
|
typ: 'maptab',
|
|
show: ['ccity', 'odth'],
|
|
val: ['centercity', 'olderthan'],
|
|
param: [["center","name"], 'weeks']
|
|
},
|
|
{
|
|
typ: 'livetab',
|
|
show: ['stday', 'nbday'],
|
|
val: ['startday', 'nbrofdays'],
|
|
param: ['seldate', 'span']
|
|
},{
|
|
typ: 'houravgtab',
|
|
show: ['stday','pklim'],
|
|
val: ['startday', 'peaklim'],
|
|
param: ['seldate', 'peak']
|
|
},{
|
|
typ: 'dayavgtab',
|
|
show: ['stday','pklim'],
|
|
val: ['startday', 'peaklim'],
|
|
param: ['seldate', 'peak']
|
|
},{
|
|
typ: 'daynighttab',
|
|
show: ['stday'],
|
|
val: ['startday'],
|
|
param: ['seldate']
|
|
},{
|
|
typ: 'ldentab',
|
|
show: ['stday'],
|
|
val: ['startday'],
|
|
param: ['seldate']
|
|
}
|
|
]
|
|
// END global variables
|
|
|
|
// localStorage.clear()
|
|
|
|
|
|
// Check new map center
|
|
const checkNewCenter = (oldC, newC) => {
|
|
if((oldC[0] !== newC[0]) || (oldC[1] !== newC[1])) {
|
|
map.setNewCenter(newC)
|
|
}
|
|
}
|
|
|
|
// Register events
|
|
// Button 'My Location' pressed
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const btnMyLocation = document.querySelector('#btnMyLocation');
|
|
if (btnMyLocation) {
|
|
btnMyLocation.addEventListener('click', () => {
|
|
map.goToMyLocation();
|
|
});
|
|
}
|
|
});
|
|
|
|
// Button 'Save' pressed
|
|
document.querySelector('#btnSave').addEventListener('click', async () => {
|
|
let curtab = getCurrentTab()
|
|
let cityErr = null // Meldung erst nach dem Schliessen des Dialogs zeigen
|
|
for(let i = 0; i < setting.length; i++) {
|
|
if(setting[i].typ === getCurrentTab()) {
|
|
for(let j = 0; j < setting[i].show.length; j++) {
|
|
let elem = document.getElementById(setting[i].val[j])
|
|
let val = elem.value
|
|
if(elem.type === 'checkbox') {
|
|
val = elem.checked
|
|
}
|
|
let x = setting[i].param[j]
|
|
console.log('x: ', x)
|
|
if ( Array.isArray(x)) {
|
|
params[setting[i].param[j][0]][setting[i].param[j][1]] = val
|
|
} else {
|
|
params[setting[i].param[j]] = val
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if (curtab !== 'maptab') {
|
|
let starttime
|
|
if ((params.seldate === 'today') || (params.seldate === 'heute') || (DateTime.now().toFormat('yyyy-MM-dd') === params.seldate)) {
|
|
starttime = ''
|
|
} else {
|
|
starttime = DateTime.fromFormat(params.seldate, 'yyyy-MM-dd').toUTC().toISO()
|
|
}
|
|
params.datetime = starttime
|
|
} else {
|
|
const oldCoords = params.center.coords
|
|
const erg = await cityCoords(params.center.name)
|
|
if (erg.coords === null) {
|
|
// Ort nicht aufloesbar: altes Zentrum behalten. Ohne das landet
|
|
// ein ungueltiger Wert in setView() und die Karte bleibt grau.
|
|
cityErr = erg.err
|
|
params.center = JSON.parse(localStorage.getItem('centercity'))
|
|
} else {
|
|
params.center.coords = erg.coords
|
|
checkNewCenter(oldCoords, params.center.coords)
|
|
localStorage.setItem('centercity',JSON.stringify(params.center))
|
|
}
|
|
}
|
|
let newlng = document.querySelector('#sellan input:checked').id
|
|
let oldlng = localStorage.getItem('curlang')
|
|
localStorage.setItem('curlang', newlng)
|
|
if(newlng !== oldlng) {
|
|
location.reload()
|
|
}
|
|
document.getElementById('dialogSettings').close()
|
|
spin.spinner.spin(spin.spindiv)
|
|
await loadAll(params,0, curtab)
|
|
spin.spinner.stop()
|
|
if (cityErr) {
|
|
showError(cityErr)
|
|
}
|
|
// ToDo:
|
|
// Load ALL incl. MAP und LIVE hier, d.h. an loadAll einen zusätzlichen Parameter übergeben
|
|
// if(params.sid !== undefined) {
|
|
// await loadAll(params)
|
|
// }
|
|
})
|
|
|
|
// Btn 'Settings' pressed
|
|
document.querySelector('#btnSet').addEventListener('click', () => {
|
|
const curtab = getCurrentTab()
|
|
const dialog = document.getElementById('dialogSettings')
|
|
|
|
// Set max and min dates for date input
|
|
const today = new Date().toISOString().split('T')[0]
|
|
const minDateStr = minDate.toISOString().split('T')[0]
|
|
const startdayInput = document.getElementById('startday')
|
|
startdayInput.max = today
|
|
startdayInput.min = minDateStr
|
|
|
|
// Convert seldate format if needed
|
|
let seldate = params.seldate
|
|
if (seldate === 'today' || seldate === 'heute') {
|
|
seldate = today
|
|
}
|
|
|
|
startdayInput.value = seldate
|
|
document.getElementById('nbrofdays').value = params.span
|
|
document.getElementById('peaklim').value = params.peak
|
|
document.getElementById('olderthan').value = params.weeks
|
|
const centercity = JSON.parse(localStorage.getItem('centercity'))
|
|
document.getElementById('centercity').value = centercity.name
|
|
|
|
// select different infos depending on the selected tab
|
|
// first clear all
|
|
document.querySelector('#ccity').style.display = 'none'
|
|
document.querySelector('#stday').style.display = 'none'
|
|
document.querySelector('#nbday').style.display = 'none'
|
|
document.querySelector('#pklim').style.display = 'none'
|
|
document.querySelector('#odth').style.display = 'none'
|
|
for(let i = 0; i < setting.length; i++) {
|
|
if(setting[i].typ === curtab) {
|
|
for(let j = 0; j < setting[i].show.length; j++) {
|
|
document.querySelector(`#${setting[i].show[j]}`).style.display = 'inline'
|
|
}
|
|
break
|
|
}
|
|
}
|
|
dialog.showModal()
|
|
})
|
|
|
|
// Close button for settings dialog
|
|
document.querySelector('#dialogSettings .dialog-close').addEventListener('click', () => {
|
|
document.getElementById('dialogSettings').close()
|
|
})
|
|
|
|
// Close button (secondary button)
|
|
document.querySelector('#btnClose').addEventListener('click', () => {
|
|
document.getElementById('dialogSettings').close()
|
|
})
|
|
|
|
const getSensorType = async (sid) => {
|
|
let url = `/srv/getoneproperty?sensorid=${sid}`
|
|
const data = await fetchfromserver(url)
|
|
return data.props
|
|
}
|
|
|
|
// main function:
|
|
// show the map if called w/o any parameter
|
|
// if called with a sensor-ID, show the live chart for this sensor
|
|
async function main() {
|
|
setNoUTC() // no UTC for HighCharts
|
|
// set correct language
|
|
let currentLang = localStorage.getItem('curlang')
|
|
if ((currentLang === null) || (currentLang === '')) {
|
|
currentLang = 'de'
|
|
localStorage.setItem('curlang', currentLang)
|
|
}
|
|
let centercity
|
|
try {
|
|
centercity = JSON.parse(localStorage.getItem('centercity'))
|
|
} catch (e) {
|
|
centercity = params.center
|
|
}
|
|
if((centercity === null) || (centercity.name === '')) {
|
|
centercity = params.center
|
|
}
|
|
localStorage.setItem('centercity', JSON.stringify(centercity))
|
|
params.center = centercity
|
|
if(currentLang === 'en') {
|
|
document.querySelector('#en').checked = true
|
|
params.seldate = 'today'
|
|
} else {
|
|
document.querySelector('#de').checked = true
|
|
params.seldate = 'heute'
|
|
}
|
|
console.log(currentLang)
|
|
if (parseInt(sysparams.csid) !== -1) {
|
|
history.pushState({}, '', `/${sysparams.csid}`)
|
|
} else {
|
|
history.pushState({}, '', '/')
|
|
}
|
|
|
|
dt.showDate(true, params)
|
|
// and show date/time every minute
|
|
setInterval(() => dt.showDate(false, params), 1000)
|
|
|
|
// initialise tabs
|
|
const triggerTabList = [].slice.call(document.querySelectorAll('.tab-button'))
|
|
triggerTabList.forEach(function (triggerEl) {
|
|
triggerEl.addEventListener('click', function (event) {
|
|
event.preventDefault()
|
|
|
|
// Remove active from all tabs
|
|
triggerTabList.forEach(tab => tab.classList.remove('active'))
|
|
document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'))
|
|
|
|
// Add active to clicked tab
|
|
triggerEl.classList.add('active')
|
|
const targetId = triggerEl.getAttribute('data-target')
|
|
document.getElementById(targetId).classList.add('active')
|
|
|
|
let newtab = event.currentTarget.id
|
|
setCurrentTab(newtab)
|
|
let tab = tabtable.find(tab => tab.id === newtab)
|
|
if (document.querySelector(`#${tab.id}err`).innerHTML !== '') { // if there is an error message, clear it
|
|
showError(document.querySelector(`#${tab.id}err`).innerHTML)
|
|
}
|
|
if (newtab === 'maptab') {
|
|
map.showMap(params)
|
|
}
|
|
})
|
|
})
|
|
const csid = parseInt(sysparams.csid)
|
|
if(csid !== -1) {
|
|
spin.spinner.spin(spin.spindiv)
|
|
// check, if sid is of right type (noise)
|
|
const props = await getSensorType(csid)
|
|
if (props.type !== 'noise') {
|
|
// wrong sensor type
|
|
spin.spinner.stop()
|
|
showError('Sensor ID ' + csid + ' is not of type noise')
|
|
return
|
|
}
|
|
params.center.coords = [props.location[0].loc.coordinates[1], props.location[0].loc.coordinates[0]]
|
|
params.sid = csid
|
|
let t = triggerTabList[1]
|
|
|
|
// Switch to live tab
|
|
triggerTabList.forEach(tab => tab.classList.remove('active'))
|
|
document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'))
|
|
t.classList.add('active')
|
|
document.getElementById('t_live').classList.add('active')
|
|
|
|
setCurrentTab('livetab')
|
|
let err = await showChart(params, ttIndex.live)
|
|
spin.spinner.stop()
|
|
if (err.error) {
|
|
let errtxt = document.querySelector(`#${tabtable[ttIndex.live].id}err`).innerHTML
|
|
if (errtxt !== '') {
|
|
showError(errtxt)
|
|
}
|
|
}
|
|
await loadAll(params)
|
|
} else {
|
|
// show the map
|
|
map.showMap(params)
|
|
}
|
|
}
|
|
|
|
main().catch(console.error)
|
|
|
|
})() |