rangeRoundBands outerPadding in bar chart way too big - javascript

I am new to D3.js and have a problem with my vertical bar chart. For some reason, the distance between the axis and the bars is way too big when I use rangeRoundBands for scaling.
In the API, it is explained like this:
So the problem seems to be the outerPadding. But setting the outerPadding to zero does not help. However, when I use rangeBands instead, the problem disappears and the bars are positioned correctly, right below the axis. But then I will get these nasty antialiasing effects, so this is not really an option. Here is my code:
var margin = {top: 40, right: 40, bottom: 20, left: 20},
width = 900 - margin.left - margin.right,
height = x - margin.top - margin.bottom;
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.ordinal().rangeRoundBands([0, height], .15, 0);
var xAxis = d3.svg.axis()
.scale(x)
.orient("top");
var xAxis2 = d3.svg.axis()
.scale(x)
.orient("bottom");
var xAxis3 = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height, 0, 0)
.tickFormat("");
var svg = d3.select("#plotContainer").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 + ")");
x.domain(d3.extent(data, function(d) {
return d.size;
})).nice();
y.domain(data.map(function(d) {
return d.name;
}));
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis2);
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(xAxis3);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) {
return d.size < 0 ? "bar negative" : "bar positive";
})
.attr("x", function(d) {
return x(Math.min(0, d.size));
})
.attr("y", function(d) {
return y(d.name);
})
.attr("width", function(d) {
return Math.abs(x(d.size) - x(0));
})
.attr("height", y.rangeBand())
.append("title")
.text(function(d) {
return "This value is " + d.name;
});
;
svg.selectAll(".bar.positive")
.style("fill", "steelblue")
.on("mouseover", function(d) {
d3.select(this).style("fill", "yellow");
})
.on("mouseout", function(d) {
d3.select(this).style("fill", "steelblue");
});
svg.selectAll(".bar.negative")
.style("fill", "brown")
.on("mouseover", function(d) {
d3.select(this).style("fill", "yellow");
})
.on("mouseout", function(d) {
d3.select(this).style("fill", "brown");
});
svg.selectAll(".axis")
.style("fill", "none")
.style("shape-rendering", "crispEdges")
.style("stroke", "#000")
.style("font", "10px sans-serif");
svg.selectAll(".grid")
.style("fill", "none")
.style("stroke", "lightgrey")
.style("opacity", "0.7");
svg.selectAll(".grid.path")
.style("stroke-width", "0");
EDIT:
Please take a look at this fiddle:
http://jsfiddle.net/GUYZk/9/
My problem is reproducible there. You cannot alter the outerPadding with rangeRoundBands, whereas rangeBands behaves normal.

TL;DR: this is a consequence of the math. To work around, use rangeBands to lay out the bars, and use shape-rendering: crispEdges in CSS to align them to pixel boundaries.
Full explanation:
Because the rangeRoundBands function must distribute the bars evenly throughout the provided pixel range AND it must also provide an integer rangeBand, it uses Math.floor to chop off the fractional bit of each successive bar.
The reason this extra outer padding compounds with longer datasets is because all those fractional pixels have to end up somewhere. The author of this function chose to evenly split them between the beginning and the end of the range.
Because the fraction pixel of each rounded bar is on the interval (0, 1), the extra pixels glommed onto each end will span about 1/4 of the data bar count. With 10 bars, 2-3 extra pixels would never be noticed, but if you have 100 or more, the extra 25+ pixels become much more noticeable.
One possible solution that appears to work in Chrome for svg:rect: use rangeBands to lay out, but then apply shape-rendering: crispEdges as a CSS style to your bar paths/rects.
This then leaves the onus on the SVG renderer to nudge each bar to a pixel boundary, but they are more evenly spaced overall, with occasional variance in the spacing to account for the error over the whole chart.
Personally, I use shape-rendering: optimizeSpeed and let the rendering agent make whatever tradeoffs it must to quickly render the (potentially fractional) bar positions.

I know this is old, but I came to this answer with the same problem; Ben's answer suggests another simple (but likely slower) workaround; you could fix a padding by shifting all x values to the left by the first value in the computed range.
Example:
// set your desired gap between y-axis and first bar
var yGap = 5;
// create your scale
var xScale = d3.scale.ordinal()
.domain(dataArray)
.rangeRoundBands([0, width], .1);
// obtain the *computed* left edge of the first bar
var leftOuter = xScale.range()[0];
// when setting the x attribute of your rects:
.attr("x", function(d) { return xScale(d) - leftOuter + yGap; });
The last line shifts everything to the left by an amount such that the left outer padding is your chosen yGap and the right outer padding is whatever it needs to be to make up the difference. Essentially this overrides the creator's intention of splitting the excess padding between the left and right sides.
I hope someone else finds this useful!

