WIP WIP WIP ** first git - markers work as expected

This commit is contained in:
rxf
2023-03-14 17:34:48 +01:00
commit b270370105
29 changed files with 2399 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
// all date and time functions
import { DateTime } from './luxon.min.js'
// Show date and time (every minute)
export const showDate = (sofort) => {
let d = DateTime.now()
if(sofort || d.second === 0) {
$('#h1datum').text(d.toFormat('yyyy-MM-dd HH:mm'))
}
}
export const formatJSDate = (d) => {
return DateTime.fromJSDate(d).toFormat('yyyy-LL-dd HH:mm:ss')
}
+45
View File
@@ -0,0 +1,45 @@
// Client side javascript
"use strict"
import * as map from './map.js'
import * as dt from './datetime.js'
$(async() => {
// global constants
const Stuttgart = [48.779, 9.16]
const MAXROWS_IN_TABLE = 1000
const defaultStartime = '2023-01-01 00:00'
// END global constants
// global variables
let params = { // set defaults
drawlines: false,
nbrentries: 0,
device: '',
coordinates: false,
center: Stuttgart,
zoom: 13,
refresh: 0,
starttime: '2023-01-01T00:00:00Z', //date2ISO(defaultStartime),
endtime: '2023-03-01T00:00:00Z' //date2ISO(emptyTimes.emptyHMtime)
}
// END global variables
async function main() {
// show dat/time at start
dt.showDate(true)
// and show date/time every minute
setInterval(() => dt.showDate(false), 1000)
// show version and version-date
$('#versn').text(`Version: ${sysparams.version} vom ${sysparams.date.slice(0,10)}`)
// show the map
map.showMap(params)
await map.buildMarkers()
}
main().catch(console.error)
})
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+300
View File
@@ -0,0 +1,300 @@
// all function related to the map
import * as dt from './datetime.js'
let map
let bounds
let popuptext = ''
const colorscale = ['#d53e4f', '#fc8d59', '#fee08b', '#e6f598', '#99d594', '#3288bd', '#808080'];
const dba = [100, 80, 60, 40, 20, 0, -999];
export const showMap = (params) => {
map = L.map('map',{ scrollWheelZoom: false}).setView(params.center, parseInt(params.zoom))
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.js.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map)
bounds = map.getBounds();
map.on('moveend', async function () {
bounds = map.getBounds()
await buildMarkers(bounds)
});
let legend = L.control({position: 'bottomright'})
legend.onAdd = function (map) {
let div = L.DomUtil.create('div', 'info legend')
let div_color = L.DomUtil.create('div', 'info legend inner', div)
div_color.innerHTML += 'dbA<br />';
// loop through our density intervals and generate a label with a colored square for each interval
for (let i = 0; i < dba.length - 1; i++) {
div_color.innerHTML +=
'<i style="background:' + colorscale[i] + '"></i>' +
'&nbsp;&nbsp;' + dba[i] + (i == 0 ? '+' : '') + '<br />'
}
div_color.innerHTML += '&nbsp;<i style="background:' + getColor(dba[dba.length - 1]) + '"></i> offline'
return div
};
legend.addTo(map)
}
function getColor(d) {
let val = parseInt(d);
for (let i = 0; i < dba.length; i++) {
if (val >= dba[i]) {
return (colorscale[i]);
}
}
}
function calcPolygon(bounds) {
return L.polygon([[bounds.getNorth(), bounds.getWest()], [bounds.getNorth(), bounds.getEast()], [bounds.getSouth(), bounds.getEast()], [bounds.getSouth(), bounds.getWest()]], {
color: 'black',
fillOpacity: 0.5
});
;
}
async function plotMap(cid, poly) {
// if sensor nbr is give, find coordinates, else use Stuttgart center
debug_log('plotMap()');
let myLatLng;
if (cid != -1) {
myLatLng = await getSensorKoords(cid);
} else {
let stgt = await getCoords('Stuttgart');
myLatLng = {lat: parseFloat(stgt.lat), lng: parseFloat(stgt.lon)};
}
// generate map centered on Stuttgart
map = L.map('map').setView(myLatLng, firstZoom);
L.tileLayer('https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png', {
maxZoom: 17,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
bounds = map.getBounds();
map.scrollWheelZoom.disable();
map.on('moveend', async function () {
bounds = map.getBounds();
await buildMarkers(bounds)
});
let legend = L.control({position: 'bottomright'});
legend.onAdd = function (map) {
let div = L.DomUtil.create('div', 'info legend');
let div_color = L.DomUtil.create('div', 'info legend inner', div);
div_color.innerHTML += 'dbA<br />';
// loop through our density intervals and generate a label with a colored square for each interval
for (let i = 0; i < dba.length - 1; i++) {
div_color.innerHTML +=
'<i style="background:' + colorscale[i] + '"></i>' +
'&nbsp;&nbsp;' + dba[i] + (i == 0 ? '+' : '') + '<br />';
}
div_color.innerHTML += '&nbsp;<i style="background:' + getColor(dba[dba.length - 1]) + '"></i> offline';
return div;
};
legend.addTo(map);
let infobutton = L.control({position: 'topright'});
infobutton.onAdd = function (map) {
let div = L.DomUtil.create('div');
div.innerHTML = '<button class="cb centerbutt">neu zentrieren</button>';
div.onclick = function () {
dialogCenter.dialog('open');
console.log('Clicked on Zentrieren');
}
return div;
}
infobutton.addTo(map);
if (useStgtBorder) {
fetchStuttgartBounds();
}
if (myLatLng.error != undefined) {
showError(3, "Kein Lärmsensor", cid);
} else {
await buildMarkers(bounds);
map.on('popupopen', function () {
$('.speciallink').click(function (x) {
showGrafik(clickedSensor);
});
});
}
}
// With all Markers in cluster (markers) calculate the median
// of the values. With this median fetch the color and return it.
// If there are 'offline' sensors (value == -1) strip then before
// calculating the median. If there are only offline sensor, return
// color of value==-1 (dark gray).
function getMedian(markers) {
markers.sort(function (a, b) { // first sort, the lowest first
let y1 = a.options.value;
let y2 = b.options.value;
if (y1 < y2) {
return -1;
}
if (y2 < y1) {
return 1;
}
return 0;
});
console.log(markers);
let i = 0; // now find the 'offlines' (value == -1)
for (i = 0; i < markers.length; i++) {
if (markers[i].options.value != -1) {
break;
}
}
markers.splice(0, i); // remove these from array
let lang = markers.length;
if (lang > 1) { //
if ((lang % 2) == 1) { // uneven ->
return getColor(markers[(Math.floor(lang / 2))].options.value); // median is in the middle
} else { // evaen ->
lang = lang / 2; // median is mean of both middle values
console.log(lang);
let wert = (markers[lang - 1].options.value +
markers[lang].options.value) / 2;
return getColor(wert);
}
} else if (lang == 1) { // only one marker -> return its color
return getColor(markers[0].options.value);
}
return getColor(-1); // only offlines
}
function buildIcon(color, n, indoor) {
let x = 100;
if (n < 10) {
x = 200;
} else if (n < 100) {
x = 150;
}
let txtColor = "black";
if (color == colorscale[5]) {
txtColor = "white"
}
let circIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600">' +
'<circle cx="300" cy="300" r="250" fill="' + color + '" stroke="black" stroke-width="20" />';
if (n !== 0) {
circIcon +=
'<text id="marker_text" x="' + x + '" ' +
'y="400" font-size="1500%" font-family="Verdana,Lucida Sans Unicode,sans-serif" ' +
'fill="' + txtColor + '">' + n + '</text>';
}
if(indoor !== 0) {
circIcon += '<line x1="125" y1="475" x2="475" y2="125" stroke="black" stroke-width="20" />'
}
circIcon += '</svg>';
let circIconUrl = encodeURI("data:image/svg+xml," + circIcon).replace(new RegExp('#', 'g'), '%23');
return circIconUrl;
}
export async function buildMarkers() {
// debug_log('buildMarkers(bounds)');
const box = {
west: bounds.getWest(), south: bounds.getSouth(),
east: bounds.getEast(), north: bounds.getNorth()
}
const url = `http://localhost:3004/getmapdata?type=noise&box=${bounds.toBBoxString()}`
let ret = await fetch(url)
.catch(e => {
console.log(e)
});
let sensors = await ret.json()
let markers = L.markerClusterGroup({
spiderfyOnMaxZoom: true,
showCoverageOnHover: false,
zoomToBoundsOnClick: true,
// disableClusteringAtZoom: 14,
iconCreateFunction: function (cluster) {
let mymarkers = cluster.getAllChildMarkers();
let color = getMedian(mymarkers);
return new L.Icon({
iconUrl: buildIcon(color, cluster.getChildCount(), 0),
iconSize: [35, 35]
});
},
});
for (let x of sensors.values) {
let marker = L.marker([x.location[1], x.location[0]], {
icon: new L.Icon({
iconUrl: buildIcon(getColor(parseInt(x.value)),0,x.indoor),
iconSize: [35, 35],
}),
name: x.id,
value: x.value,
url: '/graph?sid=' + x.id,
lastseen: dt.formatJSDate(x.lastseen),
indoor: x.indoor
})
.on('click', e => onMarkerClick(e, true)) // define click- and
.bindPopup(popuptext); // and bint the popup text
markers.addLayer(marker);
}
map.addLayer(markers);
showLastDate(sensors.lastDate);
}
async function onMarkerClick(e, click) {
let item = e.target.options;
let clickedSensor = item.name;
let offlinetext = `
<tr><td colspan="2"><span style="color:red;">offline</span></td></tr>
<tr><td>Last seen:</td><td>${item.lastseen}</td></tr>`
let normaltext = `
<tr></tr><tr><td>LA_max:</td><td>${item.value}</td></tr>`
let popuptext = `
<div id="infoTitle">
<h4>Sensor: ${item.name}</h4>
<div id="infoTable">
<table>
<tr><td colspan="2"><span style="color:limegreen;">${item.indoor==1 ? "indoor" : ""}</span></td></tr>
${item.value < 0 ? offlinetext : normaltext}
</table>
<div id="infoBtn">
<a href="#" class="speciallink">Grafik anzeigen</a>
</div>
</div>
</div>`
let popup = e.target.getPopup();
popup.setContent(popuptext); // set text into popup
e.target.openPopup(); // show the popup
if (click == true) { // if we clicked
e.target.closePopup(); // show the popup
}
}
async function getCoords(city) {
let url = NOMINATIM_URL + city;
const response = await axios.get(encodeURI(url));
const data = response.data;
return data[0];
}
// Map auf Stadt setzen
async function setCenter(adr) {
let data = await getCoords(adr);
map.setView([parseFloat(data.lat), parseFloat(data.lon)]);
console.log(data);
}