pan d3.js graph - d3.js

How would I need to modify the following code, which uses d3, to allow dragging of the graph along the x-axis:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<style type="text/css">
.axis path, .axis line {
fill: none;
stroke: #ddd;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis path
{
stroke: #999;
stroke-width: 2px;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js?2.9.4"></script>
</head>
<body>
<div id="chart" class="background">
</div>
</body>
<script type="text/javascript">
var data = [{
Id: "1",
Year: 1950,
Relevance: 55,
Category: "Cat1",
SpecFreq: 5,
GenFreq: 10
}, {
Id: "2",
Year: 1975,
Relevance: 25,
Category: "Cat1",
SpecFreq: 2,
GenFreq: 31
}, {
Id: "1",
Year: 1990,
Relevance: 75,
Category: "Cat1",
SpecFreq: 8,
GenFreq: 23
}];
$(function () {
DrawFocus(data);
});
function DrawFocus(data) {
//dimensions
var margin = {
top: 5.5,
right: 19.5,
bottom: 39.5,
left: 39.5
},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//data domain extents
var extentX = d3.extent(data, function (d) {
return d.Year;
});
var extentY = d3.extent(data, function (d) {
return d.Relevance;
});
//pad extents to provide some extra "blank" areas around edge of graph
extentX[0] = extentX[0] - 5;
extentX[1] = extentX[1] + 5;
extentY[0] = extentY[0] - 5;
extentY[1] = extentY[1] + 5;
//scales
var x = d3.scale.linear().domain(extentX).range([0, width]);
var y = d3.scale.linear().domain(extentY).range([height, 0]);
var radiusMax = .025 * width;
var radius = d3.scale.sqrt().domain([0, 100]).range([0, radiusMax]);
var color = d3.scale.ordinal().domain(["Cat1", "Cat2", "Cat3"]).range(["#b7b8a0", "#898a72", "#878772"]);
//axes
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.format("d")).tickSize(-height);
var yAxis = d3.svg.axis().scale(y).orient("left").tickSize(-width);
//create and size svg element, add zoom behavior
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 + ")");
// Add the x-axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
// Add the y-axis.
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Add the x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width / 2)
.attr("y", height + 35)
.text(" Year");
// Add the y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", -1 * height / 2)
.attr("y", -40)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Relevance");
//plot genFreq
var gGenerally = svg.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return color(d.Category);
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.GenFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
//plot specFreq
var gWithin = svg.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return "#d6d487";
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.SpecFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
return svg;
}
</script>
</html>
I've found several examples online and realize that I need to use the zoom behavior, but haven't been able to figure out precisely how to do it. There is a fiddle of the above code available.

Applying the zoom function as shown below, together with some additional code to restrict the zooming domain, was all I needed to do to get it to work. I do seem some odd "stickiness" going on once the graph has been scrolled to the far edge in any direction. It sometimes hesitates for a moment before scrolling back. I'm not sure what causes that. Also, this example is a bit unrealistic because I'm trimming my domain to match the min and max values in my data and then adding a +- 5 buffer. So, you can really only scroll that buffered area. However, I think it would behave just the same if you had a larger domain applied.
<html>
<head>
<style type="text/css">
.axis path, .axis line {
fill: none;
stroke: #ddd;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis path
{
stroke: #999;
stroke-width: 2px;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js?2.9.4"></script>
</head>
<body>
<div id="chart" class="background">
</div>
</body>
<script type="text/javascript">
var data = [{
Id: "1",
Year: 1950,
Relevance: 55,
Category: "Cat1",
SpecFreq: 5,
GenFreq: 10
}, {
Id: "2",
Year: 1975,
Relevance: 25,
Category: "Cat1",
SpecFreq: 2,
GenFreq: 31
}, {
Id: "1",
Year: 1990,
Relevance: 75,
Category: "Cat1",
SpecFreq: 8,
GenFreq: 23
}];
$(function () {
DrawFocus(data);
});
function DrawFocus(data) {
//dimensions
var margin = {
top: 5.5,
right: 19.5,
bottom: 39.5,
left: 39.5
},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//data domain extents
var extentX = d3.extent(data, function (d) {
return d.Year;
});
var extentY = d3.extent(data, function (d) {
return d.Relevance;
});
//pad extents to provide some extra "blank" areas around edge of graph
extentX[0] = extentX[0] - 5;
extentX[1] = extentX[1] + 5;
extentY[0] = extentY[0] - 5;
extentY[1] = extentY[1] + 5;
//scales
var x = d3.scale.linear().domain(extentX).range([0, width]);
var y = d3.scale.linear().domain(extentY).range([height, 0]);
var radiusMax = .025 * width;
var radius = d3.scale.sqrt().domain([0, 100]).range([0, radiusMax]);
var color = d3.scale.ordinal().domain(["Cat1", "Cat2", "Cat3"]).range(["#b7b8a0", "#898a72", "#878772"]);
//axes
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.format("d")).tickSize(-height).ticks(10);
var yAxis = d3.svg.axis().scale(y).orient("left").tickSize(-width).ticks(10);
//create and size svg element, add zoom behavior
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom))
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Add the x-axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
// Add the y-axis.
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
// Add the x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width / 2)
.attr("y", height + 35)
.text(" Year");
// Add the y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", -1 * height / 2)
.attr("y", -40)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Relevance");
//plot genFreq
var gGenerally = svg.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return color(d.Category);
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.GenFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
//plot specFreq
var gWithin = svg.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return "#d6d487";
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.SpecFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
function zoom() {
if (x.domain()[0] >= extentX[0] -5 && x.domain()[1] <= extentX[1] +5 && y.domain()[0] >= extentY[0] -5 && y.domain()[1] <= extentY[1] +5) {
svg.select(".x.axis").call(xAxis).selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
svg.select(".y.axis").call(yAxis).selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
svg.selectAll("circle").attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
}
return svg;
}
</script>
</html>

Related

D3.js scope issue with external data.json file and brush

