How to let the line fit into the axis - javascript

I am trying to plot a line using D3, but the line data only appears at the top left corner, could you please help find why the line can not fit into the axis?
var margin = { top: 20, right: 30, bottom: 30, left: 40 },
width = 680 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var lineData = [{ "x": 1, "y": 5 }, { "x": 20, "y": 20 },
{ "x": 40, "y": 10 }, { "x": 60, "y": 40 },
{ "x": 80, "y": 5 }, { "x": 100, "y": 60 }];
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1)
.domain(lineData.map(function (d) { return d.x; }));
var y = d3.scale.linear()
.range([height, 0])
.domain([0, d3.max(lineData, function (d) { return d.y; })]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svgContainer = 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 + ")");
var lineFunction = d3.svg.line()
.x(function (d) { return d.x; })
.y(function (d) { return d.y; });
var lineGraph = svgContainer.append("path")
.attr("d", lineFunction(lineData))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
svgContainer.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svgContainer.append("g")
.attr("class", "y axis")
.call(yAxis);

var lineFunction = d3.svg.line()
.x(function (d) { return x(d.x); })
.y(function (d) { return y(d.y); });
You need to apply the scales you created to your line function.

Related

How to align x axis with bars respectively in Combo Chart (Bars and Lines)?

I have a combo/ bars & lines chart based on D3.js. The x axis domain contains min and max dates, and bars are based on values. But the last bar (rect) is outside the chart. I can bring it in by forcing it (manually) but it won't reflect the data.
var data = [
{
fcst_valid_local: "2018-11-13T14:00:00-0600",
pop: 20,
rh: 67,
temp: 38,
wspd: 7
},
{
fcst_valid_local: "2018-11-14T15:00:00-0600",
pop: 15,
rh: 50,
temp: 39,
wspd: 8
},
{
fcst_valid_local: "2018-11-15T16:00:00-0600",
pop: 10,
rh: 90,
temp: 40,
wspd: 9
}
];
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 500 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Date parsing.
const parseDate = d3.timeParse("%Y-%m-%dT%H:%M:%S%Z");
data.forEach(function (d) {
d.date = parseDate(d.fcst_valid_local);
});
// Set scale domains.
var x = d3.scaleTime().range([0, width])
.domain(d3.extent(data, function (d) {
return d.date;
}));
var y0 = d3.scaleLinear().range([height, 0]).domain([0, 100]);
const y1 = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, (d) => d.pop)]);
// Construct our SVG object.
const svg = d3.select('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append('g').attr('class', 'container')
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Set x, y-left and y-right axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeDay.every(1))
// .tickFormat(d3.timeFormat('%b %d, %H:%M'))
.tickSize(0).tickPadding(10);
var y0Axis = d3.axisLeft(y0)
.ticks(5).tickSize(0);
var y1Axis = d3.axisRight(y1).ticks(5).tickSize(0);
svg.append("g")
.attr("class", "x-axis axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y-axis axis")
.attr("transform", "translate(" + 0 + ", 0)")
.call(y0Axis);
svg.append("g")
.attr("class", "y-axis axis")
.attr("transform", "translate(" + width + ", 0)")
.call(y1Axis);
// Draw bars.
var bars = svg.selectAll(".precips")
.data(data);
bars.exit().remove();
bars.enter().append("rect")
.attr("class", "precip")
.attr("width", width / data.length - 50)
.attr("x", function (d) {
return x(d.date);
})
.attr("y", height)
.transition().duration(1000)
.attr("y", function (d) {
return y0(d.pop);
})
.attr("height", function (d) {
return height - y0(d.pop);
});
const lineRH = d3.line()
.x((d) => x(d['date']))
.y(d => y0(d['rh']));
svg.append('path')
.datum(data)
.attr('class', 'line')
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-linejoin', 'round')
.attr('stroke-linecap', 'round')
.attr('stroke-width', 1.5)
.attr('d', lineRH);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
Although an answer has been accepted, I'd like to let you know that you don't have to manipulate the data (as it might be fetched from an API as well) but you can play around the x.domain() as it's all about setting the right domain here.
Try using d3 time_nice to round off the time scale domains
Play around with d3 time methods to change the dates (there are a lot here)
Here's an example of using the second approach from above and setting the x domain:
var x = d3.scaleTime().range([0, width])
.domain([d3.min(data, function (d) {
return d.date;
}), d3.timeDay.offset(d3.max(data, function (d) { return d.date; }), 1)]);
Explanation: This is offsetting the max date from the data by 1 day and so the new x.domain() would come out as:
(2) [Tue Nov 13 2018 15:00:00 GMT-0500 (Eastern Standard Time), Fri Nov 16 2018 17:00:00 GMT-0500 (Eastern Standard Time)]
which results in a chart as follows:
var data = [
{
fcst_valid_local: "2018-11-13T14:00:00-0600",
pop: 20,
rh: 67,
temp: 38,
wspd: 7
},
{
fcst_valid_local: "2018-11-14T15:00:00-0600",
pop: 15,
rh: 50,
temp: 39,
wspd: 8
},
{
fcst_valid_local: "2018-11-15T16:00:00-0600",
pop: 10,
rh: 90,
temp: 40,
wspd: 9
}
];
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 500 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Date parsing.
const parseDate = d3.timeParse("%Y-%m-%dT%H:%M:%S%Z");
data.forEach(function (d) {
d.date = parseDate(d.fcst_valid_local);
});
// Set scale domains.
var x = d3.scaleTime().range([0, width])
.domain([d3.min(data, function (d) {
return d.date;
}), d3.timeDay.offset(d3.max(data, function (d) { return d.date; }), 1)]);
var y0 = d3.scaleLinear().range([height, 0]).domain([0, 100]);
const y1 = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, (d) => d.pop)]);
//console.log(x.domain());
// Construct our SVG object.
const svg = d3.select('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append('g').attr('class', 'container')
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Set x, y-left and y-right axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeDay.every(1))
// .tickFormat(d3.timeFormat('%b %d, %H:%M'))
.tickSize(0).tickPadding(10);
var y0Axis = d3.axisLeft(y0)
.ticks(5).tickSize(0);
var y1Axis = d3.axisRight(y1).ticks(5).tickSize(0);
svg.append("g")
.attr("class", "x-axis axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y-axis axis")
.attr("transform", "translate(" + 0 + ", 0)")
.call(y0Axis);
svg.append("g")
.attr("class", "y-axis axis")
.attr("transform", "translate(" + width + ", 0)")
.call(y1Axis);
// Draw bars.
var bars = svg.selectAll(".precips")
.data(data);
bars.exit().remove();
bars.enter().append("rect")
.attr("class", "precip")
.attr("width", width / data.length - 50)
.attr("x", function (d) {
return x(d.date);
})
.attr("y", height)
.transition().duration(1000)
.attr("y", function (d) {
return y0(d.pop);
})
.attr("height", function (d) {
return height - y0(d.pop);
});
const lineRH = d3.line()
.x((d) => x(d['date']) + (width / data.length - 50)/2)
.y(d => y0(d['rh']));
svg.append('path')
.datum(data)
.attr('class', 'line')
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-linejoin', 'round')
.attr('stroke-linecap', 'round')
.attr('stroke-width', 1.5)
.attr('d', lineRH);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
I also tried with .nice() and a fun part would be to use the d3 time intervals within .nice(). Feel free to play around with those and let me know if you have any questions.
Also, I'm offsetting the line (path) by the barwidth/2 in the line generator fn.
d3.line()
.x((d) => x(d['date']) + (width / data.length - 50)/2)
Hope this helps as well.
Add a dummy data item that is a bit later then the last item
Here it is done hard coded but you can add it dynamic based on the date of the last item
var data = [
{
fcst_valid_local: "2018-11-13T14:00:00-0600",
pop: 20,
rh: 67,
temp: 38,
wspd: 7
},
{
fcst_valid_local: "2018-11-14T15:00:00-0600",
pop: 15,
rh: 50,
temp: 39,
wspd: 8
},
{
fcst_valid_local: "2018-11-15T16:00:00-0600",
pop: 10,
rh: 90,
temp: 40,
wspd: 9
},
{
fcst_valid_local: "2018-11-16T01:00:00-0600"
}
];
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 500 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Date parsing.
const parseDate = d3.timeParse("%Y-%m-%dT%H:%M:%S%Z");
data.forEach(function (d) {
d.date = parseDate(d.fcst_valid_local);
});
// Set scale domains.
var x = d3.scaleTime().range([0, width])
.domain(d3.extent(data, function (d) {
return d.date;
}));
var y0 = d3.scaleLinear().range([height, 0]).domain([0, 100]);
const y1 = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, (d) => d.pop)]);
// Construct our SVG object.
const svg = d3.select('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append('g').attr('class', 'container')
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Set x, y-left and y-right axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeDay.every(1))
// .tickFormat(d3.timeFormat('%b %d, %H:%M'))
.tickSize(0).tickPadding(10);
var y0Axis = d3.axisLeft(y0)
.ticks(5).tickSize(0);
var y1Axis = d3.axisRight(y1).ticks(5).tickSize(0);
svg.append("g")
.attr("class", "x-axis axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y-axis axis")
.attr("transform", "translate(" + 0 + ", 0)")
.call(y0Axis);
svg.append("g")
.attr("class", "y-axis axis")
.attr("transform", "translate(" + width + ", 0)")
.call(y1Axis);
// Draw bars.
var bars = svg.selectAll(".precips")
.data(data);
bars.exit().remove();
bars.enter().append("rect")
.attr("class", "precip")
.attr("width", width / data.length - 50)
.attr("x", function (d) {
return x(d.date);
})
.attr("y", height)
.transition().duration(1000)
.attr("y", function (d) {
return y0(d.pop);
})
.attr("height", function (d) {
return height - y0(d.pop);
});
const lineRH = d3.line()
.x((d) => x(d['date']))
.y(d => y0(d['rh']));
svg.append('path')
.datum(data)
.attr('class', 'line')
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-linejoin', 'round')
.attr('stroke-linecap', 'round')
.attr('stroke-width', 1.5)
.attr('d', lineRH);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

