diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f235ed1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.html.erb": "html" + } +} \ No newline at end of file diff --git a/lib/measures/detailed_hvac_viewer/resources/report.html.erb b/lib/measures/detailed_hvac_viewer/resources/report.html.erb index 9695214..263e97f 100644 --- a/lib/measures/detailed_hvac_viewer/resources/report.html.erb +++ b/lib/measures/detailed_hvac_viewer/resources/report.html.erb @@ -1,654 +1,1100 @@ - - -HVAC Detailed Viewer - - - - - - -
- - -
-
- -
- -
- - - - + + function mouseMove(e) { + let xdate = xScale.invert(d3.pointer(e)[0]); + + // hack to get closest index from 8760 hour. probably a much better way to do this. + + let [start, end] = xScale.domain(); + + let domain_fraction = + (xdate.getTime() - start.getTime()) / + (end.getTime() - start.getTime()); + + let selected_date = new Date( + startDate.getTime() + + (start.getTime() - startDate.getTime()) + + domain_fraction * (end.getTime() - start.getTime()) + ); + + let annual_fraction = + (selected_date.getTime() - startDate.getTime()) / + (endDate.getTime() - startDate.getTime()); + + let closest_idx = Math.round(annual_fraction * 8760); + + let closest_date = new Date( + startDate.getTime() + closest_idx * 3.6e6 + ); + + let pointarray = []; + + data_array.forEach((d, i) => { + pointarray.push(d.NodeValues[closest_idx]); + }); + + x_line + .style("opacity", 1) + .attr("x1", xScale(xdate)) + .attr("x2", xScale(xdate)); + + tooltip_div + .style( + "left", + 20 + event.pageX / window.innerWidth + event.pageX + "px" + ) + .style("top", 20 + event.pageY + "px") + .style("transition", "left 100ms, top 100ms") + .style("z-index", 999) + .style("opacity", 0.9).html(` +
+
+ ${d3.timeFormat("%B %d %H:%M")(closest_date)} +
+ ${pointarray + .map((d, i) => { + let node_name = data_array[i].nodeName; + let node_val = d; + + return ` +
+
+
${node_name}: ${node_val}
+
`; + }) + .join("")} +
`); + } + + hover_rect.on("mouseover", mouseOver); + hover_rect.on("mouseout", mouseOut); + hover_rect.on("mousemove", mouseMove); + + /* HANDLE ZOOM & BRUSH */ + + const brush = d3 + .brushX() + .extent([ + [0, 0], + [plotWidth, contextHeight], + ]) + .on("brush end", brushed); + + const zoomFunc = d3 + .zoom() + .scaleExtent([1, Infinity]) + .translateExtent([ + [0, 0], + [plotWidth, plotHeight], + ]) + .extent([ + [0, 0], + [plotWidth, plotHeight], + ]) + .on("zoom", zoomed); + + let idleTimeout; + let idleDelay = 350; + + function idled() { + idleTimeout = null; + } + + function brushed(event) { + if (event.sourceEvent && event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom + var s = event.selection || contextXScale.range(); + + let xdomain = s.map(contextXScale.invert, contextXScale); + xScale.domain(xdomain); + + createLine(xdomain); + x_axis_g.call(d3.axisBottom(xScale)); + } + + function zoomed(event) { + if (event.sourceEvent && event.sourceEvent.type === "brush") return; // ignore zoom-by-brush + var t = event.transform; + let xdomain = t.rescaleX(contextXScale).domain(); + xScale.domain(xdomain); + createLine(xdomain); + x_axis_g.call(d3.axisBottom(xScale)); + context_plot_g + .select(".brush") + .call(brush.move, xScale.range().map(t.invertX, t)); + } + + context_brush_g.call(brush); + + x_axis_g.call(d3.axisBottom(xScale)); + + hover_g.call(zoomFunc); + } + + /* END D3 MULTILINE PLOT */ + + function getRandomColor() { + const letters = "0123456789ABCDEF"; + let color = "#"; + for (let i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; + } + // Call function to attach event listener when the DOM is fully loaded + document.addEventListener("DOMContentLoaded", selectLoopEvent); + +