Having trouble using an external file data.json for use on a bar chart with a brush. This seems to be a scope issue.
I am using,
d3.json("data.json", function(error, data) {...});
to read in the json file. Inside this main chart and brush chart are drawn. And there is a call to the variable 'brush', which is outside of d3.json() and in that var there is a call to the brush function which is also outside of d3.json(). The 'data' cannot be read inside the brush function. I tried passing data as a parameter, brush(data) but that doesn't work.
The error I get is,
Uncaught ReferenceError: data is not defined
This error refers to line 226 which is in the brush function and looks like this,
.data(data.filter(function(d) {....
and is the first use of 'data' in the brush function.
This is set up the same as Bostock's example Brush & Zoom
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
body {
font-family: avenir next, sans-serif;
font-size: 12px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
.axis {
stroke-width: 0.5px;
stroke: #888;
font: 10px avenir next, sans-serif;
}
.axis>path {
stroke: #888;
}
</style>
<body>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var margin = {
top: 20,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 230,
right: 20,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
height2 = 300 - margin2.top - margin2.bottom;
var x = d3.scaleBand().range([0, width]).padding(0.1),
x2 = d3.scaleBand().range([0, width]).padding(0.1),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x).tickSize(0),
xAxis2 = d3.axisBottom(x2).tickSize(0),
yAxis = d3.axisLeft(y).tickSize(0);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
// .on("start brush end", brushed);
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, 2])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
focus.append("text") // yAxis label
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Distance in meters");
svg.append("text") // xAxis label
.attr("transform",
"translate(" + ((width + margin.right + margin.left) / 2) + " ," +
(height + margin.top + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Date");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom); //see var zoom above
focus.append("g") //append xAxis to main chart
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3.json("data.json", function(error, data) {
if (error) throw error;
focus.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.distance;
})])
.range([height, 0])).tickSize(0));
data.forEach(function(d) {
d.distance = +d.distance;
return d;
},
function(error, data) {
if (error) throw error;
});
x.domain(data.map(function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.distance;
})]);
x2.domain(x.domain());
y2.domain(y.domain());
//********* Main ar Chart ****************
var rects = focus.append("g");
rects.attr("clip-path", "url(#clip)");
rects.selectAll("rects")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return "lightblue";
})
.style('stroke', 'gray')
.attr("class", "rects")
.attr("x", function(d) {
return x(d.date);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height - y(d.distance);
});
// //********* Brush Bar Chart ****************
var rects = context.append("g"); //draw bar chart in brush
rects.attr("clip-path", "url(#clip)");
rects.selectAll("rect")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return "lightblue";
})
.style('stroke', 'gray')
.attr("class", "rectss")
.attr("x", function(d) {
return x2(d.date);
})
.attr("y", function(d) {
return y2(d.distance);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height2 - y2(d.distance);
});
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
}); //closes d3.json()
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
// get bounds of selection
var s = d3.event.selection,
nD = [];
x2.domain().forEach((d) => { //not as smooth as I'd like it
var pos = x2(d) + x2.bandwidth() / 2;
if (pos > s[0] && pos < s[1]) {
nD.push(d);
}
});
x.domain(nD);
focus.selectAll(".rects")
.remove().exit()
.data(data.filter(function(d) {
return nD.indexOf(d.date) > -1
}))
.enter().append("rect")
.style("fill", function(d) {
return "lightblue";
})
.style('stroke', 'gray')
.attr("class", "rects")
.attr("x", function(d) {
return x(d.date);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height - y(d.distance);
});
focus.select(".x-axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
};
function zoomed() {};
</script>
A small part of the data.json
[{
"date": "A",
"distance": "1100"
},
{
"date": "B",
"distance": "1500"
},
{
"date": "C",
"distance": "2000"
},
{
"date": "D",
"distance": "2500"
},
{
"date": "E",
"distance": "1975"
},
{
"date": "F",
"distance": "3000"
},
{
"date": "G",
"distance": "2100"
},
{
"date": "H",
"distance": "2100"
},
{
"date": "I",
"distance": "3300"
},
{
"date": "J",
"distance": "2000"
}
]
Any help would be greatly appreciated.
Thanks,
Addendum:
Why does this not work?
var data = null;
d3.json("data.json", function(error, data) {
if (error) throw error;
.....
)}
and this does work,
var data = null;
d3.json("data.json", function(error, newdata) {
if (error) throw error;
data = newdata;
.....});
Indeed you have an issue with your scope. The variable data is defined inside a function, so it can't be available out of this function. Here is the code to make it works :
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
body {
font-family: avenir next, sans-serif;
font-size: 12px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
.axis {
stroke-width: 0.5px;
stroke: #888;
font: 10px avenir next, sans-serif;
}
.axis>path {
stroke: #888;
}
</style>
<body>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var margin = {
top: 20,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 230,
right: 20,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
height2 = 300 - margin2.top - margin2.bottom;
var x = d3.scaleBand().range([0, width]).padding(0.1),
x2 = d3.scaleBand().range([0, width]).padding(0.1),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x).tickSize(0),
xAxis2 = d3.axisBottom(x2).tickSize(0),
yAxis = d3.axisLeft(y).tickSize(0);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
// .on("start brush end", brushed);
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, 2])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
focus.append("text") // yAxis label
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Distance in meters");
svg.append("text") // xAxis label
.attr("transform",
"translate(" + ((width + margin.right + margin.left) / 2) + " ," +
(height + margin.top + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Date");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom); //see var zoom above
focus.append("g") //append xAxis to main chart
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var data_global=null;
d3.json("data.json", function(error, data) {
if (error) throw error;
data_global=data;
focus.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.distance;
})])
.range([height, 0])).tickSize(0));
data.forEach(function(d) {
d.distance = +d.distance;
return d;
},
function(error, data) {
if (error) throw error;
});
x.domain(data.map(function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.distance;
})]);
x2.domain(x.domain());
y2.domain(y.domain());
//********* Main ar Chart ****************
var rects = focus.append("g");
rects.attr("clip-path", "url(#clip)");
rects.selectAll("rects")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return "lightblue";
})
.style('stroke', 'gray')
.attr("class", "rects")
.attr("x", function(d) {
return x(d.date);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height - y(d.distance);
});
// //********* Brush Bar Chart ****************
var rects = context.append("g"); //draw bar chart in brush
rects.attr("clip-path", "url(#clip)");
rects.selectAll("rect")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return "lightblue";
})
.style('stroke', 'gray')
.attr("class", "rectss")
.attr("x", function(d) {
return x2(d.date);
})
.attr("y", function(d) {
return y2(d.distance);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height2 - y2(d.distance);
});
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
}); //closes d3.json()
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
// get bounds of selection
var s = d3.event.selection,
nD = [];
x2.domain().forEach((d) => { //not as smooth as I'd like it
var pos = x2(d) + x2.bandwidth() / 2;
if (pos > s[0] && pos < s[1]) {
nD.push(d);
}
});
x.domain(nD);
focus.selectAll(".rects")
.remove().exit()
.data(data_global.filter(function(d) {
return nD.indexOf(d.date) > -1
}))
.enter().append("rect")
.style("fill", function(d) {
return "lightblue";
})
.style('stroke', 'gray')
.attr("class", "rects")
.attr("x", function(d) {
return x(d.date);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height - y(d.distance);
});
focus.select(".x-axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
};
function zoomed() {};
</script>

