diff options
Diffstat (limited to 'gui-pdp-monitoring/src/webapp/js/MonitoringChart.js')
-rw-r--r-- | gui-pdp-monitoring/src/webapp/js/MonitoringChart.js | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js b/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js new file mode 100644 index 0000000..514446b --- /dev/null +++ b/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js @@ -0,0 +1,260 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import * as d3 from "d3"; + +/* + * Create a chart and append it to a container + */ +function createChart(data, container, title, unit, lineStroke, nodeColour) { + // Set the dimensions of the canvas + var margin = { + top : 30, + right : 20, + bottom : 30, + left : 50 + }, width = 600 - margin.left - margin.right, height = 270 - margin.top + - margin.bottom; + + // Set the ranges + var x = d3.time.scale().range([ 0, width ]); + var y = d3.scale.linear().range([ height, 0 ]); + + // Define the axes + var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5).innerTickSize( + -height).outerTickSize(0).tickPadding(10); + + var yAxis = d3.svg.axis().scale(y).orient("left").ticks(10).innerTickSize( + -width).outerTickSize(0).tickPadding(10); + + // Define the line + var valueline = d3.svg.line().x(function(d) { + return x(d.timestamp); + }).y(function(d) { + return y(d.value); + }); + + // Add the svg canvas to the container + var svg = d3.select(container).append("svg").attr("preserveAspectRatio", + "xMinYMin meet").attr("viewBox", "0 0 600 400").classed( + "svg-content-responsive", true).append("g").attr("transform", + "translate(" + margin.left + "," + margin.top + ")"); + + // Set the unit for the value + svg.attr("unit", unit); + + // Format the data for the chart + data.forEach(function(d) { + d.timestamp = d.timestamp; + d.value = +d.value; + }); + + // Scale the range of the data + x.domain(d3.extent(data, function(d) { + return d.timestamp; + })); + y.domain([ 0, d3.max(data, function(d) { + return Math.ceil((d.value + 1) / 10) * 10; + }) ]); + + // Set the colour of the line + if (!lineStroke) { + lineStroke = "#5fbadd" + } + + // Set the colour of the circles + if (!nodeColour) { + nodeColour = "#00A9D4" + } + + // Add the valueline path + svg.append("path").attr("class", "line").data(data).attr("unit", unit) + .attr("stroke", lineStroke).attr("d", valueline(data)); + + // Add the scatterplot + svg.selectAll("circle").data(data).enter().append("circle").attr("r", 3.5) + .attr("class", "circle").attr("fill", nodeColour).attr("cx", + function(d) { + return x(d.timestamp); + }).attr("cy", function(d) { + return y(d.value); + }) + + // Apply the tooltip to each node + .on( + "mouseover", + function(d) { + d3.select("body").select(".tooltip").transition() + .duration(50).style("opacity", 1); + d3.select("body").select(".tooltip").html( + formatDate(new Date(d.timestamp)) + "<br/>" + + d.value + (unit ? " " + unit : "")) + .style("left", (d3.event.pageX) + "px").style( + "top", (d3.event.pageY - 28) + "px"); + }).on( + "mouseout", + function(d) { + d3.select("body").select(".tooltip").transition() + .duration(500).style("opacity", 0); + }); + + // Add the X Axis + svg.append("g").attr("class", "x axis").attr("transform", + "translate(0," + height + ")").call(xAxis); + + // Add the Y Axis + svg.append("g").attr("class", "y axis").call(yAxis); + + // Add the title + svg.append("text").attr("x", (width / 2)).attr("y", 0 - (margin.top / 2)) + .attr("text-anchor", "middle").style("font-size", "16px").style( + "text-decoration", "underline").text(title); + + // Add the background + svg.selectAll(".tick:not(:first-of-type) line").attr("stroke", "#777") + .attr("stroke-dasharray", "2,2"); +} + +/* + * Generates random chart data. Used when initializing the charts so that they + * are not empty on load + */ +function generateRandomData() { + var data = []; + for (var i = 0; i < 30; i++) { + data.push({ + timestamp : new Date().getTime() - (i * 5000), + value : Math.floor(Math.random() * 100) + 1 + }); + } + return data; +} + +/* + * Update a chart belonging to a specific container + */ +function updateChart(container, data, nodeColour) { + var margin = { + top : 30, + right : 20, + bottom : 30, + left : 50 + }, width = 600 - margin.left - margin.right, height = 270 - margin.top + - margin.bottom; + var parseDate = d3.time.format("%d-%b-%y").parse; + + // Format the data for the chart + data.forEach(function(d) { + d.timestamp = d.timestamp; + d.value = +d.value; + }); + + // Select the chart + var svg = d3.select(container); + + // Set the ranges + var x = d3.time.scale().range([ 0, width ]); + var y = d3.scale.linear().range([ height, 0 ]); + + // Define the axes + var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5).innerTickSize( + -height).outerTickSize(0).tickPadding(10); + + var yAxis = d3.svg.axis().scale(y).orient("left").ticks(10).innerTickSize( + -width).outerTickSize(0).tickPadding(10); + + // Scale the range of the data + x.domain(d3.extent(data, function(d) { + return d.timestamp; + })); + y.domain([ 0, d3.max(data, function(d) { + return Math.ceil((d.value + 1) / 10) * 10; + }) ]); + + // Update the valueline path + var valueline = d3.svg.line().x(function(d) { + return x(d.timestamp); + }).y(function(d) { + return y(d.value); + }); + + var unit = svg.select(".line").attr("unit"); + + // Remove all nodes + svg.selectAll("circle").remove(); + + // Set the node colour if one is passed in + if (!nodeColour) { + nodeColour = "#00A9D4" + } + + // Make the changes + svg.select(".line").data(data) // change the line + .transition().duration(750).attr("d", valueline(data)); + svg.select(".x.axis") // change the x axis + .transition().duration(750).call(xAxis.ticks(5)); + svg.select(".y.axis") // change the y axis + .transition().duration(750).call(yAxis); + + // Redraw the nodes based on the new data + svg.select("svg").select("g").selectAll("circle").data(data).enter() + .append("circle").attr("r", 3.5).attr("class", "circle").attr( + "fill", nodeColour).attr("cx", function(d) { + return x(d.timestamp); + }).attr("cy", function(d) { + return y(d.value); + }) + + // Apply the tooltip to each node + .on( + "mouseover", + function(d) { + d3.select("body").select(".tooltip").transition() + .duration(50).style("opacity", 1); + d3.select("body").select(".tooltip").html( + formatDate(new Date(d.timestamp)) + "<br/>" + + d.value + (unit ? " " + unit : "")) + .style("left", (d3.event.pageX) + "px").style( + "top", (d3.event.pageY - 28) + "px"); + }).on( + "mouseout", + function(d) { + d3.select("body").select(".tooltip").transition() + .duration(500).style("opacity", 0); + }); + +} + +/* + * Initialize a singleton div used as a floating tooltip for all charts + */ +function initTooltip() { + d3.select("body").append("div").attr("class", "tooltip").attr("id", + "tooltip").style("opacity", 0); +} + +/* + * Format a date object to string + */ +function formatDate(date) { + return date.toLocaleString().replace(',', ''); +} + +export { initTooltip, createChart, updateChart };
\ No newline at end of file |