Programmatic zoom in d3 js timeline chart - javascript
I implement chart to display some financial data for time period, x-axis contains javascript dates but y-axis numbers. The chart must provide zoom and pan functionality. I set x-axis for max available period, like:
var data = [
[{'x':'2017-01-02','y':0},{'x':'2017-02-03','y':5.0},{'x':'2017-03-04','y':10},{'x':'2017-04-05','y':0},{'x':'2017-05-06','y':6},{'x':'2017-06-07','y':11},{'x':'2017-07-08','y':9},{'x':'2017-08-09','y':4},{'x':'2017-09-10','y':11},{'x':'2017-10-11','y':2}],
[{'x':'2017-01-02','y':1},{'x':'2017-02-03','y':6.0},{'x':'2017-03-04','y':11},{'x':'2017-04-05','y':1},{'x':'2017-05-06','y':7},{'x':'2017-06-07','y':12},{'x':'2017-07-08','y':8},{'x':'2017-08-09','y':3},{'x':'2017-09-10','y':13},{'x':'2017-10-11','y':3}]
];
var json = JSON.parse('[[{"x":"2017-08-22T20:27:53.181+0200","y":4.0},{"x":"2017-09-01T20:27:53.181+0200","y":9.0},{"x":"2017-09-11T20:27:53.181+0200","y":15.0},{"x":"2017-09-21T20:27:53.181+0200","y":6.0},{"x":"2017-10-01T20:27:53.181+02:00","y":13.0},{"x":"2017-10-11T20:27:53.181","y":10.0},{"x":"2017-10-21T20:27:53.181","y":1.0},{"x":"2017-10-31T20:27:53.181","y":13.0},{"x":"2017-11-10T20:27:53.181","y":2.0},{"x":"2017-11-20T20:27:53.181","y":13.0},{"x":"2017-11-30T20:27:53.181","y":2.0},{"x":"2017-12-10T20:27:53.181","y":14.0},{"x":"2017-12-20T20:27:53.181","y":15.0},{"x":"2017-12-30T20:27:53.181","y":11.0},{"x":"2018-01-09T20:27:53.181","y":5.0},{"x":"2018-01-19T20:27:53.181","y":11.0},{"x":"2018-01-29T20:27:53.181","y":5.0},{"x":"2018-02-08T20:27:53.181","y":2.0},{"x":"2018-02-18T20:27:53.181","y":4.0},{"x":"2018-02-28T20:27:53.181","y":3.0},{"x":"2018-03-10T20:27:53.181","y":2.0},{"x":"2018-03-20T20:27:53.181","y":10.0},{"x":"2018-03-30T20:27:53.181","y":15.0},{"x":"2018-04-09T20:27:53.181","y":3.0},{"x":"2018-04-19T20:27:53.181","y":2.0},{"x":"2018-04-29T20:27:53.181","y":11.0},{"x":"2018-05-09T20:27:53.181","y":7.0},{"x":"2018-05-19T20:27:53.181","y":13.0},{"x":"2018-05-29T20:27:53.181","y":8.0},{"x":"2018-06-08T20:27:53.181","y":1.0},{"x":"2018-06-18T20:27:53.181","y":4.0},{"x":"2018-06-28T20:27:53.181","y":10.0},{"x":"2018-07-08T20:27:53.181","y":13.0},{"x":"2018-07-18T20:27:53.181","y":13.0},{"x":"2018-07-28T20:27:53.181","y":12.0},{"x":"2018-08-07T20:27:53.181","y":13.0},{"x":"2018-08-17T20:27:53.181","y":13.0},{"x":"2018-08-27T20:27:53.181","y":1.0},{"x":"2018-09-06T20:27:53.181","y":8.0},{"x":"2018-09-16T20:27:53.181","y":14.0},{"x":"2018-09-26T20:27:53.181","y":7.0},{"x":"2018-10-06T20:27:53.181","y":9.0},{"x":"2018-10-16T20:27:53.181","y":15.0},{"x":"2018-10-26T20:27:53.181","y":15.0},{"x":"2018-11-05T20:27:53.181","y":11.0},{"x":"2018-11-15T20:27:53.181","y":13.0},{"x":"2018-11-25T20:27:53.181","y":9.0},{"x":"2018-12-05T20:27:53.181","y":5.0},{"x":"2018-12-15T20:27:53.181","y":5.0},{"x":"2018-12-25T20:27:53.181","y":6.0}],[{"x":"2017-08-22T20:27:53.181","y":8.0},{"x":"2017-09-01T20:27:53.181","y":13.0},{"x":"2017-09-11T20:27:53.181","y":15.0},{"x":"2017-09-21T20:27:53.181","y":11.0},{"x":"2017-10-01T20:27:53.181","y":12.0},{"x":"2017-10-11T20:27:53.181","y":10.0},{"x":"2017-10-21T20:27:53.181","y":2.0},{"x":"2017-10-31T20:27:53.181","y":10.0},{"x":"2017-11-10T20:27:53.181","y":12.0},{"x":"2017-11-20T20:27:53.181","y":6.0},{"x":"2017-11-30T20:27:53.181","y":10.0},{"x":"2017-12-10T20:27:53.181","y":2.0},{"x":"2017-12-20T20:27:53.181","y":3.0},{"x":"2017-12-30T20:27:53.181","y":10.0},{"x":"2018-01-09T20:27:53.181","y":12.0},{"x":"2018-01-19T20:27:53.181","y":12.0},{"x":"2018-01-29T20:27:53.181","y":6.0},{"x":"2018-02-08T20:27:53.181","y":2.0},{"x":"2018-02-18T20:27:53.181","y":7.0},{"x":"2018-02-28T20:27:53.181","y":1.0},{"x":"2018-03-10T20:27:53.181","y":10.0},{"x":"2018-03-20T20:27:53.181","y":4.0},{"x":"2018-03-30T20:27:53.181","y":14.0},{"x":"2018-04-09T20:27:53.181","y":15.0},{"x":"2018-04-19T20:27:53.181","y":5.0},{"x":"2018-04-29T20:27:53.181","y":14.0},{"x":"2018-05-09T20:27:53.181","y":4.0},{"x":"2018-05-19T20:27:53.181","y":3.0},{"x":"2018-05-29T20:27:53.181","y":7.0},{"x":"2018-06-08T20:27:53.181","y":1.0},{"x":"2018-06-18T20:27:53.181","y":14.0},{"x":"2018-06-28T20:27:53.181","y":2.0},{"x":"2018-07-08T20:27:53.181","y":14.0},{"x":"2018-07-18T20:27:53.181","y":11.0},{"x":"2018-07-28T20:27:53.181","y":12.0},{"x":"2018-08-07T20:27:53.181","y":3.0},{"x":"2018-08-17T20:27:53.181","y":13.0},{"x":"2018-08-27T20:27:53.181","y":6.0},{"x":"2018-09-06T20:27:53.181","y":3.0},{"x":"2018-09-16T20:27:53.181","y":11.0},{"x":"2018-09-26T20:27:53.181","y":3.0},{"x":"2018-10-06T20:27:53.181","y":15.0},{"x":"2018-10-16T20:27:53.181","y":13.0},{"x":"2018-10-26T20:27:53.181","y":9.0},{"x":"2018-11-05T20:27:53.181","y":2.0},{"x":"2018-11-15T20:27:53.181","y":14.0},{"x":"2018-11-25T20:27:53.181","y":15.0},{"x":"2018-12-05T20:27:53.181","y":4.0},{"x":"2018-12-15T20:27:53.181","y":2.0},{"x":"2018-12-25T20:27:53.181","y":5.0}]]');
var a = jsonToArray(json);
var parseDate = d3.time.format("%Y-%m-%d").parse;
data.forEach(function(d) {
d.forEach(function(d2) {
d2.x = parseDate(d2.x);
});
});
var colors = [
'steelblue',
'green'
]
var pWidth = document.getElementById('chart1').offsetWidth;
var margin = {top: 20, right: 30, bottom: 30, left: 50},
width = pWidth - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.time.scale()
.domain([new Date('2016-01-01T00:00:00.000+02:00'), new Date('2018-10-01T00:00:00.000+02:00')])
.range([0, width]);
var y = d3.scale.linear()
.domain([-1, 16])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(-height)
.tickPadding(10)
.tickSubdivide(true)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.tickPadding(10)
.tickSize(-width)
.tickSubdivide(true)
.orient("left");
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, Infinity])
.on("zoom", zoomed);
var svg = d3.select("#chart1").append("svg")
.call(zoom)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", (-margin.left) + 10)
.attr("x", -height/2)
.text('Axis Label');
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var line = d3.svg.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); });
svg.selectAll('.line')
.data(data)
.enter()
.append("path")
.attr("class", "line")
.attr("clip-path", "url(#clip)")
.attr('stroke', function(d,i){
return colors[i%colors.length];
})
.attr("d", line);
var points = svg.selectAll('.dots')
.data(data)
.enter()
.append("g")
.attr("class", "dots")
.attr("clip-path", "url(#clip)");
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.selectAll('path.line').attr('d', line);
var arr = [[]];
for ( var i = 0; i < 2; i++ ) {
arr[i] = new Array();
for ( var j = 0; j < 50; j++ ) {
arr[i][j] = {'x': addDays(x.domain()[0], 10*j), 'y':Math.floor((Math.random() * 15) + 1)};
}
}
svg.selectAll('.line')
.data(arr)
.enter()
.append("path")
.attr("class", "line")
.attr("clip-path", "url(#clip)")
.attr('stroke', function(d,i){
return colors[i%colors.length];
})
.attr("d", line);
}
function addDays(startDate,numberOfDays)
{
var returnDate = new Date(
startDate.getFullYear(),
startDate.getMonth(),
startDate.getDate()+numberOfDays,
startDate.getHours(),
startDate.getMinutes(),
startDate.getSeconds());
return returnDate;
}
function jsonToArray(json) {
var result = new Array();
for(var i in json) {
var arr = new Array();
for(var j in json[i]) {
arr.push({'x':new Date(json[i][j].x), 'y':json[i][j].y});
}
result.push(arr);
}
return result;
}
}
but default time period when chart is loaded must be current date - one month back. And when I draw path for this period, chart displays it on the x-axis with max period. How can I programmatically zoom in to the default period (current date - one month back) so afterwards I can make both zoom in and zoom out?
Or I can set default period (current date - one month back) when chart is loaded but how can then make zoom out scrolling mouse wheel to wider period?
Related
How to dynamically create append svg line using d3js?
I am attempting to draw markers to a zoomable / pannable graph. My current version is available at https://jsfiddle.net/n2enn80o/ raw = {"l2":[-58,-44,-31,-21,-17,-16,-16,-15,-15,-14,-13,-12,-11,-10,-9,-7,-5,-3,-1,2,4,8,11,14,17,20,24,28,32,37,41,46,51,56,60,64,69,73,77,82,84,87,87,87,87,85,82,77,73,66,60,53,46,39,33,26,20,13,8,4,-1,-6,-10,-14,-16,-19,-22,-25,-26,-27,-28,-29,-30,-31,-31,-32,-32,-32,-32,-31,-31,-31,-31,-31,-30,-31,-30,-30,-30,-30,-29,-28,-27,-27,-27,-27,-27,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-25,-25,-25,-25,-25,-25,-24,-24,-24,-25,-25,-24,-23,-23,-23,-23,-23,-23,-23,-23,-23,-22,-22,-22,-22,-22,-22,-21,-21,-21,-21,-20,-20,-20,-20,-19,-18,-16,-13,-10,-6,-2,1,4,7,9,12,14,16,16,16,15,13,9,6,2,-1,-4,-7,-10,-11,-12,-14,-15,-16,-17,-18,-19,-19,-20,-20,-20,-20,-20,-20,-20,-24,-32,-45,-54,-50,-25,25,97,182,252,270,220,126,35,-25,-53,-65,-71,-70,-60,-46,-32,-22,-18,-16,-16,-15,-14,-13,-13,-12,-11,-10,-8,-7,-5,-3,-1,1,4,7,10,14,17,21,24,28,32,36,40,45,50,55,59,64,69,72,77,81,84,87,88,88,87,84,82,77,73,67,61,54,47,39,33,26,20,14,9,4,-1,-6,-9,-13,-16,-19,-22,-24,-26,-28,-29,-29,-30,-30,-31,-32,-32,-32,-31,-31,-32,-31,-31,-31,-31,-30,-30,-29,-29,-29,-29,-28,-28,-27,-27,-28,-28,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-25,-25,-25,-26,-26,-25,-24,-24,-24,-24,-24,-24,-24,-23,-23,-23,-23,-23,-23,-23,-24,-22,-22,-22,-22,-22,-22,-22,-21,-22,-20,-20,-20,-20,-20,-19,-18,-16,-13,-10,-8,-3,0,4,7,9,11,13,15,16,16,16,14,11,6,2,-2,-4,-7,-9,-11,-12,-14,-16,-17,-18,-19,-19,-19,-19,-20,-20,-20,-20,-20,-20,-23,-30,-43,-54,-53,-30,17,88,172,245,271,230,138,45,-19,-50,-63,-70,-70,-61,-48,-34,-23,-18,-17,-16,-16,-15,-14,-13,-13,-11,-10,-8,-7,-5,-3,-1,1,4,7,9,13,16,20,23,27,31,36,40,44,49,54,59,63,68,73,76,81,83,86,88,87,87,85,82,78,73,68,62,55,48,40,33,27,20,14,9,4,0,-5,-9,-13,-16,-19,-22,-24,-26,-28,-29,-29,-29,-30,-31,-31,-32,-31,-32,-32,-32,-32], "startts":1357714800000,"marker1":[50,100]}; var data =[]; for(var i=0;i<raw.l2.length;i++) { var marker = Number.NaN; for(var j=0;j<raw.marker1.length;j++) { if(i==raw.marker1[j]) { marker=i; break; } } var obj = {"date":raw.startts+(1/244.140625*1000)*i, "value":raw.l2[i]/75,"marker":marker} data.push(obj) } margin = { top: 20, right: 20, bottom: 20, left: 45 }; width = 1800 - margin.left - margin.right; height = 600 - margin.top - margin.bottom; var x = d3.time.scale() .domain(d3.extent(data, function (d) { return d.date; })) .range([0, width]); var y = d3.scale.linear() .domain(d3.extent(data, function (d) { return d.value; })) .range([height, 0]); var line = d3.svg.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.value); }); var zoom = d3.behavior.zoom() .x(x) .y(y) .on("zoom", zoomed); svg = d3.select('#chart') .append("svg:svg") .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append("svg:g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .call(zoom); svg.append("svg:rect") .attr("width", width) .attr("height", height) .attr("class", "plot"); var make_x_axis = function () { return d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); }; var make_y_axis = function () { return d3.svg.axis() .scale(y) .orient("left") .ticks(5); }; var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); svg.append("svg:g") .attr("class", "x axis") .attr("transform", "translate(0, " + height + ")") .call(xAxis); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(5); svg.append("g") .attr("class", "y axis") .call(yAxis); var marker = svg.append("line") .attr("x1",100) .attr("y1", 0) .attr("x2",100) .attr("y2", height) .attr("class", "marker") svg.append("text") .attr("x", 100) .attr("y", 0) .attr("dy", ".35em") .attr("dx", 5) .attr("class", "marker-text") .text(function(d) { return "test"; }); svg.append("g") .attr("class", "x grid") .attr("transform", "translate(0," + height + ")") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.append("g") .attr("class", "y grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); var clip = svg.append("svg:clipPath") .attr("id", "clip") .append("svg:rect") .attr("x", 0) .attr("y", 0) .attr("width", width) .attr("height", height); var chartBody = svg.append("g") .attr("clip-path", "url(#clip)"); chartBody.append("svg:path") .datum(data) .attr("class", "line") .attr("d", line); function zoomed() { console.log(d3.event.translate); console.log(d3.event.scale); svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); svg.select(".x.grid") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.select(".y.grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); svg.select(".line") .attr("class", "line") .attr("d", line); } I have been able to add static lines to my d3 plot in the example, what i am trying to achieve is to make the marker line along with the text "test" to be moving along with the graph. What I have tried is to use a d3.svg.line() with a line generator, but it ends up not getting the reference to d. Another version I have attempted is to create a function as below but that does not get the reference of d, and draws the line at zero. Here i attempted to draw the line if and only if the marker tag was present in the data. By default when populating the data i have ensured that it has Number.NaN when i dont need to draw the marker. var marker = svg.append("line") .attr("x1",function(d) { if(d.marker!=Number.NaN) return x(d.date); }) .attr("y1", 0) .attr("x2",function(d) { if(d.marker!=Number.NaN) return x(d.date); }) .attr("y2", height) .attr("class", "marker")
I have made a couple of small changes, as per my reply to your question. Inside your zoom handler zoomed() you need to change the x1 & x2 attributes of marker, and the x attr of markerText. This function fires on each zoom event to move stuff around or redraw stuff according to your zoom, and your axes. raw = {"l2":[-58,-44,-31,-21,-17,-16,-16,-15,-15,-14,-13,-12,-11,-10,-9,-7,-5,-3,-1,2,4,8,11,14,17,20,24,28,32,37,41,46,51,56,60,64,69,73,77,82,84,87,87,87,87,85,82,77,73,66,60,53,46,39,33,26,20,13,8,4,-1,-6,-10,-14,-16,-19,-22,-25,-26,-27,-28,-29,-30,-31,-31,-32,-32,-32,-32,-31,-31,-31,-31,-31,-30,-31,-30,-30,-30,-30,-29,-28,-27,-27,-27,-27,-27,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-25,-25,-25,-25,-25,-25,-24,-24,-24,-25,-25,-24,-23,-23,-23,-23,-23,-23,-23,-23,-23,-22,-22,-22,-22,-22,-22,-21,-21,-21,-21,-20,-20,-20,-20,-19,-18,-16,-13,-10,-6,-2,1,4,7,9,12,14,16,16,16,15,13,9,6,2,-1,-4,-7,-10,-11,-12,-14,-15,-16,-17,-18,-19,-19,-20,-20,-20,-20,-20,-20,-20,-24,-32,-45,-54,-50,-25,25,97,182,252,270,220,126,35,-25,-53,-65,-71,-70,-60,-46,-32,-22,-18,-16,-16,-15,-14,-13,-13,-12,-11,-10,-8,-7,-5,-3,-1,1,4,7,10,14,17,21,24,28,32,36,40,45,50,55,59,64,69,72,77,81,84,87,88,88,87,84,82,77,73,67,61,54,47,39,33,26,20,14,9,4,-1,-6,-9,-13,-16,-19,-22,-24,-26,-28,-29,-29,-30,-30,-31,-32,-32,-32,-31,-31,-32,-31,-31,-31,-31,-30,-30,-29,-29,-29,-29,-28,-28,-27,-27,-28,-28,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-25,-25,-25,-26,-26,-25,-24,-24,-24,-24,-24,-24,-24,-23,-23,-23,-23,-23,-23,-23,-24,-22,-22,-22,-22,-22,-22,-22,-21,-22,-20,-20,-20,-20,-20,-19,-18,-16,-13,-10,-8,-3,0,4,7,9,11,13,15,16,16,16,14,11,6,2,-2,-4,-7,-9,-11,-12,-14,-16,-17,-18,-19,-19,-19,-19,-20,-20,-20,-20,-20,-20,-23,-30,-43,-54,-53,-30,17,88,172,245,271,230,138,45,-19,-50,-63,-70,-70,-61,-48,-34,-23,-18,-17,-16,-16,-15,-14,-13,-13,-11,-10,-8,-7,-5,-3,-1,1,4,7,9,13,16,20,23,27,31,36,40,44,49,54,59,63,68,73,76,81,83,86,88,87,87,85,82,78,73,68,62,55,48,40,33,27,20,14,9,4,0,-5,-9,-13,-16,-19,-22,-24,-26,-28,-29,-29,-29,-30,-31,-31,-32,-31,-32,-32,-32,-32], "startts":1357714800000,"marker1":[50,100]}; var data =[]; for(var i=0;i<raw.l2.length;i++) { var marker = Number.NaN; for(var j=0;j<raw.marker1.length;j++) { if(i==raw.marker1[j]) { marker=i; break; } } var obj = {"date":raw.startts+(1/244.140625*1000)*i, "value":raw.l2[i]/75,"marker":marker} data.push(obj) } margin = { top: 20, right: 20, bottom: 20, left: 45 }; width = 1800 - margin.left - margin.right; height = 600 - margin.top - margin.bottom; var x = d3.time.scale() .domain(d3.extent(data, function (d) { return d.date; })) .range([0, width]); var y = d3.scale.linear() .domain(d3.extent(data, function (d) { return d.value; })) .range([height, 0]); var line = d3.svg.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.value); }); var zoom = d3.behavior.zoom() .x(x) .y(y) .on("zoom", zoomed); svg = d3.select('#chart') .append("svg:svg") .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append("svg:g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .call(zoom); svg.append("svg:rect") .attr("width", width) .attr("height", height) .attr("class", "plot"); var make_x_axis = function () { return d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); }; var make_y_axis = function () { return d3.svg.axis() .scale(y) .orient("left") .ticks(5); }; var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(5); svg.append("svg:g") .attr("class", "x axis") .attr("transform", "translate(0, " + height + ")") .call(xAxis); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(5); svg.append("g") .attr("class", "y axis") .call(yAxis); svg.append("g") .attr("class", "x grid") .attr("transform", "translate(0," + height + ")") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.append("g") .attr("class", "y grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); // Moved this after you append your grids var marker = svg.append("line") .attr("x1",100) .attr("y1", 0) .attr("x2",100) .attr("y2", height) .attr("class", "marker") // Moved this after you append your grids & saved it to a var for later use var markerText = svg.append("text") .attr("x", 100) .attr("y", 0) .attr("dy", ".35em") .attr("dx", 5) .attr("class", "marker-text") .text(function(d) { return "test"; }); var clip = svg.append("svg:clipPath") .attr("id", "clip") .append("svg:rect") .attr("x", 0) .attr("y", 0) .attr("width", width) .attr("height", height); var chartBody = svg.append("g") .attr("clip-path", "url(#clip)"); chartBody.append("svg:path") .datum(data) .attr("class", "line") .attr("d", line); function zoomed() { console.log(d3.event.translate); console.log(d3.event.scale); svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); svg.select(".x.grid") .call(make_x_axis() .tickSize(-height, 0, 0) .tickFormat("")); svg.select(".y.grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("")); svg.select(".line") .attr("class", "line") .attr("d", line); // Position at the start time according to your x scale. x(val) // It's not clear from your question where you want to position it var markerPos = x(raw.startts); // Move marker to position marker.attr("x1", markerPos ).attr("x2", markerPos ); // Move marker text to position markerText.attr("x", markerPos ); } Fiddle: https://jsfiddle.net/kqnLfkvw/1/
This can be solved by using d3 axis. A working example is available at https://jsfiddle.net/3gww76s0/ var make_x_marker = function (scale=1, transform=[0,0]) { console.log(transform) var markers =[]; var markerLabels={}; for (i=0; i<raw.marker1.length; i++) { var value=transform[0]+scale*(1/244.140625*1000)*raw.marker1[i]; markers.push(value) markerLabels[value]="A"+Math.floor(Math.random()*26); } return d3.svg.axis() .orient("top") .tickValues(markers) .tickSize(height-15) .tickFormat(function(d){return markerLabels[d]}) };
Apply zoom pan and axis rescale in d3
I have created scattered chart in D3. It's working fine but I have a requirement to add zooming and axis rescaling to the chart. Since I am pretty much new to d3 I am not able to do it.I have seen some example about it but I am able apply the zooming, panning etc code in my chart. Here is my code- var margin = { top: 35, right: 10, bottom: 40, left: 80 }, width = width - margin.left - margin.right, height = height - margin.top - margin.bottom; var xValue = function(d){ return d[measureArray[1]]; }, x = d3.scale.linear() .range([0, width*.98]), xMap = function(d,i) { return x(xValue(d)); }, make_x_axis = function() { return d3.svg.gridaxis() .scale(x) .orient("bottom") }, xAxis = d3.svg.axis() .scale(x) .orient("bottom") .tickFormat(function(d) { return d; }); var yValue = function(d){ return d[measureArray[0]]; }, y = d3.scale.linear() .range([height*.98, 0]), yMap = function(d,i){ return y(yValue(d)); }, make_y_axis = function() { return d3.svg.gridaxis() .scale(y) .orient("left") }, yAxis = d3.svg.axis() .scale(y) .orient("left") .tickFormat(function(d) { // if(typeof displayY !=="undefined" && displayY =="Yes"){ // if(yAxisFormat==""){ return d; }); var zValue = function(d){ return d[measureArray[2]]; }, zScale = d3.scale.linear() .range([height*.98, 0]), zMap = function(d) { return zScale(zValue(d)); }; var color = d3.scale.category10(); var svg = d3.select("body") .append("svg") .attr("id", "svg_" + div) .attr("viewBox", "0 0 "+(width + margin.left + margin.right)+" "+(height + margin.top + margin.bottom+ 17.5 )+" ") .classed("svg-content-responsive", true) .append("g") .attr("transform", "translate(" + (margin.left*.7) + "," + (margin.top+3) + ")"); var tooltip = d3.select("#"+divId).append("div") .attr("class", "tooltip") .style("opacity", 0); data.forEach(function(d) { d[measureArray[2]] = +d[measureArray[2]] d[measureArray[1]] = +d[measureArray[1]]; d[measureArray[0]] = +d[measureArray[0]]; }); x.domain([d3.min(data, xValue)-1, d3.max(data, xValue)+1]); y.domain([d3.min(data, yValue)-1, d3.max(data, yValue)+1]); // } if(typeof chartData[divId]["displayX"]!="undefined" && chartData[divId]["displayX"]!="" && chartData[divId]["displayX"]!="Yes"){}else{ svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .append("text") .attr("class", "label") .attr("x", width) .attr("y", -6) .style("text-anchor", "end") .text(measureArray[1]); } // y-axis if(typeof chartData[divId]["displayY"]!="undefined" && chartData[divId]["displayY"]!="" && chartData[divId]["displayY"]!="Yes"){}else{ svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("class", "label") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text(measureArray[0]); } var max = maximumValue(data, measureArray[2]); var min = minimumValue(data, measureArray[2]); var temp = {}; temp["min"] = min; temp["max"] = max; svg.selectAll(".circle") .data(data) .enter().append("circle") .attr("index_value", function(d, i) { return "index-" + d[columns[1]].replace(/[^a-zA-Z0-9]/g, '', 'gi'); }) .attr("class", function(d, i) { return "bars-Bubble-index-" + d[columns[1]].replace(/[^a-zA-Z0-9]/g, '', 'gi')+div; }) .attr("r", function(d,i){ // var scale = d3.scale.linear().domain([temp["max"], temp["min"]]).range(["38", "12"]); // var radius = scale(data[i][measureArray[2]]); return 6; }) .attr("cx", xMap) .attr("cy", yMap) .attr("opacity",".6") .attr("fill", 'red') .attr("id",function(d,i) { return d[columns[0]]+":"+d[columns[1]]; }) .attr("onclick", fun); Working fiddle.
You can do it like this: //define zoom behavior var zoom = d3.behavior.zoom() .on("zoom", draw); //make a rectangle for receiving all the zoom/pan action. svg.append("rect") .attr("class", "pane") .attr("width", width) .attr("height", height) .call(zoom); //make a clip path so that the circle don't go out of the graph. svg.append("clipPath") .attr("id", "clip") .append("rect") .attr("x", x(0)) .attr("y", y(1)) .attr("width", x(1) - x(0)) .attr("height", y(0) - y(1)); Add the following class to the style(so that the rectangle pane is not visible) note: that the fill is none: rect.pane { cursor: move; fill: none; pointer-events: all; } After defining the domain, set the zoom x and y x.domain([d3.min(data, xValue) - 1, d3.max(data, xValue) + 1]); y.domain([d3.min(data, yValue) - 1, d3.max(data, yValue) + 1]); // set the zoom for x and y zoom.x(x); zoom.y(y); Make a group for all the circle so that its within the clippath circlegroup = svg.append("g").attr("clip-path", "url(#clip)"); circlegroup.selectAll(".circle")... Define the draw function which will be called on zoom and pan: function draw() { svg.select("g.x.axis").call(xAxis);//zoom of x axis svg.select("g.y.axis").call(yAxis);//zoom of y axis //update the position of the circle on zoom/pan svg.selectAll("circle").attr("cx", xMap) .attr("cy", yMap) } working code here
Plotting a line graph along with Bar graph in d3js. Issue with Date
I'm learning d3js using various examples found online. I've been trying to plot a chart with dual Y axis and an X-axis. The Y axis on the left side would plot a bar chart against the X-axis and the Y-axis on the right side would plot a line chart against X-axis. The Bar graph plots as exactly as required but the line graph does not. The X-axis is date (2015-10-15 04:10). Following this example. The code I wrote var margin = {top: 50, right: 50, bottom: 100, left: 50}, width = 900 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var parseDate = d3.time.format("%m/%d/%Y %H:%M:%S").parse; var x = d3.scale.ordinal().rangeRoundBands([0, width], .05); var yTxnVol = d3.scale.linear().range([height, 0]); var yResTime = d3.scale.linear().range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") var yAxis = d3.svg.axis() .scale(yTxnVol) .orient("left") var yAxis2 = d3.svg.axis() .scale(yResTime) .orient("right") .ticks(10); var svg = d3.selectAll("body").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 + ")"); d3.csv("../res/data.csv", function(error, data) { data.forEach(function(d) { d.AVRG_RESP_TIME = +d.AVRG_RESP_TIME; d.TXN_VOL = +d.TXN_VOL; }); x.domain(data.map(function(d) { return d.TYM; })); yTxnVol.domain([0, d3.max(data, function(d) { return d.TXN_VOL+50; })]); yResTime.domain([0, d3.max(data, function(d) { return d.AVRG_RESP_TIME+50; })]); var minDate = d3.min(data, function(d){return d.TYM}); var maxDate = d3.max(data, function(d){ return d.TYM}); var xScale = d3.time.scale().range([0,width]);//.domain([minDate, maxDate]); xScale.domain(d3.extent(data, function(d) { return new Date(d.TYM); })); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .selectAll("text") .style("text-anchor", "end") .attr("dx", "-.8em") .attr("dy", "-.55em") .attr("transform", "rotate(-90)" ); svg.append("g") .attr("class", "y axis") .call(yAxis) svg.append("g") .attr("class","y axis") .attr("transform","translate("+width+ ", 0)") .call(yAxis2) svg.selectAll("bar") .data(data) .enter().append("rect") .attr("class", "yhover") .attr("x", function(d) { return x(d.TYM); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return yTxnVol(d.TXN_VOL); }) .attr("height", function(d) { return height - yTxnVol(d.TXN_VOL); }) var line = d3.svg.line() .x(function(d) { return xScale(new Date(d.TYM));}) .y(function(d) { return d.AVRG_RESP_TIME; }); svg.append("path") .datum(data) .attr("class", "line") .attr("d", line); }); The Output Trying to make this to a meaningful line graph. Got NaN error while formatting the dates. Could someone help me to make this a proper line graph ? The csv data sample TYM, AVRG_RESP_TIME, TXN_VOL 2015-10-15 04:00:00, 12, 170 2015-10-15 04:10:00, 18, 220 2015-10-15 04:20:00, 28, 251 2015-10-15 05:00:00, 19, 100
First, fix your csv file. It is improperly formatted and should not have spaces after the comma. Second, You are trying to mix an ordinal scale and a time scale for you xAxis. This isn't going to work. For your use case, just stick with time. Here's a reworking of your code with explanatory comments: <!DOCTYPE html> <html> <head> <script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script> </head> <body> <script> var margin = { top: 50, right: 50, bottom: 100, left: 50 }, width = 900 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse; // x scale should be time and only time var x = d3.time.scale().range([0, width]); var yTxnVol = d3.scale.linear().range([height, 0]); var yResTime = d3.scale.linear().range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") var yAxis = d3.svg.axis() .scale(yTxnVol) .orient("left") var yAxis2 = d3.svg.axis() .scale(yResTime) .orient("right") .ticks(10); var svg = d3.selectAll("body").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 + ")"); //d3.csv("data.csv", function(error, data) { var data = [{"TYM":"2015-10-15 04:00:00","AVRG_RESP_TIME":"12","TXN_VOL":"170"},{"TYM":"2015-10-15 04:10:00","AVRG_RESP_TIME":"18","TXN_VOL":"220"},{"TYM":"2015-10-15 04:20:00","AVRG_RESP_TIME":"28","TXN_VOL":"251"},{"TYM":"2015-10-15 05:00:00","AVRG_RESP_TIME":"19","TXN_VOL":"100"}]; // just make TYM a date and keep it as a date data.forEach(function(d) { d.TYM = parseDate(d.TYM); d.AVRG_RESP_TIME = +d.AVRG_RESP_TIME; d.TXN_VOL = +d.TXN_VOL; }); // get our min and max date in milliseconds // set a padding around our domain of 15% var minDate = d3.min(data, function(d){ return d.TYM; }).getTime(); var maxDate = d3.max(data, function(d){ return d.TYM; }).getTime(); var padDate = (maxDate - minDate) * .15; x.domain([new Date(minDate - padDate), new Date(maxDate + padDate)]); yTxnVol.domain([0, d3.max(data, function(d) { return d.TXN_VOL + 50; })]); yResTime.domain([0, d3.max(data, function(d) { return d.AVRG_RESP_TIME + 50; })]); // set an intelligent bar width var barWidth = (width / x.ticks().length) - 20; svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .selectAll("text") .style("text-anchor", "end") .attr("dx", "-.8em") .attr("dy", "-.55em") .attr("transform", "rotate(-90)"); svg.append("g") .attr("class", "y axis") .call(yAxis) svg.append("g") .attr("class", "y axis") .attr("transform", "translate(" + width + ", 0)") .call(yAxis2) svg.selectAll("bar") .data(data) .enter().append("rect") .attr("class", "yhover") .attr("x", function(d) { // center bar on time return x(d.TYM) - (barWidth / 2); }) .attr("width", barWidth) .attr("y", function(d) { return yTxnVol(d.TXN_VOL); }) .attr("height", function(d) { return height - yTxnVol(d.TXN_VOL); }) .style("fill","orange"); var line = d3.svg.line() .x(function(d) { return x(d.TYM); }) .y(function(d) { return d.AVRG_RESP_TIME; }); svg.append("path") .datum(data) .attr("class", "line") .attr("d", line) .style("fill","none") .style("stroke","steelblue") .style("stoke-width","3px"); // }); </script> </body> </html>
The issue with the line graph filling with black was due to improper css. The new css property. .line { fill: none; stroke: darkgreen; stroke-width: 2.5px; } For the dates I formatted it to (%Y-%m-%d %H:%M) format and it worked.
D3js Zoom lines and points from csv file won't work for points
I'm trying to modify Zoomable area from http://bl.ocks.org/mbostock/4015254 with adding data points from csv file in it, but couldn't get things right. Only lines can be zoomed and moved but not points. My guess is I need to put circle's .attr("transform", function(d){ return "translate("+x(d[key0])+","+y(d[key1])+")";}); within function draw, but then it cannot recognize key0 and key1 (I have multiple columns in the csv file). The following is the code I modified. Any idea how to fix this? <script> var margin = {top: 20, right: 60, bottom: 30, left: 20}, width = 960 - margin.left - margin.right, height = 800 - margin.top - margin.bottom;` var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse, formatDate = d3.time.format("%Y"); var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("right") .tickSize(-width) .tickPadding(6); var xGrid = d3.svg.axis() .scale(x) .orient("bottom") .tickSize(-height,0) .ticks(5) .tickPadding(1) .tickFormat(""); var line = d3.svg.line(); var zoom = d3.behavior.zoom() .scaleExtent([1,128]) .on("zoom", draw); var zoom2 = d3.behavior.zoom() .scaleExtent([1,128]) var svg = d3.select("body").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 + ")") .call(zoom); svg.append("g") .attr("class", "y axis") .attr("transform", "translate(" + width + ",0)"); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height/2 + ")"); svg.append("g") .attr("class", "x line") .attr("transform", "translate(0," + height/2 + ")") .append("svg:line") .attr("x1",x(0)) .attr("y1",-y(0)) .attr("x2",x(width)) .attr("y2",-y(0)); svg.append("g") .attr("class", "grid") .attr("transform", "translate(0,"+ height + ")"); svg.append("path") .attr("class", "line"); svg.append("rect") .attr("class", "pane") .attr("width", width) .attr("height", height); circle = svg.selectAll("circle"); d3.csv("encounter.csv", function(error, data) { key0 = Object.keys(data[0])[1]; key1 = Object.keys(data[0])[3]; data.forEach(function(d,i) { d[key0] = parseDate(d[key0]); d[key1] = +d[key1]; }); x.domain([new Date(2014, 0, 1), new Date(2015, 0, 1)]); y.domain(d3.extent(data, function(d){return d[key1];})).nice(); zoom.x(x); svg.select("path.line").data([data]); line.x(function(d) { return x(d[key0]); }) .y(function(d) { return y(d[key1]); }); circle.data(data) .enter().append("circle") .attr("class", "dot") .attr("r", 5) .attr("transform", function(d){ return "translate("+x(d[key0])+","+y(d[key1])+")";}); draw(); }); function draw() { svg.select("g.x.axis").call(xAxis); svg.select("g.y.axis").call(yAxis); svg.select("g.grid").call(xGrid); svg.select("path.line").attr("d", line); }
Line graph creation using D3
Iam new to D3.i just want to draw a line chart.For that i just put the static values ,its working fine,But when i fetch data from server db graph is not getting properly.Problem iam getting is x and y axis with values are getting.But not getting the line graph.here is the code // Line chart creation.................................................... var deviceTime = 0; var margin = { top: 20, right: 80, bottom: 30, left: 50 }; var da = '<%=#display.to_json(:only => [:DeviceTime, :Speed])%>' var margin = { top: 20, right: 80, bottom: 30, left: 50 }; var dataset=JSON.parse(da.replace(/"/g,'"')); //test code TODO :need to move somewhere else var data = []; for (var vAlue in dataset[0]) { var tempData = d3.values(dataset[0][vAlue]); data.push(tempData[1]); } // test end var yExtents = d3.extent(d3.merge(dataset), function (d) { return d.Speed; }); var xExtents = d3.extent(d3.merge(dataset), function (d) { return d.DeviceTime; }); //alert("xevents[0]-------"+xExtents[0] "xevents[1]-------"+xExtents[1]); var w = 900; var h = 400; var padding = 30; deviceTime = xExtents[1]; //Create scale functions var xScale = d3.scale.linear() .domain([xExtents[0], xExtents[1]]) .range([padding, w - padding * 2]); var yScale = d3.scale.linear() .domain([0, yExtents[1]]) //.domain([0, 160]) .range([h - padding, padding]); //Define X axis var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom") .ticks(10); //Define Y axis var yAxis = d3.svg.axis() .scale(yScale) .orient("left") .ticks(10); ///////////////// need to remove var line = d3.svg.line() .x(function (d, i) { return xScale(i); }) .y(function (d, i) { return yScale(d); }); //Create SVG element var svg = d3.select("#bar-demo") .append("svg") .attr("width", w + margin.left + margin.right) .attr("height", h + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); //Create X axis var hh = svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (h - padding) + ")") .call(xAxis); //Create Y axis svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + padding + ",0)") .call(yAxis); var path = svg.append("g") //.attr("clip-path", "url(#clip)") .append("path") .data([data]) .attr("class", "line") .attr("transform", "translate(" + 102 + ",0)") .attr("d", line); svg.append("text") .attr("transform", "rotate(-90)") .attr("y", 0-margin.left+10) .attr("x",0-h/2) .attr("dy", "1em") .style("text-anchor", "middle") .text("Speed km/h"); svg.append("text") .attr("y", h) .attr("x", h) .attr("dy", "1em") .style("text-anchor", "middle") .text("Time Stamp");