Stacked chart with data array in d3 js

I have try to customize my existing code of Stacked Bar graph with the help of this link
https://bl.ocks.org/mbostock/1134768.
I have done this graph with the help of given link but i have some confusion about the data points on y axis as shown below in picture
Here is my full code of Stacked bar Graph
var data = [
{month: 'Jan',total:100 ,A: 35, B: 5, C: 10},
{month: 'Feb',total:200 ,A: 30, B: 10, C: 20},
{month: 'Mar',total:300 ,A: 0, B: 10, C: 20}
];
var xData = ["A", "B", "C"];
var margin = {top: 20, right: 50, bottom: 30, left: 50},
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .35);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.category20();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
data.forEach(function(d) {
xData.forEach(function(c) {
d[c] = +d[c];}); })
var dataIntermediate = xData.map(function (c) {
return data.map(function (d) {
return {x: d.month, y: d[c]};});});
var dataStackLayout = d3.layout.stack()(dataIntermediate);
x.domain(dataStackLayout[0].map(function (d) {
return d.x; }));
y.domain([0,d3.max(dataStackLayout[dataStackLayout.length - 1],
function (d) { return d.y0 + d.y;}) ]).nice();
var layer = svg.selectAll(".stack")
.data(dataStackLayout)
.enter().append("g")
.attr("class", "stack")
.style("fill", function (d, i) {
console.info(i, color(i));
return color(i);});
layer.selectAll("rect")
.data(function (d) {
return d;})
.enter().append("rect")
.attr("x", function (d) {
console.info("dx", d.x,x(d.x), x.rangeBand());
return x(d.x);})
.attr("y", function (d) {
return y(d.y + d.y0);})
.attr("height", function (d) {
return y(d.y0) - y(d.y + d.y0); })
.attr("width", x.rangeBand() -1);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis axis--y")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
As i have taken help from the given url, i have change my code which take array data as input instead of CSV file. But i think my data points(label) on y axis is not according to given data(key:total).
Any help should be appreciated.
Here is solution about my confusion on how can i show the total count on y axis
because in my array data there is key 'total' which is total count of all states such as 'A','B' & 'C' shown in array data
var data = [
{month: "4/1854",total:"100" ,A: "45", B:"45", C:"10"},
{month: "5/1854",total:"200" ,A:"80", B:"70", C:"50"},
{month: "6/1854",total:"300" ,A:"0", B:"100", C:"200"}
];
So as there is a total key in array data and i have to show as label on y axis.Here is my full code
var data = [
{month: "4/1854",total:"100" ,A: "45", B:"45", C:"10"},
{month: "5/1854",total:"200" ,A:"80", B:"70", C:"50"},
{month: "6/1854",total:"300" ,A:"0", B:"100", C:"200"}
];
var xData = ["A", "B", "C"];
var parseDate = d3.time.format("%m/%Y").parse;
var margin = {top: 20, right: 50, bottom: 30, left: 50},
width = 500 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .35);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.category20();
//console.info(color(0));
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%b"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#pie").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 + ")");
data.forEach(function(d) {
d.month = parseDate(d.month);
xData.forEach(function(c) {
d[c] = +d[c];
});
})
var dataIntermediate = xData.map(function (c) {
return data.map(function (d) {
return {x: d.month, y: d[c]};
});
});
var dataStackLayout = d3.layout.stack()(dataIntermediate);
x.domain(dataStackLayout[0].map(function (d) {
return d.x;
}));
y.domain([0,
d3.max(data, function(d) { return d.total; })
])
.nice();
var layer = svg.selectAll(".stack")
.data(dataStackLayout)
.enter().append("g")
.attr("class", "stack")
.style("fill", function (d, i) {
console.info(i, color(i));
return color(i);
});
layer.selectAll("rect")
.data(function (d) {
return d;
})
.enter().append("rect")
.attr("x", function (d) {
console.info("dx", d.x,x(d.x), x.rangeBand());
return x(d.x);
})
.attr("y", function (d) {
return y(d.y + d.y0);
})
.attr("height", function (d) {
// console.info(d.y0, d.y, y(d.y0), y(d.y))
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.rangeBand() -1);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis axis--y")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
}
In case you are trying to add a new dataset with key "total", try to add key "total" in your xData array like following and it should be Visible on chart.
var xData = ["total", "A", "B", "C"];