I have the same issue, and no matter I used for inner padding or outer padding, I just could not get the ticks align at the center of my vertical bars on the X-Axis. So I have not used the inner or outer padding. I have used my own padding, say 0.1.
for the bar rect, I set the width as
width: (1.0 - padding) * xScale.rangeBand()
for the x I just add half of the padding like this.
x: padding * xScale.rangeBand() / 2
This made the ticks perfectly align with the vertical bands.

Related

Creating a categorical line chart in D3.js (V4)

I'm relatively new to D3.js and I'm visualising the 'PassengersIn' & 'PassengersOut' values from my busdatasimple.json file. For reference, one of the JSON objects looks like this;
{
"BusNo": "1",
"Date": "21 November 2016",
"Time": "09:10:34 AM",
"Destination": "Pier 50",
"Longitude": "-122.383262",
"Latitude": "37.773644",
"PassengersIn": "8",
"PassengersOut": "1"
}
I'm now trying to graph the PassengersIn & PassengersOut against the Destination using two lines on a line graph. I'm struggling with the axes as the x has only 2 ticks and the y is not scaling to my data. As seen below;
My code is as follows. I've removed the irrelevant Google Maps and jQuery.
//Setting The Dimensions Of The Canvas
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 650 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
//Setting X & Y Ranges
var x = d3.scaleOrdinal().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
//Define The Axes
var xAxis = d3.axisBottom().scale(x);
var yAxis = d3.axisLeft().scale(y).ticks(10);
//Add The SVG Element
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right + 50)
.attr("height", height + margin.top + margin.bottom + 200)
.attr("class", "svg")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Load Data From JSON
d3.json("busdatasimple.json", function(error, data) {
//Functions for Y-Axis Grid Lines
function yGridLines() {
return d3.axisLeft().scale(y).ticks(5);
}
//Adding the Y-Axis Grid Lines
svg.append("g")
.attr("class", "grid-lines")
.call(yGridLines().tickSize(-width, 0, 0).tickFormat(""));
//Adding Y-Axis
svg.append("g")
.attr("class", "y axis").call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Passengers In");
//Adding X-Axis (Added to the end of the code so the label show over bottom bars)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "middle")
//.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "translate(-5, 15)")
.attr("font-family", "Arial")
.attr("font-weight", "bold")
.attr("font-size", "1.1em");
x.domain(data.map(function(d){return d.Destination;}));
y.domain([d3.min(data, function(d){return d.PassengersIn;}), d3.max(data, function(d) {return d.PassengersIn;})]);
var line = d3.line()
.x(function(d){return x(d.Destination);})
.y(function(d){return y(d.PassengersIn);});
svg.append("path").datum(data)
.attr("class", "line")
.attr("d", function(d){return d.PassengersIn;})
.attr("stroke", "green")
.attr("stroke-width", 2);
});
I've managed to find a few examples that use a categorical ordinal scale, however, they are all using v3 of d3.js and after reading through the v4 API countless times I still can't figure it out.
You want a categorical scale, that's right, but you don't want an ordinal scale here.
There was a lot of changes from v3 to v4. In v3, you could set .rangeBands, .rangeRoundBands, .rangePoints and .rangeRoundPoints to your ordinal scale, which therefore could accept an continuous range. Not anymore: in D3 v4 you have the brand new scaleBand and scalePoint.
In v4, in a regular ordinal scale (which is scaleOrdinal):
If range is specified, sets the range of the ordinal scale to the specified array of values. The first element in the domain will be mapped to the first element in range, the second domain value to the second range value, and so on. If there are fewer elements in the range than in the domain, the scale will reuse values from the start of the range. (emphases mine)
So, in an scaleOrdinal, the range need to have the same length (number of elements) of the domain.
That being said, you want a point scale (scalePoint) here. Band scales and point scales...
...are like ordinal scales except the output range is continuous and numeric. (emphasis mine)
Check this snippet, look at the console and compare the two scales:
var destinations = ["foo", "bar", "baz", "foobar", "foobaz"];
var scale1 = d3.scaleOrdinal()
.range([0, 100])
.domain(destinations);
var scale2 = d3.scalePoint()
.range([0, 100])
.domain(destinations);
destinations.forEach(d=>{
console.log(d + " in an ordinal scale: " + scale1(d) + " / in a point scale: " + scale2(d))
})
<script src="https://d3js.org/d3.v4.min.js"></script>
Regarding your y-axis problem:
Set the domain of the y scale before calling the axis. So, instead of this:
svg.append("g")
.attr("class", "y axis").call(yAxis);
y.domain([d3.min(data, function(d){
return d.PassengersIn;
}), d3.max(data, function(d){
return d.PassengersIn;
})]);
Change the order:
y.domain([d3.min(data, function(d){
return d.PassengersIn;
}), d3.max(data, function(d){
return d.PassengersIn;
})]);//set the domain first!
svg.append("g")
.attr("class", "y axis").call(yAxis);

