2019-07-23 12:49:45 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
import path from 'path';
|
|
|
|
|
|
|
|
import L from 'leaflet';
|
2019-07-24 17:08:53 +00:00
|
|
|
import {
|
|
|
|
loadLayersModel as tf_loadLayersModel,
|
|
|
|
tensor as tf_tensor
|
|
|
|
} from '@tensorflow/tfjs';
|
2019-07-23 14:45:29 +00:00
|
|
|
import chroma from 'chroma-js';
|
2019-07-23 12:49:45 +00:00
|
|
|
|
|
|
|
import GetFromUrl from './Helpers/GetFromUrl.mjs';
|
|
|
|
import Config from './ClientConfig.mjs';
|
2019-07-23 14:45:29 +00:00
|
|
|
import { normalise } from '../../common/Math.mjs';
|
2019-07-23 12:49:45 +00:00
|
|
|
|
|
|
|
class LayerAI {
|
|
|
|
get gateway_bounds() {
|
|
|
|
let result = {
|
|
|
|
east: Infinity,
|
|
|
|
west: -Infinity,
|
|
|
|
north: Infinity,
|
|
|
|
south: -Infinity
|
|
|
|
};
|
2019-07-24 17:08:53 +00:00
|
|
|
for(let gateway of this.index.index) {
|
2019-07-23 12:49:45 +00:00
|
|
|
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)
|
|
|
|
);
|
2019-07-23 14:58:11 +00:00
|
|
|
console.log(this.index);
|
2019-07-23 12:49:45 +00:00
|
|
|
|
2019-07-23 14:45:29 +00:00
|
|
|
for(let gateway of this.index.index) {
|
|
|
|
this.gateways.set(
|
|
|
|
gateway.id,
|
2019-07-24 17:08:53 +00:00
|
|
|
await tf_loadLayersModel(`${window.location.href}/${path.dirname(Config.ai_index_file)}/${gateway.id}/model.json`)
|
2019-07-23 14:45:29 +00:00
|
|
|
);
|
2019-07-23 12:49:45 +00:00
|
|
|
}
|
2019-07-23 14:45:29 +00:00
|
|
|
|
|
|
|
this.layer = this.generate_layer();
|
|
|
|
this.layer.addTo(this.map);
|
2019-07-25 13:02:16 +00:00
|
|
|
console.log("[Layer/AI] Complete");
|
2019-07-23 14:45:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
generate_layer() {
|
2019-07-25 13:02:16 +00:00
|
|
|
console.log("[Layer/AI] Rendering map");
|
2019-07-24 17:08:53 +00:00
|
|
|
let map = this.render_map();
|
2019-07-25 13:02:16 +00:00
|
|
|
console.log("[Layer/AI] Passing to Leaflet");
|
2019-07-24 17:08:53 +00:00
|
|
|
return L.geoJSON(map, {
|
2019-07-23 14:45:29 +00:00
|
|
|
style: (feature) => { return {
|
2019-07-25 12:33:11 +00:00
|
|
|
stroke: true,
|
|
|
|
color: feature.properties.colour,
|
|
|
|
weight: 1,
|
|
|
|
|
2019-07-23 14:45:29 +00:00
|
|
|
fillColor: feature.properties.colour,
|
|
|
|
fillOpacity: 0.4
|
|
|
|
} }
|
|
|
|
});
|
2019-07-23 12:49:45 +00:00
|
|
|
}
|
|
|
|
|
2019-07-23 14:45:29 +00:00
|
|
|
render_map() {
|
|
|
|
// FUTURE: Do this in a web worker?
|
2019-07-23 12:49:45 +00:00
|
|
|
let map_bounds = this.gateway_bounds;
|
2019-07-25 13:02:16 +00:00
|
|
|
map_bounds.north += Config.border.lat;
|
|
|
|
map_bounds.south -= Config.border.lat;
|
2019-07-23 12:49:45 +00:00
|
|
|
|
2019-07-25 13:02:16 +00:00
|
|
|
map_bounds.east += Config.border.lng;
|
|
|
|
map_bounds.west -= Config.border.lng;
|
2019-07-23 12:49:45 +00:00
|
|
|
|
2019-07-23 14:45:29 +00:00
|
|
|
let coverage = [],
|
2019-07-24 17:08:53 +00:00
|
|
|
colour_scale = chroma.scale([
|
2019-07-23 14:45:29 +00:00
|
|
|
Config.colour_scale.min,
|
|
|
|
Config.colour_scale.max
|
2019-07-24 17:08:53 +00:00
|
|
|
]).domain(
|
2019-07-23 14:45:29 +00:00
|
|
|
this.index.properties.rssi_min,
|
|
|
|
this.index.properties.rssi_max
|
|
|
|
);
|
|
|
|
|
2019-07-25 13:02:16 +00:00
|
|
|
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) {
|
2019-07-23 14:45:29 +00:00
|
|
|
let max_predicted_rssi = -Infinity;
|
|
|
|
|
|
|
|
for(let [, ai] of this.gateways) {
|
2019-07-24 17:08:53 +00:00
|
|
|
let next_prediction = ai.predict(
|
|
|
|
tf_tensor([ lat, lng ], [1, 2])
|
|
|
|
);
|
2019-07-23 14:45:29 +00:00
|
|
|
max_predicted_rssi = Math.max(
|
|
|
|
max_predicted_rssi,
|
2019-07-24 17:08:53 +00:00
|
|
|
next_prediction.arraySync()[0][0]
|
2019-07-23 14:45:29 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
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: [
|
2019-07-25 12:33:11 +00:00
|
|
|
[ // Outer shape
|
2019-07-25 13:02:16 +00:00
|
|
|
[lng, lat],
|
|
|
|
[lng, lat + Config.step.lat],
|
|
|
|
[lng + Config.step.lng, lat + Config.step.lat],
|
|
|
|
[lng + Config.step.lng, lat]
|
2019-07-25 12:33:11 +00:00
|
|
|
]
|
|
|
|
// If there were any holes in the shape, we'd put them here
|
2019-07-23 14:45:29 +00:00
|
|
|
]
|
|
|
|
},
|
|
|
|
properties: {
|
2019-07-24 17:08:53 +00:00
|
|
|
colour: colour_scale(max_predicted_rssi).toString()
|
2019-07-23 14:45:29 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-07-23 12:49:45 +00:00
|
|
|
|
2019-07-23 14:45:29 +00:00
|
|
|
return coverage;
|
2019-07-23 12:49:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default LayerAI;
|