D3.js: Zoom in x direction with panning

I am quiet new to D3 and currently trying to create zooming functionality in my plots. I was able to get the zoom option working but not exactly how i want it to be. I only want zoom behaviour in horizontal (x axis) and no zooming behaviour in y axis. I also want to code panning in my horizontal (x axis) direction. I was also trying to hinge it to the x axis without disabling panning. I have tried a few ways but unable to do so.
Any help/tips would be awesome
JSFIDDLE LINK: https://jsfiddle.net/4sts8nfs/3/
Code:
var data = [{
"mytime": "2015-12-01T23:10:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:15:00.000Z",
"value": 67
}, {
"mytime": "2015-12-01T23:20:00.000Z",
"value": 70
}, {
"mytime": "2015-12-01T23:25:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:30:00.000Z",
"value": 72
}, {
"mytime": "2015-12-01T23:35:00.000Z",
"value": 75
}, {
"mytime": "2015-12-01T23:40:00.000Z",
"value": 71
}, {
"mytime": "2015-12-01T23:45:00.000Z",
"value": 80
}, {
"mytime": "2015-12-01T23:50:00.000Z",
"value": 83
}, {
"mytime": "2015-12-01T23:55:00.000Z",
"value": 86
}, {
"mytime": "2015-12-02T00:00:00.000Z",
"value": 80
}, {
"mytime": "2015-12-02T00:05:00.000Z",
"value": 85
}];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
data.forEach(function(d) {
d.mytime = parseDate(d.mytime);
});
//var margin = { top: 30, right: 30, bottom: 40, left:50 },
var margin = {
top: 30,
right: 30,
bottom: 40,
left: 50
},
height = 200,
width = 900;
var color = "green";
var xaxis_param = "mytime";
var yaxis_param = "value";
var params1 = {
margin: margin,
height: height,
width: width,
color: color,
xaxis_param: xaxis_param,
yaxis_param: yaxis_param
};
draw_graph(data, params1);
function draw_graph(data, params) {
var make_x_axis = function() {
return d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.ticks(5);
};
var make_y_axis = function() {
return d3.svg.axis()
.scale(y_scale)
.orient("left")
.ticks(5);
};
//Get the margin
var xaxis_param = params.xaxis_param;
var yaxis_param = params.yaxis_param;
var color_code = params.color;
var margin = params.margin;
var height = params.height - margin.top - margin.bottom,
width = params.width - margin.left - margin.right;
var x_extent = d3.extent(data, function(d) {
return d[xaxis_param]
});
var y_extent = d3.extent(data, function(d) {
return d[yaxis_param]
});
var x_scale = d3.time.scale()
.domain(x_extent)
.range([0, width]);
var y_scale = d3.scale.linear()
.domain([0, y_extent[1]])
.range([height, 0]);
var zoom = d3.behavior.zoom()
.x(x_scale)
.y(y_scale)
.on("zoom", zoomed);
//Line
var line = d3.svg.line()
.defined(function(d) {
return d[yaxis_param];
})
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(d[yaxis_param]);
});
var lineRef = d3.svg.line()
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(20);
});
var myChart = d3.select('body').append('svg')
.attr('id', 'graph')
.style('background', '#E7E0CB')
.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)
.on("mousedown.zoom", null);
myChart.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot");
var legend = myChart.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
legend.append("rect")
.style("fill", color_code)
.attr("width", 20)
.attr("height", 20);
legend.append("text")
.text(yaxis_param)
.attr("x", 25)
.attr("y", 12);
var vAxis = d3.svg.axis()
.scale(y_scale)
.orient('left')
.ticks(5)
var hAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(5);
var majorAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.day, 1)
.tickSize(-height)
.outerTickSize(0);
myChart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(hAxis);
myChart.append("g")
.attr("class", "x axis major")
.attr("transform", "translate(0," + height + ")")
.call(majorAxis);
myChart.append("g")
.attr("class", "y axis")
.call(vAxis);
var circlePoint = myChart.selectAll('circle')
.data(data)
.enter()
.append("circle");
var circleAttributes = circlePoint
.attr("cx", function (d) { return x_scale(d[xaxis_param]); })
.attr("cy", function (d) { return y_scale(d[yaxis_param]); })
.attr("r", 3)
.style("fill", "none")
.style("stroke", "red");
var clip = myChart.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var chartBody = myChart.append("g")
.attr("clip-path", "url(#clip)");
chartBody.append("svg:path")
.datum(data)
.attr('class', 'line')
.attr("d", line)
.attr('stroke', color_code)
.attr('stroke-width', 1)
.attr('fill', 'none');
chartBody
.append('svg:path')
.datum(data)
.attr('class', 'line1')
.attr("d", lineRef)
.attr('stroke', 'blue')
.attr('stroke-width', 1)
.style("stroke-dasharray", ("3, 3"))
.attr('fill', 'none');
function zoomed() {
myChart.select(".x.axis").call(hAxis);
myChart.select(".y.axis").call(vAxis);
myChart.select(".x.axis.major").call(majorAxis);
myChart.select(".line")
.attr("class", "line")
.attr("d", line);
myChart.select(".line1")
.attr("class", "line1")
.attr("d", lineRef);
circlePoint
.attr("cx", function (d) { return x_scale(d[xaxis_param]); })
.attr("cy", function (d) { return y_scale(d[yaxis_param]); });
}
}
You only have to change the definition of your zoom behavior such that it will not update y_scale while zooming.
var zoom = d3.behavior.zoom()
.x(x_scale)
//.y(y_scale)
.on("zoom", zoomed);