Finding the mean of bars in bar chart - d3

I am new to d3 and am trying to draw mean line through all the bars in the bar chart but not quite able to achieve that. Below is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: #444;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script>
var data=[
{"letter": "BU", "higher": .08,"lower": .05},
{"letter": "AU", "higher": .05,"lower": .03},
{"letter": "LA", "higher": .04,"lower": .02}
]
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x2 = d3.scale.ordinal()
.rangeBands([0, width], 0);
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("left")
.tickFormat(formatPercent);
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 + ")");
data.forEach(function(d) {
d.higher = +d.higher;
d.lower = +d.lower;
});
x.domain(data.map(function(d) { return d.letter; }));
x2.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.higher; })]);
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")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.higher); })
.attr("height", function(d) { return height - y(d.higher-d.lower); });
var dataSum = d3.mean(data, function(d) { return (d.higher + d.lower); });
var line = d3.svg.line()
.x(function(d, i) {
return x(d.letter) + i; })
.y(function(d, i) { return y(dataSum/data.length); });
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
</script>
</body>
</html>
The proper code is here'http://jsbin.com/beyakumohi/1/edit?html' . The line should pass through center of each bars but it only happens for the 3rd bar and that too it do not cross it. Is there any mistake in my code.
d3.mean will give you the mean value of the full array. Meaning, that in this case you will get the mean of:
data[0].higher + data[0].lower + data[1].higher + data[1].lower + data[2].higher + data[2].lower
In this case I would say it is more appropiate to edit your line function as following
var line = d3.svg.line()
.x(function(d, i) {
return x(d.letter); })
.y(function(d, i) { return y((d.higher + d.lower) / 2); });

Link in D3 bar chart

