Built with blockbuilder.org
Last active
June 24, 2020 14:29
-
-
Save praveenuics/1961da999ff3cc24814fa08efa4256b6 to your computer and use it in GitHub Desktop.
fresh block
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| license: mit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <html> | |
| <head> | |
| <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> | |
| <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> | |
| <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> | |
| <script src="https://d3js.org/d3.v4.min.js"></script> | |
| <style> | |
| html { | |
| font-family: 'Arial', sans-serif; | |
| } | |
| .header { | |
| margin-top: 60px; | |
| margin-left: 60px; | |
| text-align: left; | |
| } | |
| input { | |
| margin-right: 10px; | |
| } | |
| .form-check-label { | |
| font-size: 0.8em; | |
| margin-right: 20px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class ='container-fluid'> | |
| <div class='row'> | |
| <div id="chart"> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| var glines | |
| var mouseG | |
| var tooltip | |
| var parseDate = d3.timeParse("%Y-%m") | |
| var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"] | |
| var margin = {top: 80, right: 200, bottom: 40, left: 80} | |
| var width = 1400 - margin.left - margin.right | |
| var height = 500 - margin.top - margin.bottom | |
| var lineOpacity = 1 | |
| var lineStroke = "2px" | |
| var axisPad = 6 // axis formatting | |
| var R = 6 //legend marker | |
| var color = d3.scaleOrdinal() | |
| .range(["#2D4057", "#7C8DA4", "#B7433D", "#2E7576", "#EE811D"]); | |
| var data = [ | |
| {date: 2010-01, serverName: 'Server1', count: 70 }, | |
| {date: 2010-02, serverName: 'Server3', count: 60 }, | |
| {date: 2010-03, serverName: 'Server2', count: 40 }, | |
| {date: 2010-05, serverName: 'Server2', count: 20 }, | |
| {date: 2010-05, serverName: 'Server5', count: 10 } | |
| ]; | |
| var res = data.map((d,i) => { | |
| return { | |
| date : parseDate(d.month), | |
| serverName: d.serverName, | |
| count : d.count | |
| } | |
| }) | |
| var xScale = d3.scaleTime() | |
| .domain(d3.extent(res, d=>d.date)) | |
| .range([0, width]) | |
| var yScale = d3.scaleLinear() | |
| .domain([0, d3.max(res, d => d.count)]) | |
| .range([height, 0]); | |
| var svg = d3.select("#chart").append("svg") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom) | |
| .append('g') | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
| // CREATE AXES // | |
| // render axis first before lines so that lines will overlay the horizontal ticks | |
| var xAxis = d3.axisBottom(xScale).ticks(d3.timeYear.every(1)).tickSizeOuter(axisPad*2).tickSizeInner(axisPad*2) | |
| var yAxis = d3.axisLeft(yScale).ticks(10, "s").tickSize(-width) //horizontal ticks across svg width | |
| svg.append("g") | |
| .attr("class", "x axis") | |
| .attr("transform", `translate(0, ${height})`) | |
| .call(xAxis) | |
| .call(g => { | |
| var years = xScale.ticks(d3.timeYear.every(1)) | |
| var xshift = (width/(years.length))/2 | |
| g.selectAll("text").attr("transform", `translate(${xshift}, 0)`) //shift tick labels to middle of interval | |
| .style("text-anchor", "middle") | |
| .attr("y", axisPad) | |
| .attr('fill', '#A9A9A9') | |
| g.selectAll("line") | |
| .attr('stroke', '#A9A9A9') | |
| g.select(".domain") | |
| .attr('stroke', '#A9A9A9') | |
| }) | |
| svg.append("g") | |
| .attr("class", "y axis") | |
| .call(yAxis) | |
| .call(g => { | |
| g.selectAll("text") | |
| .style("text-anchor", "middle") | |
| .attr("x", -axisPad*2) | |
| .attr('fill', '#A9A9A9') | |
| g.selectAll("line") | |
| .attr('stroke', '#A9A9A9') | |
| .attr('stroke-width', 0.7) // make horizontal tick thinner and lighter so that line paths can stand out | |
| .attr('opacity', 0.3) | |
| g.select(".domain").remove() | |
| }) | |
| .append('text') | |
| .attr('x', 50) | |
| .attr("y", -10) | |
| .attr("fill", "#A9A9A9") | |
| .text("Singapore Dollars") | |
| // line generator | |
| var line = d3.line() | |
| .x(d => xScale(d.date)) | |
| .y(d => yScale(d.count)) | |
| renderChart(1) // inital chart render (set default to Bidding Exercise 1 data) | |
| // Update chart when radio button is selected | |
| // d3.selectAll(("input[name='bidding_no']")).on('change', function(){ | |
| // updateChart(this.value) | |
| // }) | |
| function updateChart() { | |
| var res_nested = d3.nest() | |
| .key(d=>d.count) | |
| .entries(res) | |
| glines.select('.line') //select line path within line-group (which represents a vehicle category), then bind new data | |
| .data(res_nested) | |
| .transition().duration(750) | |
| .attr('d', function(d) { | |
| return line(d.values) | |
| }) | |
| mouseG.selectAll('.mouse-per-line') | |
| .data(res_nested) | |
| mouseG.on('mousemove', function () { | |
| var mouse = d3.mouse(this) | |
| updateTooltipContent(mouse, res_nested) | |
| }) | |
| } | |
| function renderChart() { | |
| var res_nested = d3.nest() // necessary to nest data so that keys represent each vehicle category | |
| .key(d=>d.count) | |
| .entries(res) | |
| // APPEND MULTIPLE LINES // | |
| var lines = svg.append('g') | |
| .attr('class', 'lines') | |
| glines = lines.selectAll('.line-group') | |
| .data(res_nested).enter() | |
| .append('g') | |
| .attr('class', 'line-group') | |
| glines | |
| .append('path') | |
| .attr('class', 'line') | |
| .attr('d', d => line(d.values)) | |
| .style('stroke', (d, i) => color(i)) | |
| .style('fill', 'none') | |
| .style('opacity', lineOpacity) | |
| .style('stroke-width', lineStroke) | |
| // APPEND CIRCLE MARKERS // | |
| //var gcircle = lines.selectAll("circle-group") | |
| //.data(res_nested).enter() | |
| //.append("g") | |
| //.attr('class', 'circle-group') | |
| //gcircle.selectAll("circle") | |
| //.data(d => d.values).enter() | |
| //.append("g") | |
| //.attr("class", "circle") | |
| //.append("circle") | |
| //.attr("cx", d => xScale(d.date)) | |
| //.attr("cy", d => yScale(d.premium)) | |
| //.attr("r", 2) | |
| // CREATE HOVER TOOLTIP WITH VERTICAL LINE // | |
| tooltip = d3.select("#chart").append("div") | |
| .attr('id', 'tooltip') | |
| .style('position', 'absolute') | |
| .style("background-color", "#D3D3D3") | |
| .style('padding', 6) | |
| .style('display', 'none') | |
| mouseG = svg.append("g") | |
| .attr("class", "mouse-over-effects"); | |
| mouseG.append("path") // create vertical line to follow mouse | |
| .attr("class", "mouse-line") | |
| .style("stroke", "#A9A9A9") | |
| .style("stroke-width", lineStroke) | |
| .style("opacity", "0"); | |
| var lines = document.getElementsByClassName('line'); | |
| var mousePerLine = mouseG.selectAll('.mouse-per-line') | |
| .data(res_nested) | |
| .enter() | |
| .append("g") | |
| .attr("class", "mouse-per-line"); | |
| mousePerLine.append("circle") | |
| .attr("r", 4) | |
| .style("stroke", function (d) { | |
| return color(d.key) | |
| }) | |
| .style("fill", "none") | |
| .style("stroke-width", lineStroke) | |
| .style("opacity", "0"); | |
| mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas | |
| .attr('width', width) | |
| .attr('height', height) | |
| .attr('fill', 'none') | |
| .attr('pointer-events', 'all') | |
| .on('mouseout', function () { // on mouse out hide line, circles and text | |
| d3.select(".mouse-line") | |
| .style("opacity", "0"); | |
| d3.selectAll(".mouse-per-line circle") | |
| .style("opacity", "0"); | |
| d3.selectAll(".mouse-per-line text") | |
| .style("opacity", "0"); | |
| d3.selectAll("#tooltip") | |
| .style('display', 'none') | |
| }) | |
| .on('mouseover', function () { // on mouse in show line, circles and text | |
| d3.select(".mouse-line") | |
| .style("opacity", "1"); | |
| d3.selectAll(".mouse-per-line circle") | |
| .style("opacity", "1"); | |
| d3.selectAll("#tooltip") | |
| .style('display', 'block') | |
| }) | |
| .on('mousemove', function () { // update tooltip content, line, circles and text when mouse moves | |
| var mouse = d3.mouse(this) | |
| d3.selectAll(".mouse-per-line") | |
| .attr("transform", function (d, i) { | |
| var xDate = xScale.invert(mouse[0]) // use 'invert' to get date corresponding to distance from mouse position relative to svg | |
| var bisect = d3.bisector(function (d) { return d.date; }).left // retrieve row index of date on parsed csv | |
| var idx = bisect(d.values, xDate); | |
| d3.select(".mouse-line") | |
| .attr("d", function () { | |
| var data = "M" + xScale(d.values[idx].date) + "," + (height); | |
| data += " " + xScale(d.values[idx].date) + "," + 0; | |
| return data; | |
| }); | |
| return "translate(" + xScale(d.values[idx].date) + "," + yScale(d.values[idx].count) + ")"; | |
| }); | |
| updateTooltipContent(mouse, res_nested) | |
| }) | |
| } | |
| function updateTooltipContent(mouse, res_nested) { | |
| sortingObj = [] | |
| res_nested.map(d => { | |
| var xDate = xScale.invert(mouse[0]) | |
| var bisect = d3.bisector(function (d) { return d.date; }).left | |
| var idx = bisect(d.values, xDate) | |
| sortingObj.push({key: d.values[idx].count, count: d.values[idx].count, year: d.values[idx].date.getFullYear(), month: monthNames[d.values[idx].date.getMonth()]}) | |
| }) | |
| sortingObj.sort(function(x, y){ | |
| return d3.descending(x.count, y.count); | |
| }) | |
| var sortingArr = sortingObj.map(d=> d.key) | |
| var res_nested1 = res_nested.slice().sort(function(a, b){ | |
| return sortingArr.indexOf(a.key) - sortingArr.indexOf(b.key) // rank vehicle category based on price of count | |
| }) | |
| tooltip.html(sortingObj[0].month + "-" + sortingObj[0].year) | |
| .style('display', 'block') | |
| .style('left', d3.event.pageX + 20) | |
| .style('top', d3.event.pageY - 20) | |
| .style('font-size', 11.5) | |
| .selectAll() | |
| .data(res_nested1).enter() // for each vehicle category, list out name and price of count | |
| .append('div') | |
| .style('color', d => { | |
| return color(d.key) | |
| }) | |
| .style('font-size', 10) | |
| .html(d => { | |
| var xDate = xScale.invert(mouse[0]) | |
| var bisect = d3.bisector(function (d) { return d.date; }).left | |
| var idx = bisect(d.values, xDate) | |
| return d.key.substring(0, 3) + " " + d.key.slice(-1) + d.values[idx].count | |
| }) | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment