diff --git a/agvis/static/index.html b/agvis/static/index.html
deleted file mode 100644
index 505d65c..0000000
--- a/agvis/static/index.html
+++ /dev/null
@@ -1,249 +0,0 @@
-
-
- CURENT LTBWeb
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/agvis/static/js/ConfigControl.js b/agvis/static/js/ConfigControl.js
index 1b92414..67230dc 100644
--- a/agvis/static/js/ConfigControl.js
+++ b/agvis/static/js/ConfigControl.js
@@ -108,28 +108,25 @@ function addSidebarConfig(win, options, sidebar) {
});
- const opt_dimehost = document.querySelector(`#${table_id} input[name='opt_dimehost']`);
- const opt_dimeport = document.querySelector(`#${table_id} input[name='opt_dimeport']`);
- const opt_amin = document.querySelector(`#${table_id} input[name='opt_amin']`);
- const opt_amax = document.querySelector(`#${table_id} input[name='opt_amax']`);
- const opt_vmin = document.querySelector(`#${table_id} input[name='opt_vmin']`);
- const opt_vmax = document.querySelector(`#${table_id} input[name='opt_vmax']`);
- const opt_fmin = document.querySelector(`#${table_id} input[name='opt_fmin']`);
- const opt_fmax = document.querySelector(`#${table_id} input[name='opt_fmax']`);
- const opt_opacity = document.querySelector(`#${table_id} input[name='opt_opacity']`);
- const opt_togglezones = document.querySelector(`#${table_id} input[name='opt_togglezones']`);
+ const opt_dimehost = document.querySelector(`#${table_id} input[name='opt_dimehost']`);
+ const opt_dimeport = document.querySelector(`#${table_id} input[name='opt_dimeport']`);
+ const opt_amin = document.querySelector(`#${table_id} input[name='opt_amin']`);
+ const opt_amax = document.querySelector(`#${table_id} input[name='opt_amax']`);
+ const opt_vmin = document.querySelector(`#${table_id} input[name='opt_vmin']`);
+ const opt_vmax = document.querySelector(`#${table_id} input[name='opt_vmax']`);
+ const opt_fmin = document.querySelector(`#${table_id} input[name='opt_fmin']`);
+ const opt_fmax = document.querySelector(`#${table_id} input[name='opt_fmax']`);
+ const opt_opacity = document.querySelector(`#${table_id} input[name='opt_opacity']`);
+ const opt_togglezones = document.querySelector(`#${table_id} input[name='opt_togglezones']`);
const opt_togglebuslabels = document.querySelector(`#${table_id} input[name='opt_togglebuslabels']`);
-
- const opt_loadconfig = document.querySelector(`#${table_id} input[name='opt_loadconfig']`);
- const opt_saveconfig = document.querySelector(`#${table_id} input[name='opt_saveconfig']`);
- const opt_loadsimulation = document.querySelector(`#${table_id} input[name='opt_loadsimulation']`);
- const opt_savesimulation = document.querySelector(`#${table_id} input[name='opt_savesimulation']`);
-
- const opt_alabel = document.querySelector(`#${table_id} span[name='opt_alabel']`);
- const opt_vlabel = document.querySelector(`#${table_id} span[name='opt_vlabel']`);
- const opt_flabel = document.querySelector(`#${table_id} span[name='opt_flabel']`);
-
- const ts_up = document.querySelector(`#${table_id} input[name='ts_up']`);
+ const opt_loadconfig = document.querySelector(`#${table_id} input[name='opt_loadconfig']`);
+ const opt_saveconfig = document.querySelector(`#${table_id} input[name='opt_saveconfig']`);
+ const opt_loadsimulation = document.querySelector(`#${table_id} input[name='opt_loadsimulation']`);
+ const opt_savesimulation = document.querySelector(`#${table_id} input[name='opt_savesimulation']`);
+ const opt_alabel = document.querySelector(`#${table_id} span[name='opt_alabel']`);
+ const opt_vlabel = document.querySelector(`#${table_id} span[name='opt_vlabel']`);
+ const opt_flabel = document.querySelector(`#${table_id} span[name='opt_flabel']`);
+ const ts_up = document.querySelector(`#${table_id} input[name='ts_up']`);
//Updating function for Timestamp
ts_up.onclick = function() {
@@ -281,6 +278,8 @@ function addSidebarConfig(win, options, sidebar) {
if (win.contourLayer.variableName === "theta") {
win.contourLayer.updateRange(options.amin, options.amax);
}
+
+ win.legend.update();
}
}
@@ -299,6 +298,8 @@ function addSidebarConfig(win, options, sidebar) {
if (win.contourLayer.variableName === "theta") {
win.contourLayer.updateRange(options.amax, options.amax);
}
+
+ win.legend.update();
}
};
@@ -317,6 +318,8 @@ function addSidebarConfig(win, options, sidebar) {
if (win.contourLayer.variableName === "V") {
win.contourLayer.updateRange(options.vmin, options.vmax);
}
+
+ win.legend.update();
}
};
@@ -335,6 +338,8 @@ function addSidebarConfig(win, options, sidebar) {
if (win.contourLayer.variableName === "V") {
win.contourLayer.updateRange(options.vmin, options.vmax);
}
+
+ win.legend.update();
}
}
@@ -353,6 +358,8 @@ function addSidebarConfig(win, options, sidebar) {
if (win.contourLayer.variableName === "freq") {
win.contourLayer.updateRange(options.fmin, options.fmax);
}
+
+ win.legend.update();
}
};
@@ -371,6 +378,8 @@ function addSidebarConfig(win, options, sidebar) {
if (win.contourLayer.variableName === "freq") {
win.contourLayer.updateRange(options.fmin, options.fmax);
}
+
+ win.legend.update();
}
};
@@ -388,6 +397,8 @@ function addSidebarConfig(win, options, sidebar) {
dt = dt.toUTCString();
document.cookie = `opacity${win.num}=${val};expires=${dt};path=/`;
+
+ win.legend.update();
}
};
diff --git a/agvis/static/js/Legend.js b/agvis/static/js/Legend.js
new file mode 100644
index 0000000..547cceb
--- /dev/null
+++ b/agvis/static/js/Legend.js
@@ -0,0 +1,167 @@
+/**
+ * ==================================================================================================
+ * File Name: Legend.js
+ * Author: Zack Malkmus
+ * Date: 9/6/2023
+ * Description: Creates a draggable and updating legend for LTB AGVis
+ * ==================================================================================================
+ */
+
+/**
+ * Create a new dynamic legend for AGVis.
+ *
+ * This class creates a new legend for AGVis that is draggable and updatable
+ *
+ * @author Zack Malkmus
+ * @param {Object} win - The AGVis window that the legend is associated with.
+ * @returns {Object} The new legend element.
+ */
+L.DynamicLegend = L.Control.extend({
+ options: {
+ position: 'bottomright',
+ },
+
+ /**
+ * Initialize the legend.
+ * @memberof L.DynamicLegend
+ * @param {Object} win - The AGVis window that the legend is associated with.
+ * @returns {void}
+ * @constructor
+ */
+ initialize: function(win) {
+ this.win = win;
+ },
+
+ /**
+ * Create the legend element.
+ * @memberof L.DynamicLegend
+ * @returns {Element} - The legend element.
+ */
+ onAdd: function() {
+ // Legend Container
+ let div = L.DomUtil.create('div', 'info leaflet-bar');
+ L.DomEvent.disableClickPropagation(div);
+ L.DomEvent.disableScrollPropagation(div);
+ div.style.backgroundColor = 'white';
+ div.style.boxSizing = 'border-box';
+ div.style.padding = '5px';
+ div.style.paddingLeft = '15px';
+ div.style.paddingRight = '15px';
+ div.style.width = '300px';
+ div.style.height = '75px';
+ div.style.userSelect = 'none';
+
+ // Make the legend draggable
+ L.DomEvent.on(div, 'mousedown', this.onDragStart, this);
+ L.DomEvent.on(document, 'mousemove', this.onDrag, this);
+ L.DomEvent.on(document, 'mouseup', this.onDragEnd, this);
+
+ // Title and Units labels
+ let topLabels = L.DomUtil.create('div', '', div);
+ topLabels.style.width = '100%';
+ topLabels.style.height = '16px';
+
+ this.title = L.DomUtil.create('div', '', topLabels);
+ this.title.style.float = 'left';
+ this.title.style.width = '45%';
+
+ this.units = L.DomUtil.create('div', '', topLabels);
+ this.units.style.float = 'right';
+ this.units.style.width = '55%';
+
+ // Create Dynamic Gradient
+ let gradient = L.DomUtil.create('div', '', div);
+ gradient.style.backgroundImage = 'linear-gradient(to right, #0b00ff, #0044ff, #0044ff, #279eff, #41cef1, #41cef1, #ffffff, #ffb41e, #ffb41e, #ff9537, #ff4727, #ff4727, #ff0000)';
+ gradient.style.width = '100%';
+ gradient.style.height = '20px';
+ gradient.style.border = '1px solid grey';
+ gradient.style.borderRadius = '2px';
+
+ // Min and max labels
+ let bottomLabels = L.DomUtil.create('div', '', div);
+ bottomLabels.style.width = '100%';
+
+ this.min = L.DomUtil.create('div', '', bottomLabels);
+ this.min.style.float = 'left';
+
+ this.max = L.DomUtil.create('div', '', bottomLabels);
+ this.max.style.float = 'right';
+
+ // Initialize the legend values
+ this.update();
+
+ return div;
+ },
+
+ /**
+ * Set the legend to be draggable.
+ * @memberof L.DynamicLegend
+ * @param {Object} e - The event object.
+ * @returns {void}
+ */
+ onDragStart: function (e) {
+ this.dragging = true;
+ this.dragStartX = e.clientX;
+ this.dragStartY = e.clientY;
+ this.originalX = parseInt(this._container.style.left) || 0;
+ this.originalY = parseInt(this._container.style.top) || 0;
+
+ },
+
+ /**
+ * Update the legend position while dragging.
+ * @memberof L.DynamicLegend
+ * @param {Object} e - The event object.
+ * @returns {void}
+ */
+ onDrag: function (e) {
+ if (!this.dragging) return;
+
+ let deltaX = e.clientX - this.dragStartX;
+ let deltaY = e.clientY - this.dragStartY;
+
+ this._container.style.left = this.originalX + deltaX + 'px';
+ this._container.style.top = this.originalY + deltaY + 'px';
+
+ },
+
+ /**
+ * Stop dragging the legend.
+ * @memberof L.DynamicLegend
+ * @returns {void}
+ */
+ onDragEnd: function () {
+ this.dragging = false;
+ },
+
+ /**
+ * Used to update the legend values when the user changes the state or min/max values of the state.
+ * @memberof L.DynamicLegend
+ * @returns {void}
+ * @see ConfigControl.js
+ * @see Window.js
+ */
+ update: function() {
+ // Voltage Angle
+ if (this.win.state == this.win.states.angl) {
+ this.title.innerHTML = "V Angle";
+ this.units.innerHTML = "(rad)";
+ this.min.innerHTML = "" + this.win.options.amin + "";
+ this.max.innerHTML = "" + this.win.options.amax + "";
+ }
+ // Voltage Magnitude
+ else if (this.win.state == this.win.states.volt) {
+ this.title.innerHTML = "V Magnitude";
+ this.units.innerHTML = "(p.u.)";
+ this.min.innerHTML = "" + this.win.options.vmin + "";
+ this.max.innerHTML = "" + this.win.options.vmax + "";
+ }
+ // Frequency
+ else if (this.win.state == this.win.states.freq) {
+ this.title.innerHTML = "Frequency";
+ this.units.innerHTML = "(p.u.)";
+ this.min.innerHTML = "" + this.win.options.fmin + "";
+ this.max.innerHTML = "" + this.win.options.fmax + "";
+ }
+ },
+});
\ No newline at end of file
diff --git a/agvis/static/js/Window.js b/agvis/static/js/Window.js
index d5796ca..f0c1082 100644
--- a/agvis/static/js/Window.js
+++ b/agvis/static/js/Window.js
@@ -3,21 +3,25 @@ class Window {
this.workspace = {};
this.history = {};
+ // Keep track of the view state
+ this.states = {
+ angl: 0,
+ volt: 1,
+ freq: 2,
+ }
+ this.state = this.states.freq;
+
this.num = num;
this.options = options;
- this.multilayer = [];
- this.multihistory = [];
- this.mlayercur = 0;
- this.mnumfree = 0;
-
-
-
+ this.multilayer = [];
+ this.multihistory = [];
+ this.mlayercur = 0;
+ this.mnumfree = 0;
//Loops every 17 milliseconds to update the animation for the independent simulation data
//Animation step is associated with receiving info from DiME, so we have to use this for the bundled version
setInterval(function(multilayer) {
-
let timestep = Number(Date.now());
for (let i = 0; i < multilayer.length; i++) {
@@ -31,8 +35,6 @@ class Window {
let pt = (timestep - multi.curtime) / 1000;
multi.pbar.updatePlaybackBar(pt, timestep);
multi.curtime = Number(timestep);
-
-
}
}, 17, this.multilayer);
@@ -65,22 +67,16 @@ class Window {
zoom: 5,
});
- //this.map.timescale = 1.0;
- //this.map.end_time = null;
this.map.handshake = true;
-
+ this.legend = new L.DynamicLegend(this, options).addTo(this.map);
this.pbar = new PlaybackControl(this, options);
-
this.tileLayer = L.tileLayer(TILE_LAYER_URL).addTo(this.map);
-
this.zoneLayer = L.zoneLayer().addTo(this.map);
this.topologyLayer = L.topologyLayer().addTo(this.map);
this.contourLayer = L.contourLayer().addTo(this.map);
this.communicationLayer = L.communicationLayer().addTo(this.map);
this.searchLayer = L.searchLayer().addTo(this.map);
-
this.map.addControl(this.searchLayer.control);
-
this.simTimeBox = L.simTimeBox({ position: 'topright' }).addTo(this.map);
// side bar
@@ -274,9 +270,6 @@ class Window {
let dt = (currentTime - firstTime) / 1000.0;
-
-
-
if (self.end_time !== null) {
dt *= self.timescale;
}
@@ -285,8 +278,6 @@ class Window {
self.pbar.updatePlaybackBar(self.time);
-
-
self.workspace.currentTimeInSeconds = self.time;
firstTime = currentTime;
@@ -299,10 +290,7 @@ class Window {
self.topologyLayer.update(self.workspace);
self.contourLayer.update(self.workspace);
self.searchLayer.update(self.workspace, self);
-
-
-
-
+
//console.log(self.workspace.Varvgs);
if (self.workspace.Varvgs) {
@@ -358,6 +346,8 @@ class Window {
self.contourLayer.showVariable("theta");
self.contourLayer.updateRange(amin, amax);
+ self.state = self.states.angl;
+ self.legend.update();
});
const voltageButton = L.easyButton('V', function(btn, map) {
@@ -366,6 +356,8 @@ class Window {
self.contourLayer.showVariable("V");
self.contourLayer.updateRange(vmin, vmax);
+ self.state = self.states.volt;
+ self.legend.update();
});
const freqButton = L.easyButton('f', function(btn, map) {
@@ -374,6 +366,8 @@ class Window {
self.contourLayer.showVariable("freq");
self.contourLayer.updateRange(fmin, fmax);
+ self.state = self.states.freq;
+ self.legend.update();
});
/// Added toggle buttons for different layer views
@@ -461,4 +455,4 @@ class Window {
save() {
return dime.dimebdumps({history: this.history, workspace: this.workspace});
}
-}
+}
\ No newline at end of file
diff --git a/agvis/templates/index.html b/agvis/templates/index.html
index 68f8c96..51f4656 100644
--- a/agvis/templates/index.html
+++ b/agvis/templates/index.html
@@ -131,6 +131,7 @@
+