I am creating a D3 bar chart using below code .
<head>
<style>
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
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("left")
.ticks(10);
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 + ")");
var data = [{"text":"A","value":50,"linkurl":"http://google.com"}, {"text":"B","value":100,"linkurl":"http://php.net"},{"text":"C","value":150,"linkurl":"http://drupal.org"}]
data.forEach(function(d) {
d.text = d.text;
d.value = +d.value;
d.linkurl = d.linkurl;
});
x.domain(data.map(function(d) { return d.text; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
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(-45)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.text); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
svg.on("click", function (d) { //<-D
//var position = d3.mouse(svg.node());
window.location.href='http://google.com';
//window.location.href=d.linkurl;
});
</script>
</body>
And want to create a link on each bar.. for that I am using the code . It is working fine when I am using same URL for each bar but doesn't
for different URl for each bar...
var data = [{"text":"A","value":50,"linkurl":"http://google.com"},{"text":"B","value":100,"linkurl":"http://php.net"},{"text":"C","value":150,"linkurl":"http://drupal.org"}]
data.forEach(function(d) {
d.text = d.text;
d.value = +d.value;
d.linkurl = d.linkurl;
});
svg.on("click", function (d) { //<-D
//var position = d3.mouse(svg.node());
window.location.href='http://google.com';
//window.location.href=d.linkurl;
});
You have to bind the click listener to the bars (rectangles) instead of svg.
var bars = svg.selectAll("bar")
.data(data)
.enter()
.append("rect")
.style("fill", "steelblue")
.attr("x", function(d) {
return x(d.text);
}).attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.value);
}).attr("height", function(d) {
return height - y(d.value);
});
bars.on("click", function(d) {
window.location.href = d.linkurl;
});
var margin = {
top: 20,
right: 20,
bottom: 70,
left: 40
}, width = 600 - margin.left - margin.right, height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([ 0, width ], .05);
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("left").ticks(10);
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 + ")");
var data = [ {
text: "A",
value: 50,
linkurl: "http://google.com"
}, {
text: "B",
value: 100,
linkurl: "http://php.net"
}, {
text: "C",
value: 150,
linkurl: "http://drupal.org"
} ];
data.forEach(function(a) {
a.text = a.text;
a.value = +a.value;
a.linkurl = a.linkurl;
});
x.domain(data.map(function(a) {
return a.text;
}));
y.domain([ 0, d3.max(data, function(a) {
return a.value;
}) ]);
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(-45)");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("");
var bars = svg.selectAll("bar")
.data(data)
.enter()
.append("rect")
.style("fill", "steelblue")
.attr("x", function(a) {
return x(a.text);
}).attr("width", x.rangeBand())
.attr("y", function(a) {
return y(a.value);
}).attr("height", function(a) {
return height - y(a.value);
});
bars.on("click", function(a) {
window.location.href = a.linkurl;
});
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Transitions in D3 Axis

I'm trying to create a grouped bar chart that can include transitions. I referenced Mike Bostock's grouped bar chart example and have the bar chart transition working but can't get the axis to transition as well. What I'm referring to in this example is the state labels.
I tried following another example posted but I'm baffled as to why I can get it to work. For my example code the transition occurs on click of the SVG
Grouped Bar Chart Reference Example
Axis Reference Example
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x_axis path {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
//Update the flips bars but not the axis
function update() {
var data_update = data.reverse()
x0.domain[data_update]
svg.selectAll(".state")
.data(data_update)
.transition()
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
svg.select(".x_axis")
.transition()
.call(xAxis);
}
d3.select("svg")
.on("click", update)
//The rest of the code
var data
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
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 + ")");
d3.csv("data.csv", function(error, csv) {
data = csv
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x_axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "state")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
});
</script>
I'm a dummy and figured it out.
Calling the .domain method on an array of objects is useless. I forgot to map the object array to get the value of the states
x0.domain(data_update.map(function(d) { return d.State; }))
Full Code below for those curious
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x_axis path {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var data
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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 + ")");
d3.csv("data.csv", function(error, csv) {
data = csv
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, function(d) { return d.value; }); })]);
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")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data, function(d) {return d.State})
.enter().append("g")
.attr("class", "state")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
//Update the flips bars
function update() {
data.reverse()
x0.domain(data.map(function(d) { return d.State; }))
svg.selectAll(".state")
.data(data, function(d) {return d.State})
.transition()
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
svg.select(".x_axis")
.transition()
.call(xAxis);
}
d3.select("svg")
.on("click", update)
</script>

d3.js adding id's to the stacked bar chart