D3.js -- How do I update dataset via Javascript?

I have a dataset with 11 different variables (csv file with 12 columns). I want to be able to select a certain column for my scatterplot, but I'm having some difficulties. Please bear with me, as JavaScript is not my strong suit (obviously). Here's what I attempted:
<div class="variables" id="fixedacidity" onclick="drawPlot('fixedacidity');">
<h1>fixed acidity</h1>
</div>
<div class="variables" id="volatileacidity" onclick="drawPlot('volatileacidity');">
<h1>volatile acidity</h1>
</div>
<div class="variables" id="citricacid" onclick="drawPlot('citricacid');">
<h1>citric acid</h1>
</div>
<div class="variables" id="residualsugar" onclick="drawPlot('residualsugar');">
<h1>residual sugar</h1>
</div>
etc ...
I made a simple menu that calls on the drawPlot function, but I'm having trouble trying to get the variable to pass on correctly.
Relevant d3/javascript:
function drawPlot(selectedVar){
$(".visarea").html("");
var wineVar = selectedVar;
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 860 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0])
.domain([0,10]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(10);
var chart1 = d3.select(".visarea").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("red.csv", function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.wineVar = +d.wineVar;
d.quality = +d.quality;
});
x.domain(d3.extent(data, function(d) { return d.wineVar; })).nice();
y.domain([0,10]).nice();
chart1.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(wineVar);
chart1.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("Rated Quality")
chart1.selectAll(".red.dot")
.data(data)
.enter().append("circle")
.attr("class", "red dot")
.attr("r", 3)
.attr("cx", function(d) { return x(d.wineVar); })
.attr("cy", function(d) { return y(d.quality); })
.style("fill", "red");
});
}
Although the variable gets passed on to the function, d.wineVar, as expected, does not return the desired value, and thus the chart does not draw the correct values. Can anyone recommend a workaround for this? It seems so simple, yet I've spent hours failing trying to figure this out.
Sample of red.csv:
fixedacidity,volatileacidity,citricacid,residualsugar,chlorides,freesulfurdioxide,totalsulfurdioxide,density,pH,sulphates,alcohol,quality
7.4,0.7,0,1.9,0.076,11,34,0.9978,3.51,0.56,9.4,5
7.8,0.88,0,2.6,0.098,25,67,0.9968,3.2,0.68,9.8,5
7.8,0.76,0.04,2.3,0.092,15,54,0.997,3.26,0.65,9.8,5
Image of what I'm trying to accomplish. The first dataset, fixedacidity, gets drawn up fine. I'm having difficulties trying to get the menu to correctly show its respective dataset. "Rated Quality" will always be the data for the Y-axis.
You has wrong variable reference, here:
data.forEach(function(d) {
d.wineVar = +d.wineVar; // <---------Here
d.quality = +d.quality;
});
change by:
data.forEach(function(d) {
d.wineVar = +d[wineVar]; // <----------Here
d.quality = +d.quality;
});
There is the obvious issue pointed out by klaujesi about data extraction. But there are more issues with your code.
I would say you need to adapt your approach to the way d3.js works. Currently you will add a new svg on each call to the function, caused by this line in your code: d3.select(".visarea").append("svg")
I usually have some init code wrapped in one function, which creates the svg and sets ups everything static. Then there is an update function which will handle input changes to show different data, use different scales etc.
The nice thing about d3.js is that you can control very easily what's to happen with newly introduced data via .enter() and removed data via .exit().

rotate d3 horizontal bar graph to vertical bar graph [duplicate]

