diff --git a/client_src/js/DeviceData.mjs b/client_src/js/DeviceData.mjs new file mode 100644 index 0000000..c0bb8e2 --- /dev/null +++ b/client_src/js/DeviceData.mjs @@ -0,0 +1,43 @@ +"use strict"; + +import Config from './Config.mjs'; + +import GetFromUrl from './Helpers/GetFromUrl.mjs'; + +/** + * Handles and caches data about devices. + */ +class DeviceData { + /** + * Creates a new DeviceData class instance. + */ + constructor() { + this.devices = []; + } + + /** + * Fetches the device data from the server. + * @return {Promise} A promise that resolves when the data has been fetched from the server. + */ + async setup() { + this.devices = JSON.parse(await GetFromUrl( + `${Config.api_root}?action=list-devices` + )); + + // Create a map to help us lookup ids faster + this.device_map = new Map(); + for(let device of this.devices) + this.device_map.set(device.id, device); + } + + /** + * Looks up a device by its id. + * @param {Number} device_id The ID of the device to return + * @return {object} The info about the device with the specified id. + */ + get_by_id(device_id) { + return this.device_map.get(device_id); + } +} + +export default DeviceData; diff --git a/client_src/js/LayerDeviceMarkers.mjs b/client_src/js/LayerDeviceMarkers.mjs index 6d8380c..9ea8400 100644 --- a/client_src/js/LayerDeviceMarkers.mjs +++ b/client_src/js/LayerDeviceMarkers.mjs @@ -12,8 +12,9 @@ import DeviceReadingDisplay from './DeviceReadingDisplay.mjs'; import GetFromUrl from './Helpers/GetFromUrl.mjs'; class LayerDeviceMarkers { - constructor(in_map) { + constructor(in_map, in_device_data) { this.map = in_map; + this.device_data = in_device_data; // Create a new clustering layer this.layer = L.markerClusterGroup({ @@ -22,14 +23,12 @@ class LayerDeviceMarkers { } async setup() { - - // Fetch the device list - let device_list = JSON.parse(await GetFromUrl( - `${Config.api_root}?action=list-devices&only-with-location=yes` - )); - // Add a marker for each device - for (let device of device_list) { + for (let device of this.device_data.devices) { + // If the device doesn't have a location, we're not interested + // FUTURE: We might be able to displaymobile devices by adding additional logic here + if(typeof device.latitude != "number" || typeof device.longitude != "number") + continue; this.add_device_marker(device); } diff --git a/client_src/js/LayerHeatmap.mjs b/client_src/js/LayerHeatmap.mjs index bb61f2a..9a93dd5 100644 --- a/client_src/js/LayerHeatmap.mjs +++ b/client_src/js/LayerHeatmap.mjs @@ -11,8 +11,9 @@ class LayerHeatmap { * Creates a new heatmap manager wrapper class fort he given map. * @param {L.Map} in_map The leaflet map to attach to. */ - constructor(in_map) { + constructor(in_map, in_device_data) { this.map = in_map; + this.device_data = in_device_data; this.overlay_config = { radius: Config.heatmap.blob_radius, @@ -101,6 +102,13 @@ class LayerHeatmap { * @param {object[]} readings_list The array of data points to display. */ set_data(readings_list) { + // Substitute in the device locations + for(let reading of readings_list) { + let device_info = this.device_data.get_by_id(reading.device_id); + reading.latitude = device_info.latitude; + reading.longitude = device_info.longitude; + } + let data_object = { max: 0, data: readings_list diff --git a/client_src/js/MapManager.mjs b/client_src/js/MapManager.mjs index 0efdf02..a958a91 100644 --- a/client_src/js/MapManager.mjs +++ b/client_src/js/MapManager.mjs @@ -9,6 +9,7 @@ import Config from './Config.mjs'; import LayerDeviceMarkers from './LayerDeviceMarkers.mjs'; import LayerHeatmap from './LayerHeatmap.mjs'; import LayerHeatmapGlue from './LayerHeatmapGlue.mjs'; +import DeviceData from './DeviceData.mjs'; import UI from './UI.mjs'; class MapManager { @@ -16,7 +17,7 @@ class MapManager { console.log(Config); } - setup() { + async setup() { // Create the map this.map = L.map("map", { fullscreenControl: true, @@ -40,14 +41,16 @@ class MapManager { this.map.attributionControl.addAttribution("Air Quality Web by Starbeamrainbowlabs"); + // Load the device information + this.device_data = new DeviceData(); + await this.device_data.setup(); + console.log("[map] Device data loaded"); + // Add the device markers console.info("[map] Loading device markers...."); - this.setup_device_markers().then(() => { - console.info("[map] Device markers loaded successfully."); - - // Display a layer controller - this.setup_layer_control(); - }); + this.setup_device_markers() + .then(() => console.info("[map] Device markers loaded successfully.")) + .then(this.setup_layer_control.bind(this)); // Add the heatmap console.info("[map] Loading heatmap...."); @@ -59,6 +62,7 @@ class MapManager { this.ui = new UI(Config, this); this.ui.setup().then(() => console.log("[map] Settings initialised.")); + } setup_time_dimension() { @@ -97,12 +101,12 @@ class MapManager { } async setup_device_markers() { - this.device_markers = new LayerDeviceMarkers(this.map); + this.device_markers = new LayerDeviceMarkers(this.map, this.device_data); await this.device_markers.setup(); } async setup_heatmap() { - this.heatmap = new LayerHeatmap(this.map); + this.heatmap = new LayerHeatmap(this.map, this.device_data); // TODO: Use leaflet-timedimension here // TODO: Allow configuration of the different reading types here