LoRaWAN-Signal-Mapping/client_src/js/LayerAI.mjs

139 lines
3.2 KiB
JavaScript

"use strict";
import path from 'path';
import L from 'leaflet';
import {
loadLayersModel as tf_loadLayersModel,
tensor as tf_tensor
} from '@tensorflow/tfjs';
import chroma from 'chroma-js';
import GetFromUrl from './Helpers/GetFromUrl.mjs';
import Config from './ClientConfig.mjs';
import { normalise } from '../../common/Math.mjs';
class LayerAI {
get gateway_bounds() {
let result = {
east: Infinity,
west: -Infinity,
north: Infinity,
south: -Infinity
};
for(let gateway of this.index.index) {
result.east = Math.min(gateway.longitude, result.east);
result.west = Math.max(gateway.longitude, result.west);
result.north = Math.min(gateway.latitude, result.north);
result.south = Math.max(gateway.latitude, result.south);
}
return result;
}
constructor(map) {
this.map = map;
this.gateways = new Map();
}
async setup() {
this.index = JSON.parse(
await GetFromUrl(Config.ai_index_file)
);
console.log(this.index);
for(let gateway of this.index.index) {
this.gateways.set(
gateway.id,
await tf_loadLayersModel(`${window.location.href}/${path.dirname(Config.ai_index_file)}/${gateway.id}/model.json`)
);
}
this.layer = this.generate_layer();
this.layer.addTo(this.map);
console.log("[Layer/AI] Complete");
}
generate_layer() {
console.log("[Layer/AI] Rendering map");
let map = this.render_map();
console.log("[Layer/AI] Passing to Leaflet");
return L.geoJSON(map, {
style: (feature) => { return {
stroke: true,
color: feature.properties.colour,
weight: 1,
fillColor: feature.properties.colour,
fillOpacity: 0.4
} }
});
}
render_map() {
// FUTURE: Do this in a web worker?
let map_bounds = this.gateway_bounds;
map_bounds.north += Config.border.lat;
map_bounds.south -= Config.border.lat;
map_bounds.east += Config.border.lng;
map_bounds.west -= Config.border.lng;
let coverage = [],
colour_scale = chroma.scale([
Config.colour_scale.min,
Config.colour_scale.max
]).domain(
this.index.properties.rssi_min,
this.index.properties.rssi_max
);
for(let lat = map_bounds.south; lat < map_bounds.north; lat += Config.step.lat) {
for(let lng = map_bounds.west; lng < map_bounds.east; lng += Config.step.lng) {
let max_predicted_rssi = -Infinity;
for(let [, ai] of this.gateways) {
let next_prediction = ai.predict(
tf_tensor([ lat, lng ], [1, 2])
);
max_predicted_rssi = Math.max(
max_predicted_rssi,
next_prediction.arraySync()[0][0]
);
}
max_predicted_rssi = normalise(max_predicted_rssi,
{ min: 0, max: 1 },
{
min: this.index.properties.rssi_min,
max: this.index.properties.rssi_max
}
);
coverage.push({
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [
[ // Outer shape
[lng, lat],
[lng, lat + Config.step.lat],
[lng + Config.step.lng, lat + Config.step.lat],
[lng + Config.step.lng, lat]
]
// If there were any holes in the shape, we'd put them here
]
},
properties: {
colour: colour_scale(max_predicted_rssi).toString()
}
})
}
}
return coverage;
}
}
export default LayerAI;