I have a vertical bar chart that is grouped in pairs. I was trying to play around with how to flip it horizontally. In my case, the keywords would appear on the y axis, and the scale would appear on the x-axis.
I tried switching various x/y variables, but that of course just produced funky results. Which areas of my code do I need to focus on in order to switch it from vertical bars to horizontal ones?
My JSFiddle: Full Code
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
// ternary operator to determine if global or local has a larger scale
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function (d) {
return (d.local > d.global) ? d.local : d.global;
})])
.range([h, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(function (d) {
return dataset[d].keyword;
})
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.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 + ")");
// Graph Bars
var sets = svg.selectAll(".set")
.data(dataset)
.enter()
.append("g")
.attr("class", "set")
.attr("transform", function (d, i) {
return "translate(" + xScale(i) + ",0)";
});
sets.append("rect")
.attr("class", "local")
.attr("width", xScale.rangeBand() / 2)
.attr("y", function (d) {
return yScale(d.local);
})
.attr("x", xScale.rangeBand() / 2)
.attr("height", function (d) {
return h - yScale(d.local);
})
.attr("fill", colors[0][1])
;
sets.append("rect")
.attr("class", "global")
.attr("width", xScale.rangeBand() / 2)
.attr("y", function (d) {
return yScale(d.global);
})
.attr("height", function (d) {
return h - yScale(d.global);
})
.attr("fill", colors[1][1])
;
sets.append("rect")
.attr("class", "global")
.attr("width", xScale.rangeBand() / 2)
.attr("y", function (d) {
return yScale(d.global);
})
.attr("height", function (d) {
return h - yScale(d.global);
})
.attr("fill", colors[1][1])
;
I just did the same thing last night, and I basically ended up rewriting the code as it was quicker than fixing all the bugs but here's the tips I can give you.
The biggest issues with flipping the x and y axis will be with things like return h - yScale(d.global) because height is calculated from the "top" of the page not the bottom.
Another key thing to remember is that when you set .attr("x", ..) make sure you set it to 0 (plus any padding for the left side) so = .attr("x", 0)"
I used this tutorial to help me think about my own code in terms of horizontal bars instead - it really helped
http://hdnrnzk.me/2012/07/04/creating-a-bar-graph-using-d3js/
here's my own code making it horizontal if it helps:
var w = 600;
var h = 600;
var padding = 30;
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d){
return d.values[0]; })]) //note I'm using an array here to grab the value hence the [0]
.range([padding, w - (padding*2)]);
var yScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([padding, h- padding], 0.05);
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", 0 + padding)
.attr("y", function(d, i){
return yScale(i);
})
.attr("width", function(d) {
return xScale(d.values[0]);
})
.attr("height", yScale.rangeBand())
An alternative is to rotate the chart (see this). This is a bit hacky as then you need to maintain the swapped axes in your head (the height is actually the width etc), but it is arguably simpler if you already have a working vertical chart.
An example of rotating the chart is below. You might need to rotate the text as well to make it nice.
_chart.select('g').attr("transform","rotate(90 200 200)");
Here is the procedure I use in this case:
1) Inverse all Xs and Ys
2) Remember that the 0 for y is on top, thus you will have to inverse lots of values as previous values for y will be inversed (you don't want your x axis to go from left to right) and the new y axis will be inversed too.
3) Make sure the bars display correctly
4) Adapt legends if there are problems
This question may help in the sense that it shows how to go from horizontal bar charts to vertical: d3.js histogram with positive and negative values

D3js Converting a vertical bar chart to horizontal bar chart [duplicate]