D3.js: Moving circles along with line in a 2D graph when zoomed

I am currently trying to plot a time series data in d3.js. I have rendered a line and plotted circles for each data point (In the future circles would be used to annotate specific data points). I am trying to zoom all the components using the "zoom" behaviour in d3.js. But I am unable to drag and zoom the circle along the line.
How can i move the circles along with line. Following is the jsfiddle for the code:
https://jsfiddle.net/adityap16/4sts8nfs/2/
Code:
var data = [{
"mytime": "2015-12-01T23:10:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:15:00.000Z",
"value": 67
}, {
"mytime": "2015-12-01T23:20:00.000Z",
"value": 70
}, {
"mytime": "2015-12-01T23:25:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:30:00.000Z",
"value": 72
}, {
"mytime": "2015-12-01T23:35:00.000Z",
"value": 75
}, {
"mytime": "2015-12-01T23:40:00.000Z",
"value": 71
}, {
"mytime": "2015-12-01T23:45:00.000Z",
"value": 80
}, {
"mytime": "2015-12-01T23:50:00.000Z",
"value": 83
}, {
"mytime": "2015-12-01T23:55:00.000Z",
"value": 86
}, {
"mytime": "2015-12-02T00:00:00.000Z",
"value": 80
}, {
"mytime": "2015-12-02T00:05:00.000Z",
"value": 85
}];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
data.forEach(function(d) {
d.mytime = parseDate(d.mytime);
});
//var margin = { top: 30, right: 30, bottom: 40, left:50 },
var margin = {
top: 30,
right: 30,
bottom: 40,
left: 50
},
height = 200,
width = 900;
var color = "green";
var xaxis_param = "mytime";
var yaxis_param = "value";
var params1 = {
margin: margin,
height: height,
width: width,
color: color,
xaxis_param: xaxis_param,
yaxis_param: yaxis_param
};
draw_graph(data, params1);
function draw_graph(data, params) {
var make_x_axis = function() {
return d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.ticks(5);
};
var make_y_axis = function() {
return d3.svg.axis()
.scale(y_scale)
.orient("left")
.ticks(5);
};
//Get the margin
var xaxis_param = params.xaxis_param;
var yaxis_param = params.yaxis_param;
var color_code = params.color;
var margin = params.margin;
var height = params.height - margin.top - margin.bottom,
width = params.width - margin.left - margin.right;
var x_extent = d3.extent(data, function(d) {
return d[xaxis_param]
});
var y_extent = d3.extent(data, function(d) {
return d[yaxis_param]
});
var x_scale = d3.time.scale()
.domain(x_extent)
.range([0, width]);
var y_scale = d3.scale.linear()
.domain([0, y_extent[1]])
.range([height, 0]);
var zoom = d3.behavior.zoom()
.x(x_scale)
.y(y_scale)
.on("zoom", zoomed);
//Line
var line = d3.svg.line()
.defined(function(d) {
return d[yaxis_param];
})
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(d[yaxis_param]);
});
var lineRef = d3.svg.line()
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(20);
});
var myChart = d3.select('body').append('svg')
.attr('id', 'graph')
.style('background', '#E7E0CB')
.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);
myChart.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot");
var legend = myChart.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
legend.append("rect")
.style("fill", color_code)
.attr("width", 20)
.attr("height", 20);
legend.append("text")
.text(yaxis_param)
.attr("x", 25)
.attr("y", 12);
var vAxis = d3.svg.axis()
.scale(y_scale)
.orient('left')
.ticks(5)
var hAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(5);
var majorAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.day, 1)
.tickSize(-height)
.outerTickSize(0);
myChart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(hAxis);
myChart.append("g")
.attr("class", "x axis major")
.attr("transform", "translate(0," + height + ")")
.call(majorAxis);
myChart.append("g")
.attr("class", "y axis")
.call(vAxis);
var circlePoint = myChart.selectAll('circle')
.data(data)
.enter()
.append("circle");
var circleAttributes = circlePoint
.attr("cx", function (d) { return x_scale(d[xaxis_param]); })
.attr("cy", function (d) { return y_scale(d[yaxis_param]); })
.attr("r", 3)
.style("fill", "none")
.style("stroke", "red");
var clip = myChart.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var chartBody = myChart.append("g")
.attr("clip-path", "url(#clip)");
chartBody.append("svg:path")
.datum(data)
.attr('class', 'line')
.attr("d", line)
.attr('stroke', color_code)
.attr('stroke-width', 1)
.attr('fill', 'none');
chartBody
.append('svg:path')
.datum(data)
.attr('class', 'line1')
.attr("d", lineRef)
.attr('stroke', 'blue')
.attr('stroke-width', 1)
.style("stroke-dasharray", ("3, 3"))
.attr('fill', 'none');
function zoomed() {
myChart.select(".x.axis").call(hAxis);
myChart.select(".y.axis").call(vAxis);
myChart.select(".x.axis.major").call(majorAxis);
myChart.select(".line")
.attr("class", "line")
.attr("d", line);
myChart.select(".line1")
.attr("class", "line1")
.attr("d", lineRef);
}
}
You just need to update the circles' positions in your zoomed() handler function:
circlePoint
.attr("cx", function (d) { return x_scale(d[xaxis_param]); })
.attr("cy", function (d) { return y_scale(d[yaxis_param]); });
Because D3's zoom behavior will have taken care of updating the scales, they can easily be used for calculating the new positions.
Have a look at this snippet for a full example:
var data = [{
"mytime": "2015-12-01T23:10:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:15:00.000Z",
"value": 67
}, {
"mytime": "2015-12-01T23:20:00.000Z",
"value": 70
}, {
"mytime": "2015-12-01T23:25:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:30:00.000Z",
"value": 72
}, {
"mytime": "2015-12-01T23:35:00.000Z",
"value": 75
}, {
"mytime": "2015-12-01T23:40:00.000Z",
"value": 71
}, {
"mytime": "2015-12-01T23:45:00.000Z",
"value": 80
}, {
"mytime": "2015-12-01T23:50:00.000Z",
"value": 83
}, {
"mytime": "2015-12-01T23:55:00.000Z",
"value": 86
}, {
"mytime": "2015-12-02T00:00:00.000Z",
"value": 80
}, {
"mytime": "2015-12-02T00:05:00.000Z",
"value": 85
}];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
data.forEach(function(d) {
d.mytime = parseDate(d.mytime);
});
//var margin = { top: 30, right: 30, bottom: 40, left:50 },
var margin = {
top: 30,
right: 30,
bottom: 40,
left: 50
},
height = 200,
width = 900;
var color = "green";
var xaxis_param = "mytime";
var yaxis_param = "value";
var params1 = {
margin: margin,
height: height,
width: width,
color: color,
xaxis_param: xaxis_param,
yaxis_param: yaxis_param
};
draw_graph(data, params1);
function draw_graph(data, params) {
var make_x_axis = function() {
return d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.ticks(5);
};
var make_y_axis = function() {
return d3.svg.axis()
.scale(y_scale)
.orient("left")
.ticks(5);
};
//Get the margin
var xaxis_param = params.xaxis_param;
var yaxis_param = params.yaxis_param;
var color_code = params.color;
var margin = params.margin;
var height = params.height - margin.top - margin.bottom,
width = params.width - margin.left - margin.right;
var x_extent = d3.extent(data, function(d) {
return d[xaxis_param]
});
var y_extent = d3.extent(data, function(d) {
return d[yaxis_param]
});
var x_scale = d3.time.scale()
.domain(x_extent)
.range([0, width]);
var y_scale = d3.scale.linear()
.domain([0, y_extent[1]])
.range([height, 0]);
var zoom = d3.behavior.zoom()
.x(x_scale)
.y(y_scale)
.on("zoom", zoomed);
//Line
var line = d3.svg.line()
.defined(function(d) {
return d[yaxis_param];
})
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(d[yaxis_param]);
});
var lineRef = d3.svg.line()
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(20);
});
var myChart = d3.select('body').append('svg')
.attr('id', 'graph')
.style('background', '#E7E0CB')
.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);
myChart.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot");
var legend = myChart.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
legend.append("rect")
.style("fill", color_code)
.attr("width", 20)
.attr("height", 20);
legend.append("text")
.text(yaxis_param)
.attr("x", 25)
.attr("y", 12);
var vAxis = d3.svg.axis()
.scale(y_scale)
.orient('left')
.ticks(5)
var hAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(5);
var majorAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.day, 1)
.tickSize(-height)
.outerTickSize(0);
myChart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(hAxis);
myChart.append("g")
.attr("class", "x axis major")
.attr("transform", "translate(0," + height + ")")
.call(majorAxis);
myChart.append("g")
.attr("class", "y axis")
.call(vAxis);
var circlePoint = myChart.selectAll('circle')
.data(data)
.enter()
.append("circle");
var circleAttributes = circlePoint
.attr("cx", function (d) { return x_scale(d[xaxis_param]); })
.attr("cy", function (d) { return y_scale(d[yaxis_param]); })
.attr("r", 3)
.style("fill", "none")
.style("stroke", "red");
var clip = myChart.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var chartBody = myChart.append("g")
.attr("clip-path", "url(#clip)");
chartBody.append("svg:path")
.datum(data)
.attr('class', 'line')
.attr("d", line)
.attr('stroke', color_code)
.attr('stroke-width', 1)
.attr('fill', 'none');
chartBody
.append('svg:path')
.datum(data)
.attr('class', 'line1')
.attr("d", lineRef)
.attr('stroke', 'blue')
.attr('stroke-width', 1)
.style("stroke-dasharray", ("3, 3"))
.attr('fill', 'none');
function zoomed() {
myChart.select(".x.axis").call(hAxis);
myChart.select(".y.axis").call(vAxis);
myChart.select(".x.axis.major").call(majorAxis);
myChart.select(".line")
.attr("class", "line")
.attr("d", line);
myChart.select(".line1")
.attr("class", "line1")
.attr("d", lineRef);
circlePoint
.attr("cx", function (d) { return x_scale(d[xaxis_param]); })
.attr("cy", function (d) { return y_scale(d[yaxis_param]); });
}
}
svg {
font: 10px sans-serif;
}
.plot {
fill: rgba(250, 250, 255, 0.6);
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

d3.js: Issue in zooming multiple lines in the same graph

I am quiet new to d3.js and currently working on plotting a time series with zoom capabilities. I was able to get the code working for zooming a single line on the graph but as soon as i add another line (reference line in dashes) that line remains stationary.
I tried a few variations in how i am trying to call the lines in my "zoomed()" function but to no avail .
Following is the link to jsfiddle as looking at the example might give you a better idea on what i mean:
https://jsfiddle.net/adityap16/x78zgwux/14/
Also attached is the code:
var data = [{
"mytime": "2015-12-01T23:10:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:15:00.000Z",
"value": 67
}, {
"mytime": "2015-12-01T23:20:00.000Z",
"value": 70
}, {
"mytime": "2015-12-01T23:25:00.000Z",
"value": 64
}, {
"mytime": "2015-12-01T23:30:00.000Z",
"value": 72
}, {
"mytime": "2015-12-01T23:35:00.000Z",
"value": 75
}, {
"mytime": "2015-12-01T23:40:00.000Z",
"value": 71
}, {
"mytime": "2015-12-01T23:45:00.000Z",
"value": 80
}, {
"mytime": "2015-12-01T23:50:00.000Z",
"value": 83
}, {
"mytime": "2015-12-01T23:55:00.000Z",
"value": 86
}, {
"mytime": "2015-12-02T00:00:00.000Z",
"value": 80
}, {
"mytime": "2015-12-02T00:05:00.000Z",
"value": 85
}];
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ").parse;
data.forEach(function(d) {
d.mytime = parseDate(d.mytime);
});
//var margin = { top: 30, right: 30, bottom: 40, left:50 },
var margin = {
top: 30,
right: 30,
bottom: 40,
left: 50
},
height = 200,
width = 900;
var color = "green";
var xaxis_param = "mytime";
var yaxis_param = "value";
var params1 = {
margin: margin,
height: height,
width: width,
color: color,
xaxis_param: xaxis_param,
yaxis_param: yaxis_param
};
draw_graph(data, params1);
function draw_graph(data, params) {
var make_x_axis = function() {
return d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.ticks(5);
};
var make_y_axis = function() {
return d3.svg.axis()
.scale(y_scale)
.orient("left")
.ticks(5);
};
//Get the margin
var xaxis_param = params.xaxis_param;
var yaxis_param = params.yaxis_param;
var color_code = params.color;
var margin = params.margin;
var height = params.height - margin.top - margin.bottom,
width = params.width - margin.left - margin.right;
var x_extent = d3.extent(data, function(d) {
return d[xaxis_param]
});
var y_extent = d3.extent(data, function(d) {
return d[yaxis_param]
});
var x_scale = d3.time.scale()
.domain(x_extent)
.range([0, width]);
var y_scale = d3.scale.linear()
.domain([0, y_extent[1]])
.range([height, 0]);
var zoom = d3.behavior.zoom()
.x(x_scale)
.y(y_scale)
.on("zoom", zoomed);
//Line
var line = d3.svg.line()
.defined(function(d) {
return d[yaxis_param];
})
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(d[yaxis_param]);
});
var lineRef = d3.svg.line()
.x(function(d) {
return x_scale(d[xaxis_param]);
})
.y(function(d) {
return y_scale(20);
});
var myChart = d3.select('body').append('svg')
.attr('id', 'graph')
.style('background', '#E7E0CB')
.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);
myChart.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot");
var legend = myChart.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + 5 + "," + (height - 25) + ")")
legend.append("rect")
.style("fill", color_code)
.attr("width", 20)
.attr("height", 20);
legend.append("text")
.text(yaxis_param)
.attr("x", 25)
.attr("y", 12);
var vAxis = d3.svg.axis()
.scale(y_scale)
.orient('left')
.ticks(5)
var hAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(5);
var majorAxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.ticks(d3.time.day, 1)
.tickSize(-height)
.outerTickSize(0);
myChart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(hAxis);
myChart.append("g")
.attr("class", "x axis major")
.attr("transform", "translate(0," + height + ")")
.call(majorAxis);
myChart.append("g")
.attr("class", "y axis")
.call(vAxis);
var clip = myChart.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var chartBody = myChart.append("g")
.attr("clip-path", "url(#clip)");
chartBody.append("svg:path")
.datum(data)
.attr('class', 'line')
.attr("d", line)
.attr('stroke', color_code)
.attr('stroke-width', 1)
.attr('fill', 'none');
chartBody
.append('svg:path')
.datum(data)
.attr('class', 'line')
.attr("d", lineRef)
.attr('stroke', 'blue')
.attr('stroke-width', 1)
.style("stroke-dasharray", ("3, 3"))
.attr('fill', 'none');
function zoomed() {
myChart.select(".x.axis").call(hAxis);
myChart.select(".y.axis").call(vAxis);
myChart.select(".x.axis.major").call(majorAxis);
myChart.select('path.line').attr('d', lineRef);
myChart.select('path.line').attr('d', line);
}
}
I was also wondering how to zoom in or out if instead of mouse roll over, i could just select that part of the data and that part zooms in which is kind of like context via brushing https://bl.ocks.org/mbostock/1667367
But i wanted to make it without the second graph.
Thanks!

Categories

Resources