I have created a stacked bar chart which depicts scanned and unscanned items as bars and also added the tooltip to represent the values of each stack when mouse is moved over. However when i move on a stack i would like to show tooltip like "UnScanned - 57 items" and when i move the mouse over the lower bar it should display "Scanned - 50" . I just need to differentiate the bar whether it is scanned or unscanned along with the data values displayed.
My code goes like:
<!DOCTYPE html>
<html>
<head>
<title>Scanned vs Non Scanned Data</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="../../js/Core_functions.js"></script>
<script type="text/javascript" src="../../js/graphic_functions.js"></script>
<style type="text/css">
svg {
width: 960px;
height: 500px;
border: solid 1px #ccc;
font: 10px sans-serif;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="container">
<h1>Mockup of Component Scanned Mapping DV</h1>
</div>
<script type="text/javascript">
var w = 960,
h = 500,
p = [20, 50, 30, 20],
x = d3.scale.ordinal().rangeRoundBands([0, w - p[1] - p[3]]),
y = d3.scale.linear().range([0, h - p[0] - p[2]]),
z = d3.scale.ordinal().range(["#819FF7", "#CB491A"]),
parse = d3.time.format("%m/%Y").parse,
format = d3.time.format("%b-%y");
/*var yAxis = d3.svg.axis()
.scale(y)
.ticks(12)
.orient("left");*/
var svg = d3.select("#container").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + p[3] + "," + (h - p[2]) + ")");
d3.csv("scandata.csv", function(scan) {
// Transpose the data into layers by cause.
var scantypes = d3.layout.stack()(["scanned", "unscanned"].map(function(scans) {
return scan.map(function(d) {
return {x: parse(d.date), y: +d[scans]};
});
}));
// Compute the x-domain (by date) and y-domain (by top).
x.domain(scantypes [0].map(function(d) { return d.x; }));
y.domain([0, d3.max(scantypes[scantypes .length - 1], function(d) { return d.y0 + d.y; })]);
// Add a group for each column.
var cause = svg.selectAll("g.scan")
.data(scantypes)
.enter().append("svg:g")
.attr("class", "scan")
.style("fill", function(d, i) { return z(i); })
.style("stroke", function(d, i) { return d3.rgb(z(i)).darker(); });
// Add a rect for each date.
var rect = cause.selectAll("rect")
.data(Object)
.enter().append("svg:rect")
.attr("x", function(d,i) {
if (i ==0)
{
return x(d.x)+ 10 ;
}
else
{
return x(d.x);
}} )
.attr("y", function(d) { return -y(d.y0) - y(d.y); })
.attr("height", function(d) { return y(d.y); })
.attr("width", x.rangeBand()/2)
.on("mouseover", function(d){
return tooltip.style("visibility", "visible")
.text((d.y))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 20) + "px"); ;})
.on("mousemove", function(d){
return tooltip.style("visibility", "visible")
.text((d.y))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 20) + "px"); ;})
.on("mouseout", function(d){return tooltip.style("visibility", "hidden");})
.on("click", function(d){console.log(d);});
var tooltip = d3.select("#container")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "visible")
.text("Scanned vs UnScanned")
.style("font", "Arial")
.style("font-size", "14px");
// Add a label per date.
var label = svg.selectAll("text")
.data(x.domain())
.enter().append("svg:text")
.attr("x", function(d) { return x(d) + x.rangeBand() / 3; })
.attr("y", 6)
.attr("text-anchor", "middle")
.attr("dy", ".71em")
.text(format);
// Add y-axis rules.
var rule = svg.selectAll("g.rule")
.data(y.ticks(5))
.enter().append("svg:g")
.attr("class", "rule")
.attr("transform", function(d) { return "translate(0," + -y(d) + ")"; });
rule.append("svg:line")
.attr("x2", w - p[1] - p[3])
.style("stroke", function(d) { return d ? "#fff" : "#000"; })
.style("stroke-opacity", function(d) { return d ? .7 : null; });
rule.append("svg:text")
.attr("x", -15)
.style("font-family","Arial 12px")
.attr("dy", ".25em")
.text(d3.format(",d"));
});
</script>
</body>
</html> </script>
</body>
</html>
My csv data :
date,scanned,unscanned
01/2014,10,90
02/2014,55,40
03/2014,45,23
04/2014,65,35
05/2014,100,20
06/2014,50,30
07/2014,10,90
08/2014,22,48
09/2014,0,100
10/2014,3,97
11/2014,22,60
12/2014,57,37
You could make this part of the data that you make for the chart:
var scantypes = d3.layout.stack()(["scanned", "unscanned"].map(function(scans) {
return scan.map(function(d) {
return {x: parse(d.date), y: +d[scans], type: scans};
});
}));
// more code...
.on("mouseover", function(d){
return tooltip.text(d.type + " - " + d.y);
});

Resources