Created
June 1, 2020 09:58
-
-
Save bkamapantula/5fa32f63bb9a2f6a7274ce5a918d8df2 to your computer and use it in GitHub Desktop.
Created with Gramex Charts. Visit at https://gramener.com/gramexcharts
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
[ | |
{ | |
"a": 1, | |
"b": 2 | |
} | |
] |
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
data = { | |
// JSON or a CSV endpoint | |
url: "" | |
} | |
function draw(data, el, opts) { | |
var myData = "date New York San Francisco Austin\n\ | |
20111001 63.4 62.7 72.2\n\ | |
20111002 58.0 59.9 67.7\n\ | |
20111003 53.3 59.1 69.4\n\ | |
20111004 55.7 58.8 68.0\n\ | |
20111005 64.2 58.7 72.4\n\ | |
20111006 58.8 57.0 77.0\n\ | |
20111007 57.9 56.7 82.3\n\ | |
20111008 61.8 56.8 78.9\n\ | |
20111009 69.3 56.7 68.8\n\ | |
20111010 71.2 60.1 68.7\n\ | |
20111011 68.7 61.1 70.3\n\ | |
20111012 61.8 61.5 75.3\n\ | |
20111013 63.0 64.3 76.6\n\ | |
20111014 66.9 67.1 66.6\n\ | |
20111015 61.7 64.6 68.0\n\ | |
20111016 61.8 61.6 70.6\n\ | |
20111017 62.8 61.1 71.1\n\ | |
20111018 60.8 59.2 70.0\n\ | |
20111019 62.1 58.9 61.6\n\ | |
20111020 65.1 57.2 57.4\n\ | |
20111021 55.6 56.4 64.3\n\ | |
20111022 54.4 60.7 72.4\n"; | |
var margin = { | |
top: 20, | |
right: 80, | |
bottom: 30, | |
left: 50 | |
} | |
let width = 600 - margin.left - margin.right | |
let height = 450 - margin.top - margin.bottom; | |
var parseDate = d3.timeParse("%Y%m%d"); | |
var x = d3.scaleTime() | |
.range([0, width]); | |
var y = d3.scaleLinear() | |
.range([height, 0]); | |
var color = d3.scaleOrdinal().range(d3.schemeCategory10); | |
var xAxis = d3.axisBottom(x) | |
.tickSize(0) | |
// .ticks(d3.timeMonth) | |
// .tickSize(0, 0) | |
// .tickFormat(d3.timeFormat("%B")) | |
// .tickSizeInner(0) | |
.tickPadding(10); | |
var line = d3.line() | |
.curve(d3.curveBasis) | |
.x(function (d) { | |
return x(d.date); | |
}) | |
.y(function (d) { | |
return y(d.temperature); | |
}); | |
var svg = d3.select(el) | |
.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 + ")"); | |
var data = d3.tsvParse(myData); | |
color.domain(d3.keys(data[0]).filter(function (key) { | |
return key !== "date"; | |
})); | |
data.forEach(function (d) { | |
d.date = parseDate(d.date); | |
}); | |
var cities = color.domain().map(function (name) { | |
return { | |
name: name, | |
values: data.map(function (d) { | |
return { | |
date: d.date, | |
temperature: +d[name] | |
}; | |
}) | |
}; | |
}); | |
x.domain(d3.extent(data, function (d) { | |
return d.date; | |
})); | |
y.domain([ | |
0, | |
d3.max(cities, function (c) { | |
return d3.max(c.values, function (v) { | |
return v.temperature; | |
}); | |
}) | |
]); | |
let ticksAmount = 5; | |
let minValue = d3.min(cities, function (c) { | |
return d3.min(c.values, function (v) { | |
return v.temperature; | |
}); | |
}) | |
let maxValue = d3.max(cities, function (c) { | |
return d3.max(c.values, function (v) { | |
return v.temperature; | |
}); | |
}) | |
let tickStep = (maxValue - 0) / (ticksAmount); | |
let step = Math.ceil(tickStep / 5) * 5; | |
var yAxis = d3.axisLeft(y) | |
.ticks(ticksAmount) | |
.tickValues(d3.range(0, maxValue + step, step)) | |
.tickFormat(d3.format('d')) | |
// .innerTickSize(-width) | |
// .outerTickSize(0) | |
.tickPadding(5); | |
// var legend = svg.selectAll('g') | |
// .data(cities) | |
// .enter() | |
// .append('g') | |
// .attr('class', 'legend'); | |
// legend.append('rect') | |
// .attr('x', width - 20) | |
// .attr('y', function (d, i) { | |
// return i * 20; | |
// }) | |
// .attr('width', 10) | |
// .attr('height', 10) | |
// .style('fill', function (d) { | |
// return color(d.name); | |
// }); | |
// legend.append('text') | |
// .attr('x', width - 8) | |
// .attr('y', function (d, i) { | |
// return (i * 20) + 9; | |
// }) | |
// .text(function (d) { | |
// return d.name; | |
// }); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis) | |
.call(g => g.select(".domain").remove()) | |
// svg.append("g") | |
// .attr("class", "y axis") | |
// .call(yAxis) | |
// .call(g => g.select(".domain").remove()) | |
// .append("text") | |
// .attr("transform", "rotate(-90)") | |
// .attr("y", 6) | |
// .attr("dy", ".71em") | |
// .style("text-anchor", "end") | |
// .text("Temperature (ºF)"); | |
//y axis grid lines | |
svg.append("g") | |
.attr("class", "grid") | |
.call( | |
d3.axisLeft(y) | |
.ticks(ticksAmount) | |
.tickValues(d3.range(0, maxValue + step, step)) | |
.tickFormat(d3.format('d')) | |
.tickPadding(5) | |
.tickSize(-width) | |
) | |
var city = svg.selectAll(".city") | |
.data(cities) | |
.enter().append("g") | |
.attr("class", "city") | |
.style("mix-blend-mode", "multiply") | |
.attr('fill', 'none') | |
.style("stroke", function (d) { | |
return color(d.name); | |
}); | |
let each_line = city.append("path") | |
.attr("class", "line") | |
.attr("d", function (d) { | |
return line(d.values); | |
}) | |
// city.append("text") | |
// .datum(function (d) { | |
// return { | |
// name: d.name, | |
// value: d.values[d.values.length - 1] | |
// }; | |
// }) | |
// .attr("transform", function (d) { | |
// return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; | |
// }) | |
// .attr("x", 3) | |
// .attr("dy", ".35em") | |
// .text(function (d) { | |
// return d.name; | |
// }); | |
var mouseG = svg.append("g") | |
.attr("class", "mouse-over-effects"); | |
mouseG.append("path") // this is the black vertical line to follow mouse | |
.attr("class", "mouse-line") | |
.style("stroke", "#343434") | |
.style("stroke-width", "1px") | |
.style("stroke-dasharray", "4px 2px") | |
.style("opacity", "0"); | |
var lines = document.getElementsByClassName('line'); | |
var mousePerLine = mouseG.selectAll('.mouse-per-line') | |
.data(cities) | |
.enter() | |
.append("g") | |
.attr("class", "mouse-per-line"); | |
mousePerLine.append("circle") | |
.attr("r", 6) | |
.style("fill", function (d) { | |
return color(d.name); | |
}) | |
// .style("fill", "none") | |
.style("stroke-width", "1px") | |
.style("opacity", "0"); | |
mousePerLine.append("text") | |
.attr("transform", "translate(10,3)"); | |
mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas | |
.attr('width', width) // can't catch mouse events on a g element | |
.attr('height', height) | |
.attr('fill', 'none') | |
.attr('pointer-events', 'all') | |
.on('mouseout', function () { // on mouse out hide line, circles and text | |
d3.selectAll(".mouse-line") | |
.style("opacity", "0"); | |
d3.selectAll(".mouse-per-line circle") | |
.style("opacity", "0"); | |
d3.selectAll(".mouse-per-line text") | |
.style("opacity", "0"); | |
}) | |
.on('mouseover', function () { // on mouse in show line, circles and text | |
d3.selectAll(".mouse-line") | |
.style("opacity", "1"); | |
d3.selectAll(".mouse-per-line circle") | |
.style("opacity", "1"); | |
d3.selectAll(".mouse-per-line text") | |
.style("opacity", "1"); | |
}) | |
.on('mousemove touchmove', function () { // mouse moving over canvas | |
var mouse = d3.mouse(this); | |
d3.selectAll(".mouse-line") | |
.attr("d", function () { | |
var d = "M" + mouse[0] + "," + height; | |
d += " " + mouse[0] + "," + 0; | |
return d; | |
}); | |
const xm = x.invert(mouse[0]); | |
const ym = y.invert(mouse[1]); | |
const i1 = d3.bisectLeft(_.map(data, 'date'), xm, 1); | |
const i0 = i1 - 1; | |
const i = xm - data[i0].date > data[i1].date - xm ? i1 : i0; | |
// const s = d3.min(cities, d => Math.abs(d.values[i]['temperature'] - ym)); | |
let s | |
let least_val = 10000 | |
_.each(data[i], function (val, key) { | |
if (Math.abs(val - ym) < least_val) { | |
least_val = Math.abs(val - ym) | |
s = key | |
} | |
}) | |
d3.selectAll(".line").style("stroke", d => { return d.name === s ? "red" : "#ddd"; }).filter(d => d.name === s).raise(); | |
d3.selectAll(".mouse-per-line circle").style("fill", d => { return d.name === s ? "red" : "#ddd"; }).filter(d => d.name === s).raise(); | |
d3.selectAll(".mouse-per-line") | |
.attr("transform", function (d, i) { | |
// console.log(width / mouse[0]) | |
var xDate = x.invert(mouse[0]), | |
bisect = d3.bisector(function (d) { return d.date; }).right; | |
idx = bisect(d.values, xDate); | |
var beginning = 0, | |
end = lines[i].getTotalLength(), | |
target = null; | |
while (true) { | |
target = Math.floor((beginning + end) / 2); | |
pos = lines[i].getPointAtLength(target); | |
if ((target === end || target === beginning) && pos.x !== mouse[0]) { | |
break; | |
} | |
if (pos.x > mouse[0]) end = target; | |
else if (pos.x < mouse[0]) beginning = target; | |
else break; //position found | |
} | |
// d3.select(this).select('text') | |
// .text(y.invert(pos.y).toFixed(2)); | |
return "translate(" + mouse[0] + "," + pos.y + ")"; | |
}); | |
}); | |
} | |
scripts = [ | |
"https://gramener.com/ui/d3v5/dist/d3.min.js" | |
// "https://unpkg.com/[email protected]/build/vega.min.js", | |
// "https://unpkg.com/[email protected]/build/vega-tooltip.min.js" | |
] | |
options = { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment