Mercurial > gemma
changeset 2610:5ce1b4b29869
client: waterlevel diagram: refactored code for better readability
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Wed, 13 Mar 2019 11:25:03 +0100 |
parents | 546ed93a9829 |
children | e8c97481438f |
files | client/src/components/gauge/Waterlevel.vue |
diffstat | 1 files changed, 122 insertions(+), 131 deletions(-) [+] |
line wrap: on
line diff
--- a/client/src/components/gauge/Waterlevel.vue Wed Mar 13 10:08:31 2019 +0100 +++ b/client/src/components/gauge/Waterlevel.vue Wed Mar 13 11:25:03 2019 +0100 @@ -5,7 +5,7 @@ <style lang="sass" scoped> .diagram-container /deep/ - .area + .line stroke: steelblue stroke-width: 2 fill: transparent @@ -69,184 +69,200 @@ drawDiagram() { if (!this.selectedGauge || !this.waterlevels.length) return; + // remove old diagram + d3.select(".diagram-container svg").remove(); + + // get HDC/LDC/MW of the gauge let refWaterLevels = JSON.parse( this.selectedGauge.properties.reference_water_levels ); + // CREATE SVG AND SET DIMENSIONS/MARGINS + let svgWidth = document.querySelector(".diagram-container").clientWidth; let svgHeight = document.querySelector(".diagram-container").clientHeight; - d3.select(".diagram-container svg").remove(); - let svg = d3.select(".diagram-container").append("svg"); - svg.attr("width", "100%").attr("height", "100%"); - let margin = { top: 20, right: 20, bottom: 110, left: 40 }, - margin2 = { - top: svgHeight - margin.top - 50, + let svg = d3 + .select(".diagram-container") + .append("svg") + .attr("width", "100%") + .attr("height", "100%"); + let mainMargin = { top: 50, right: 20, bottom: 110, left: 40 }, + navMargin = { + top: svgHeight - mainMargin.top - 35, right: 20, bottom: 30, left: 40 }, - width = +svgWidth - margin.left - margin.right, - height = +svgHeight - margin.top - margin.bottom, - height2 = +svgHeight - margin2.top - margin2.bottom; + width = +svgWidth - mainMargin.left - mainMargin.right, + mainHeight = +svgHeight - mainMargin.top - mainMargin.bottom, + navHeight = +svgHeight - navMargin.top - navMargin.bottom; + // PREPARING AXES/SCALING + + // scaling helpers to convert real values to pixels + // based on the diagrams dimensions let x = d3.scaleTime().range([0, width]), x2 = d3.scaleTime().range([0, width]), - y = d3.scaleLinear().range([height, 0]), - y2 = d3.scaleLinear().range([height2, 0]); - - let xAxis = d3.axisBottom(x), - xAxis2 = d3.axisBottom(x2), - yAxis = d3.axisLeft(y); - + y = d3.scaleLinear().range([mainHeight, 0]), + y2 = d3.scaleLinear().range([navHeight, 0]); // find min/max values for the waterlevel axis - // based on hdc/ldc +/- 100 cm - let xMinMax = d3.extent( + // including hdc/ldc (+/- 100 cm) + let WaterlevelMinMax = d3.extent( [ ...this.waterlevels, { waterlevel: refWaterLevels.HDC + 100 }, { waterlevel: Math.max(refWaterLevels.LDC - 100, 0) } ], - function(d) { - return d.waterlevel; - } + d => d.waterlevel ); - - x.domain( - d3.extent(this.waterlevels, function(d) { - return d.date; - }) - ); - y.domain(xMinMax); + // setting the min and max values for the diagram axes + x.domain(d3.extent(this.waterlevels, d => d.date)); + y.domain(WaterlevelMinMax); x2.domain(x.domain()); y2.domain(y.domain()); - - let brush = d3 - .brushX() - .handleSize(4) - .extent([[0, 0], [width, height2]]) - .on("brush end", brushed); + // creating the axes based on these scales + let xAxis = d3.axisBottom(x), + xAxis2 = d3.axisBottom(x2), + yAxis = d3.axisLeft(y); - let zoom = d3 - .zoom() - .scaleExtent([1, Infinity]) - .translateExtent([[0, 0], [width, height]]) - .extent([[0, 0], [width, height]]) - .on("zoom", zoomed); + // PREPARING CHART FUNCTIONS // waterlevel line in big chart - let area = d3 + let mainLineChart = d3 .line() .curve(d3.curveMonotoneX) - .x(function(d) { - return x(d.date); - }) - .y(function(d) { - return y(d.waterlevel); - }); - + .x(d => x(d.date)) + .y(d => y(d.waterlevel)); // waterlevel line in small chart - let area2 = d3 + let navLineChart = d3 .line() .curve(d3.curveMonotoneX) - .x(function(d) { - return x2(d.date); - }) - .y(function(d) { - return y2(d.waterlevel); - }); - + .x(d => x2(d.date)) + .y(d => y2(d.waterlevel)); + // hdc/ldc/mw let refWaterlevelLine = d3 .line() .x(d => x(d.x)) .y(d => y(d.y)); + // DRAWING MAINCHART + + // define visible area + // everything outside this area will be hidden (clipped) svg .append("defs") .append("clipPath") .attr("id", "clip") .append("rect") .attr("width", width) - .attr("height", height); + .attr("height", mainHeight); - let focus = svg + let mainChart = svg .append("g") - .attr("class", "focus") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + .attr("transform", `translate(${mainMargin.left}, ${mainMargin.top})`); - let context = svg + // axes + mainChart .append("g") - .attr("class", "context") - .attr( - "transform", - "translate(" + margin2.left + "," + margin2.top + ")" - ); + .attr("transform", `translate(0, ${mainHeight})`) + .call(xAxis); + mainChart.append("g").call(yAxis); - focus + // waterlevel chart + mainChart .append("path") .datum(this.waterlevels) - .attr("class", "area") - .attr("d", area); + .attr("class", "line") + .attr("d", mainLineChart); - focus + // reference waterlevels + let lastDate = this.waterlevels[this.waterlevels.length - 1].date; + mainChart .append("path") .datum([ { x: 0, y: refWaterLevels.HDC }, - { - x: this.waterlevels[this.waterlevels.length - 1].date, - y: refWaterLevels.HDC - } + { x: lastDate, y: refWaterLevels.HDC } ]) .attr("class", "hdc-line") .attr("d", refWaterlevelLine); - - focus + mainChart .append("path") .datum([ { x: 0, y: refWaterLevels.LDC }, - { - x: this.waterlevels[this.waterlevels.length - 1].date, - y: refWaterLevels.LDC - } + { x: lastDate, y: refWaterLevels.LDC } ]) .attr("class", "ldc-line") .attr("d", refWaterlevelLine); - - focus + mainChart .append("path") .datum([ { x: 0, y: refWaterLevels.MW }, - { - x: this.waterlevels[this.waterlevels.length - 1].date, - y: refWaterLevels.MW - } + { x: lastDate, y: refWaterLevels.MW } ]) .attr("class", "mw-line") .attr("d", refWaterlevelLine); - focus + // DRAWING NAVCHART + + let navChart = svg + .append("g") + .attr("transform", `translate(${navMargin.left}, ${navMargin.top})`); + + // axis (nav chart only has y-axis) + navChart .append("g") .attr("class", "axis axis--x") - .attr("transform", "translate(0," + height + ")") - .call(xAxis); + .attr("transform", `translate(0, ${navHeight})`) + .call(xAxis2); - focus - .append("g") - .attr("class", "axis axis--y") - .call(yAxis); - - context + // waterlevel chart + navChart .append("path") .datum(this.waterlevels) - .attr("class", "area") - .attr("d", area2); + .attr("class", "line") + .attr("d", navLineChart); + + // INTERACTIVITY - context - .append("g") - .attr("class", "axis axis--x") - .attr("transform", "translate(0," + height2 + ")") - .call(xAxis2); + // selecting time period in nav chart + let brush = d3 + .brushX() + .handleSize(4) + .extent([[0, 0], [width, navHeight]]) + .on("brush end", () => { + if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") + return; // ignore brush-by-zoom + let s = d3.event.selection || x2.range(); + x.domain(s.map(x2.invert, x2)); + mainChart.select(".line").attr("d", mainLineChart); + mainChart.select(".axis--x").call(xAxis); + svg + .select(".zoom") + .call( + zoom.transform, + d3.zoomIdentity.scale(width / (s[1] - s[0])).translate(-s[0], 0) + ); + }); - context + // zooming with mousewheel in main chart + let zoom = d3 + .zoom() + .scaleExtent([1, Infinity]) + .translateExtent([[0, 0], [width, mainHeight]]) + .extent([[0, 0], [width, mainHeight]]) + .on("zoom", () => { + if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") + return; // ignore zoom-by-brush + let t = d3.event.transform; + x.domain(t.rescaleX(x2).domain()); + mainChart.select(".line").attr("d", mainLineChart); + mainChart.select(".axis--x").call(xAxis); + navChart + .select(".brush") + .call(brush.move, x.range().map(t.invertX, t)); + }); + + navChart .append("g") .attr("class", "brush") .call(brush) @@ -256,34 +272,9 @@ .append("rect") .attr("class", "zoom") .attr("width", width) - .attr("height", height) - .attr("transform", "translate(" + margin.left + "," + margin.top + ")") + .attr("height", mainHeight) + .attr("transform", `translate(${mainMargin.left}, ${mainMargin.top})`) .call(zoom); - - function brushed() { - if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") - return; // ignore brush-by-zoom - let s = d3.event.selection || x2.range(); - x.domain(s.map(x2.invert, x2)); - focus.select(".area").attr("d", area); - focus.select(".axis--x").call(xAxis); - svg - .select(".zoom") - .call( - zoom.transform, - d3.zoomIdentity.scale(width / (s[1] - s[0])).translate(-s[0], 0) - ); - } - - function zoomed() { - if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") - return; // ignore zoom-by-brush - let t = d3.event.transform; - x.domain(t.rescaleX(x2).domain()); - focus.select(".area").attr("d", area); - focus.select(".axis--x").call(xAxis); - context.select(".brush").call(brush.move, x.range().map(t.invertX, t)); - } } }, created() {