Write AI layer, but it's untested.
This commit is contained in:
parent
f0694de02c
commit
61edaf6fa3
8 changed files with 106 additions and 22 deletions
|
@ -12,5 +12,10 @@ export default {
|
||||||
border: 0.1,
|
border: 0.1,
|
||||||
|
|
||||||
// The resolution of the coverage map
|
// The resolution of the coverage map
|
||||||
step: 0.005
|
step: 0.005,
|
||||||
|
|
||||||
|
colour_scale: {
|
||||||
|
min: "#ffffff",
|
||||||
|
max: "#2cb42c"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import L from 'leaflet';
|
import L from 'leaflet';
|
||||||
import tf from '@tensorflow/tfjs';
|
import * as tf from '@tensorflow/tfjs';
|
||||||
|
import chroma from 'chroma-js';
|
||||||
|
|
||||||
import GetFromUrl from './Helpers/GetFromUrl.mjs';
|
import GetFromUrl from './Helpers/GetFromUrl.mjs';
|
||||||
import Config from './ClientConfig.mjs';
|
import Config from './ClientConfig.mjs';
|
||||||
|
import { normalise } from '../../common/Math.mjs';
|
||||||
|
|
||||||
class LayerAI {
|
class LayerAI {
|
||||||
get gateway_bounds() {
|
get gateway_bounds() {
|
||||||
|
@ -37,21 +39,28 @@ class LayerAI {
|
||||||
);
|
);
|
||||||
console.log(index);
|
console.log(index);
|
||||||
|
|
||||||
for(let gateway of this.index) {
|
for(let gateway of this.index.index) {
|
||||||
let gateway_data = {
|
this.gateways.set(
|
||||||
// TODO: Swap this out for the real thing - probably a GeoJSON layer or something
|
gateway.id,
|
||||||
// This is just a placeholder
|
await tf.LayersModel.loadModel(`${window.location.href}/${path.dirname(Config.ai_index_file)}/${gateway.id}`)
|
||||||
layer: L.layerGroup([
|
);
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
ai: await tf.loadModel(`${window.location.href}/${path.dirname(Config.ai_index_file)}/${gateway.id}`)
|
|
||||||
}
|
|
||||||
this.gateways.set(gateway.id, gateway_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async render_map() {
|
this.layer = this.generate_layer();
|
||||||
|
this.layer.addTo(this.map);
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_layer() {
|
||||||
|
return L.geoJSON(this.render_map(), {
|
||||||
|
style: (feature) => { return {
|
||||||
|
fillColor: feature.properties.colour,
|
||||||
|
fillOpacity: 0.4
|
||||||
|
} }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render_map() {
|
||||||
|
// FUTURE: Do this in a web worker?
|
||||||
let map_bounds = this.gateway_bounds;
|
let map_bounds = this.gateway_bounds;
|
||||||
map_bounds.north += Config.border;
|
map_bounds.north += Config.border;
|
||||||
map_bounds.south -= Config.border;
|
map_bounds.south -= Config.border;
|
||||||
|
@ -59,7 +68,55 @@ class LayerAI {
|
||||||
map_bounds.east += Config.border;
|
map_bounds.east += Config.border;
|
||||||
map_bounds.west -= Config.border;
|
map_bounds.west -= Config.border;
|
||||||
|
|
||||||
|
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.west; lat < map_bounds.east; lat += Config.step) {
|
||||||
|
for(let lng = map_bounds.south; lng < map_bounds.north; lng += Config.step) {
|
||||||
|
let max_predicted_rssi = -Infinity;
|
||||||
|
|
||||||
|
for(let [, ai] of this.gateways) {
|
||||||
|
max_predicted_rssi = Math.max(
|
||||||
|
max_predicted_rssi,
|
||||||
|
ai.predict(
|
||||||
|
tf.tensor1d([ lat, lng ])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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: [
|
||||||
|
[lat, lng],
|
||||||
|
[lat + Config.step, lng],
|
||||||
|
[lat + Config.step, lng + Config.step],
|
||||||
|
[lat, lng + Config.step]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
colour: colour_scale(max_predicted_rssi)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return coverage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
package-lock.json
generated
23
package-lock.json
generated
|
@ -93,6 +93,12 @@
|
||||||
"@types/integer": "*"
|
"@types/integer": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/chroma-js": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chroma-js/-/chroma-js-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-i9hUiO3bwgmzZUDwBuR65WqsBQ/nwN+H2fKX0bykXCdd8cFQEuIj8vI7FXjyb2f5z5h+pv76I/uakikKSgaqTA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/estree": {
|
"@types/estree": {
|
||||||
"version": "0.0.39",
|
"version": "0.0.39",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
||||||
|
@ -492,9 +498,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30000984",
|
"version": "1.0.30000985",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000984.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000985.tgz",
|
||||||
"integrity": "sha512-n5tKOjMaZ1fksIpQbjERuqCyfgec/m9pferkFQbLmWtqLUdmt12hNhjSwsmPdqeiG2NkITOQhr1VYIwWSAceiA==",
|
"integrity": "sha512-1ngiwkgqAYPG0JSSUp3PUDGPKKY59EK7NrGGX+VOxaKCNzRbNc7uXMny+c3VJfZxtoK3wSImTvG9T9sXiTw2+w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
|
@ -512,6 +518,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
||||||
},
|
},
|
||||||
|
"chroma-js": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-gk71qOrSdBTLbsd0DIUO3QjZL8tTvMwpG1EoXYScy7rI4rcO4EyYH6zGuvCgUDumKumqg0pt6Ua+vWnMJsTYhw=="
|
||||||
|
},
|
||||||
"class-utils": {
|
"class-utils": {
|
||||||
"version": "0.3.6",
|
"version": "0.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||||
|
@ -1041,9 +1052,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.3.194",
|
"version": "1.3.199",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.194.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.199.tgz",
|
||||||
"integrity": "sha512-w0LHR2YD9Ex1o+Sz4IN2hYzCB8vaFtMNW+yJcBf6SZlVqgFahkne/4rGVJdk4fPF98Gch9snY7PiabOh+vqHNg==",
|
"integrity": "sha512-gachlDdHSK47s0N2e58GH9HMC6Z4ip0SfmYUa5iEbE50AKaOUXysaJnXMfKj0xB245jWbYcyFSH+th3rqsF8hA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"emojis-list": {
|
"emojis-list": {
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
"async-mqtt": "^2.3.0",
|
"async-mqtt": "^2.3.0",
|
||||||
"awilix": "^4.2.2",
|
"awilix": "^4.2.2",
|
||||||
"better-sqlite3": "^5.4.0",
|
"better-sqlite3": "^5.4.0",
|
||||||
|
"chroma-js": "^2.0.4",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"leaflet": "^1.5.1",
|
"leaflet": "^1.5.1",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/better-sqlite3": "^5.4.0",
|
"@types/better-sqlite3": "^5.4.0",
|
||||||
|
"@types/chroma-js": "^1.4.1",
|
||||||
"@types/leaflet": "^1.4.6",
|
"@types/leaflet": "^1.4.6",
|
||||||
"rollup": "^1.17.0",
|
"rollup": "^1.17.0",
|
||||||
"rollup-plugin-commonjs": "^10.0.1",
|
"rollup-plugin-commonjs": "^10.0.1",
|
||||||
|
|
|
@ -34,6 +34,9 @@ let plugins = [
|
||||||
|
|
||||||
// not all files you want to resolve are .js files
|
// not all files you want to resolve are .js files
|
||||||
extensions: ['.mjs', '.js', '.jsx', '.json'], // Default: [ '.mjs', '.js', '.json', '.node' ]
|
extensions: ['.mjs', '.js', '.jsx', '.json'], // Default: [ '.mjs', '.js', '.json', '.node' ]
|
||||||
|
|
||||||
|
// Use the polyfills we've installed as dependencies instead of the Node.js natives, since those aren't available in the browser
|
||||||
|
preferBuiltins: false
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// json({
|
// json({
|
||||||
|
|
|
@ -65,7 +65,13 @@ class AITrainer {
|
||||||
this.settings.ai.output_directory,
|
this.settings.ai.output_directory,
|
||||||
"index.json"
|
"index.json"
|
||||||
),
|
),
|
||||||
JSON.stringify(index)
|
JSON.stringify({
|
||||||
|
properties: {
|
||||||
|
rssi_min: this.settings.ai.rssi_min,
|
||||||
|
rssi_max: this.settings.ai.rssi_max
|
||||||
|
},
|
||||||
|
index
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { normalise, clamp } from '../Helpers/Math.mjs';
|
import { normalise, clamp } from '../../common/Math.mjs';
|
||||||
|
|
||||||
class DatasetFetcher {
|
class DatasetFetcher {
|
||||||
constructor({ settings, RSSIRepo }) {
|
constructor({ settings, RSSIRepo }) {
|
||||||
|
|
Loading…
Reference in a new issue