I have a vertical bar chart that is grouped in pairs. I was trying to play around with how to flip it horizontally. In my case, the keywords would appear on the y axis, and the scale would appear on the x-axis.
I tried switching various x/y variables, but that of course just produced funky results. Which areas of my code do I need to focus on in order to switch it from vertical bars to horizontal ones?
My JSFiddle: Full Code
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
// ternary operator to determine if global or local has a larger scale
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function (d) {
return (d.local > d.global) ? d.local : d.global;
})])
.range([h, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(function (d) {
return dataset[d].keyword;
})
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.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 + ")");
// Graph Bars
var sets = svg.selectAll(".set")
.data(dataset)
.enter()
.append("g")
.attr("class", "set")
.attr("transform", function (d, i) {
return "translate(" + xScale(i) + ",0)";
});
sets.append("rect")
.attr("class", "local")
.attr("width", xScale.rangeBand() / 2)
.attr("y", function (d) {
return yScale(d.local);
})
.attr("x", xScale.rangeBand() / 2)
.attr("height", function (d) {
return h - yScale(d.local);
})
.attr("fill", colors[0][1])
;
sets.append("rect")
.attr("class", "global")
.attr("width", xScale.rangeBand() / 2)
.attr("y", function (d) {
return yScale(d.global);
})
.attr("height", function (d) {
return h - yScale(d.global);
})
.attr("fill", colors[1][1])
;
sets.append("rect")
.attr("class", "global")
.attr("width", xScale.rangeBand() / 2)
.attr("y", function (d) {
return yScale(d.global);
})
.attr("height", function (d) {
return h - yScale(d.global);
})
.attr("fill", colors[1][1])
;
I just did the same thing last night, and I basically ended up rewriting the code as it was quicker than fixing all the bugs but here's the tips I can give you.
The biggest issues with flipping the x and y axis will be with things like return h - yScale(d.global) because height is calculated from the "top" of the page not the bottom.
Another key thing to remember is that when you set .attr("x", ..) make sure you set it to 0 (plus any padding for the left side) so = .attr("x", 0)"
I used this tutorial to help me think about my own code in terms of horizontal bars instead - it really helped
http://hdnrnzk.me/2012/07/04/creating-a-bar-graph-using-d3js/
here's my own code making it horizontal if it helps:
var w = 600;
var h = 600;
var padding = 30;
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d){
return d.values[0]; })]) //note I'm using an array here to grab the value hence the [0]
.range([padding, w - (padding*2)]);
var yScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([padding, h- padding], 0.05);
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", 0 + padding)
.attr("y", function(d, i){
return yScale(i);
})
.attr("width", function(d) {
return xScale(d.values[0]);
})
.attr("height", yScale.rangeBand())
An alternative is to rotate the chart (see this). This is a bit hacky as then you need to maintain the swapped axes in your head (the height is actually the width etc), but it is arguably simpler if you already have a working vertical chart.
An example of rotating the chart is below. You might need to rotate the text as well to make it nice.
_chart.select('g').attr("transform","rotate(90 200 200)");
Here is the procedure I use in this case:
1) Inverse all Xs and Ys
2) Remember that the 0 for y is on top, thus you will have to inverse lots of values as previous values for y will be inversed (you don't want your x axis to go from left to right) and the new y axis will be inversed too.
3) Make sure the bars display correctly
4) Adapt legends if there are problems
This question may help in the sense that it shows how to go from horizontal bar charts to vertical: d3.js histogram with positive and negative values

x-axis zooming with d3 line charts

I'm just getting into using d3, and relatively novice in js still. I'm trying to set up a page of log file visualizations for monitoring some servers. Right now I'm focusing on getting a line chart of CPU utilization, where I can focus on specific time periods (So an x-zoom only). I am able to do a static charts easily, but when it comes to the zooming the examples are going over my head and I can't seem to make them work.
This is the static example I followed to get things up and running, and this is the zoom example I've been trying to follow.
My input csv is from a rolling set of log files (which will not be labled on the first row), each row looks like this:
datetime,server,cpu,memory,disk,network_in,network_out
So far what I've been able to get on the zoom looks like this:
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
// define dimensions of graph
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
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")
.tickSize(-height, 0)
.tickPadding(6);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(-width)
.tickPadding(6);
// Define how we will access the information needed from each row
var line = d3.svg.line()
.interpolate("step-after")
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[2]); });
// Insert an svg element into the document for each chart
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 + ")");
// Declare zoom handler
var zoom = d3.behavior.zoom().on("zoom", draw);
// Open the log and extract the information
d3.text("log.csv", function(text) {
var data = d3.csv.parseRows(text).map(function(row) {
return row.map(function(value, index) {
if (index == 0) {
return parseDate(value);
}
else if (index > 1) {
return +value;
}
else {
return value;
}
});
});
// Set the global minimum and maximums
x.domain(d3.extent(data, function(d) { return d[0]; }));
y.domain(d3.extent(data, function(d) { return d[2]; }));
zoom.x(x);
// Finally, we have the data parsed, and the parameters of the charts set, so now we
// fill in the charts with the lines and the labels
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.attr("text-anchor", "end")
.text("Percent (%)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
svg.append("text")
.attr("x", margin.left)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text('all');
svg.append("rect")
.attr("class", "pane")
.attr("width", width)
.attr("height", height)
.call(zoom);
svg.select("path.line").data([data]);
draw();
});
function draw() {
svg.select("g.x.axis").call(xAxis);
svg.select("g.y.axis").call(yAxis);
svg.select("path.line").attr("d", line);
}
What this gives me is a very sluggish chart that can be zoomed and panned, but it does not clip off the line at the ends of the chart. I've tried adding in the clipping elements described in the example, but that ends up fully erasing my line every time.
Thanks for any help or direction

Categories

Resources