This is a d3 bar chart. line 162 ,
.on("mouseover", console.log("I am on it"))
should only happen when user is over a bar on the bar chart, but the log is output from the time the page loads.
The running code is here, https://shanegibney.github.io/d3Mouseover/
The full code is here, https://github.com/shanegibney/d3Mouseover
<!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>
<div id="totalDistance">
</div>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
/* Adapted from: https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172 */
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 parseTime = d3.timeParse("%Y-%m-%d %H:%M");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]),
dur = d3.scaleLinear().range([0, 12]);
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);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.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 + ")");
d3.json("dataDefault.json", function(error, data) {
if (error) throw error;
var parseTime = d3.timeParse("%Y-%m-%d %H:%M");
var mouseoverTime = d3.timeFormat("%a %e %b %Y %H:%M");
var minTime = d3.timeFormat("%b%e, %Y");
var parseDate = d3.timeParse("%b %Y");
data.forEach(function(d) {
d.mouseoverDisplay = parseTime(d.date);
d.date = parseTime(d.date);
d.end = parseTime(d.end);
d.duration = ((d.end - d.date) / (60 * 1000)); // session duration in minutes
d.distance = +d.distance;
d.intensityInverted = (1 / (d.distance / d.duration)); // inverse of intensity so that the light colour is for low intensity and dark colour is for high intensity
d.intensity = Math.round(d.distance / d.duration); // actually intensity, metres per minute.
d.course = d.course.toLowerCase();
return d;
},
function(error, data) {
if (error) throw error;
});
var total = 0;
data.forEach(function(d) {
total = d.distance + total;
});
var minDate = d3.min(data, function(d) {
return d.date;
});
total = String(total).replace(/(.)(?=(\d{3})+$)/g, '$1,') //place thousands comma in total distance string
var xMin = d3.min(data, function(d) {
return d.date;
});
var yMax = Math.max(20, d3.max(data, function(d) {
return d.distance;
}));
dur.domain([0, d3.max(data, function(d) {
return d.duration;
})]);
var colorScale = d3.scaleSequential(d3.interpolateInferno)
.domain([0, d3.max(data, function(d) {
return d.intensityInverted;
})]);
var totalDistance = d3.select("#totalDistance").append("p").text("Total distance: " + total + "m");
x.domain([xMin, new Date()]);
y.domain([0, yMax]);
x2.domain(x.domain());
y2.domain(y.domain());
// append scatter plot to main chart area
var messages = focus.append("g");
messages.attr("clip-path", "url(#clip)");
messages.selectAll("message")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return colorScale(d.intensityInverted);
})
.attr("class", "message")
.attr("x", function(d) {
return x(d.date);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", function(d) {
return dur(d.duration);
})
.attr("height", function(d) {
return height - y(d.distance);
})
// .on("mouseover", onit);
.on("mouseover", () => console.log("I am now on it for sure!"));
// function onit(d) {
// console.log("I am on it now!")
// }
focus.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
// Summary Stats
focus.append("text")
.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");
// focus.append("text")
// .attr("x", width - margin.right)
// .attr("dy", "1em")
// .attr("text-anchor", "end")
// // .text("Messages: " + num_messages(data, x));
// .text("Total distance: " + total + "m");
svg.append("text")
.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);
// append scatter plot to brush chart area
var messages = context.append("g");
messages.attr("clip-path", "url(#clip)");
messages.selectAll("message")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return colorScale(d.intensityInverted);
})
.attr("class", "message")
.attr("x", function(d) {
return x2(d.date);
})
.attr("y", function(d) {
return y2(d.distance);
})
.attr("width", function(d) {
return dur(d.duration);
})
.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());
});
//create brush function redraw scatterplot with selection
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.selectAll(".message")
.attr("x", function(d) {
return x(d.date);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", function(d) {
return dur(d.duration);
})
.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));
var e = d3.event.selection;
var selectedMessages = focus.selectAll('.message').filter(function() {
var xValue = this.getAttribute('x');
return e[0] <= xValue && xValue <= e[1];
});
// console.log(selectedMessages.nodes().length);
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".message")
// .enter().append("rect")
// .style("fill", function(d) {
// return colorScale(d.intensityInverted);
// })
// .attr("class", "message")
.attr("x", function(d) {
return x(d.date);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", function(d) {
return dur(d.duration);
})
.attr("height", function(d) {
return height - y(d.distance);
});
focus.select(".x-axis").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
function handleMouseOver(d) {
d3.select(this)
.style("fill", "lightBlue");
g.select('text')
.attr("x", 15)
.attr("y", 5)
.text("Session no. " + d.number)
.append('tspan')
.text("Date: " + mouseoverTime(d.mouseoverDisplay))
.attr("x", 15)
.attr("y", 30)
.append('tspan')
.text("Distance: " + d.distance + "m")
.attr("x", 15)
.attr("y", 50)
.append('tspan')
.text("Duration: " + d.duration + " mins")
.attr("x", 15)
.attr("y", 70)
.append('tspan')
.text("Intensity: " + d.intensity + " meters/mins")
.attr("x", 15)
.attr("y", 90)
.append('tspan')
.text("Pool: " + d.pool + " (" + d.course + ")")
.attr("x", 15)
.attr("y", 110);
console.log("handleMouseOver function");
}
function handleMouseOut(d) {
d3.select(this)
.style("fill", function(d) {
return colorScale(d.intensityInverted);
});
g.select('text').text("Total distance since " + minTime(minDate) + ": " + total + "m");
}
</script>
Sample data,
[{
"number": "1",
"date": "2016-11-09 11:15",
"end": "2016-11-09 11:45",
"distance": "1100",
"course": "LC",
"pool": "UCD"
},
{
"number": "2",
"date": "2016-11-10 10:40",
"end": "2016-11-10 11:20",
"distance": "1500",
"course": "LC",
"pool": "UCD"
},
{
"number": "3",
"date": "2016-11-11 16:45",
"end": "2016-11-11 17:50",
"distance": "2000",
"course": "LC",
"pool": "UCD"
},
{
"number": "4",
"date": "2016-11-12 12:48",
"end": "2016-11-12 13:53",
"distance": "2500",
"course": "LC",
"pool": "UCD"
}
]
I added the last two lines here, but still no luck,
var messages = focus.append("g");
messages.attr("clip-path", "url(#clip)");
messages.selectAll("message")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return colorScale(d.intensityInverted);
})
.attr("class", "message")
.attr("x", function(d) {
return x(d.date);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", function(d) {
return dur(d.duration);
})
.attr("height", function(d) {
return height - y(d.distance);
})
// .on("mouseover", onit);
.on("mouseover", () => console.log("I am now on it for sure!"));
// function onit(d) {
// console.log("I am on it now!")
// }
I suspect the problem is because I am using brushX() and zoom.
When you do this:
.on("mouseover", console.log("I am on it"))
You're passing the result of the console.log function to the callback. Instead of that, you want to pass its reference:
.on("mouseover", function(){
console.log("I am on it")
})
Check this snippet (don't hover over the circle!):
var circle = d3.select("circle");
circle.on("mouseover", console.log("I'm on it!"));
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg>
<circle cx="50" cy="50" r="15" fill="teal"></circle>
</svg>
Now the same code, with the reference to console.log:
var circle = d3.select("circle");
circle.on("mouseover", () => console.log("I'm on it!"));
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg>
<circle cx="50" cy="50" r="15" fill="teal"></circle>
</svg>
The issue is that you are calling the function at the time you are declaring it:
.on("mouseover", console.log("I am on it")) //function call
should be something like this:
.on("mouseover", console.log) //function ref
.on("mouseover", function(d) { console.log("I am on it") }) //function ref
Related
I am trying to draw a multi line chart with D3.js. The chart is drawn properly but the x-axis ticks are not aligned correctly with the data points circle.
Find the example code here: https://jsfiddle.net/gopal31795/qsLd7pc5/9/
I assume that there is some error in the code for creating the dots.
// Creating Dots on line
segment.selectAll("dot")
.data(function(d) {
return d.linedata;
})
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) {
//return x(parseDate(new Date(d.mins))) + x.rangeBand() / 2;
return x(d.mins);
})
.attr("cy", function(d) {
return y(d.value);
})
.style("stroke", "white")
.style("fill", function(d) {
return color(this.parentNode.__data__.name);
})
.on("mouseenter", function(d) {
d3.select(this).transition().style("opacity", "0.25");
tooltip.html("<span style='color:" + color(this.parentNode.__data__.name) + ";'>" + this.parentNode.__data__.name + "</span>: " + (d.value + "s")).style("visibility", "visible").style("top", (event.pageY + 10) + "px").style("left", (event.pageX) + "px");
})
.on("mouseleave", function(d) {
d3.select(this).transition().style("opacity", "1");
tooltip.style("visibility", "hidden");
});
The problem in your code (which uses the old v3) is that you're using rangeRoundBands. That would be the correct choice if you had a bar chart, for instance.
However, since you're dealing with data points, you should use rangePoints or rangeRoundPoints, which:
Sets the output range from the specified continuous interval. The array interval contains two elements representing the minimum and maximum numeric value. This interval is subdivided into n evenly-spaced points, where n is the number of (unique) values in the input domain.
So, it should be:
var x = d3.scale.ordinal()
.rangeRoundPoints([0, width]);
Here is your code with that change:
var Data = [{
"name": "R",
"linedata": [{
"mins": 0,
"value": 1120
},
{
"mins": 2,
"value": 1040
},
{
"mins": 4,
"value": 1400
},
{
"mins": 6,
"value": 1500
}
]
},
{
"name": "E",
"linedata": [{
"mins": 0,
"value": 1220
},
{
"mins": 2,
"value": 1500
},
{
"mins": 4,
"value": 1610
},
{
"mins": 6,
"value": 1700
}
]
}
];
var margin = {
top: 20,
right: 90,
bottom: 35,
left: 90
},
width = $("#lineChart").width() - margin.left - margin.right,
height = $("#lineChart").width() * 0.3745 - margin.top - margin.bottom;
//var parseDate = d3.time.format("%d-%b");
var x = d3.scale.ordinal()
.rangeRoundPoints([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
var xData = Data[0].linedata.map(function(d) {
return d.mins;
});
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) {
return x(d.mins);
})
.y(function(d) {
return y(d.value);
});
function transition(path) {
path.transition()
.duration(4000)
.attrTween("stroke-dasharray", tweenDash);
}
function tweenDash() {
var l = this.getTotalLength(),
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) {
return i(t);
};
}
var svg = d3.select("#lineChart").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 + ")");
color.domain(Data.map(function(d) {
return d.name;
}));
x.domain(xData);
var valueMax = d3.max(Data, function(r) {
return d3.max(r.linedata, function(d) {
return d.value;
})
});
var valueMin = d3.min(Data, function(r) {
return d3.min(r.linedata, function(d) {
return d.value;
})
});
y.domain([valueMin, valueMax]);
//Drawing X Axis
svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "xAxisText")
.attr("x", width)
.attr("dy", "-.41em")
.style("text-anchor", "end")
.style("fill", "white")
.text("Time(m)");
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")
.style("fill", "none")
.style("stroke", "white")
.style("stroke-width", 0.8)
.text("Duration(s)");
// Drawing Lines for each segments
var segment = svg.selectAll(".segment")
.data(Data)
.enter().append("g")
.attr("class", "segment");
segment.append("path")
.attr("class", "line")
.attr("id", function(d) {
return d.name;
})
.attr("visible", 1)
.call(transition)
//.delay(750)
//.duration(1000)
//.ease('linear')
.attr("d", function(d) {
return line(d.linedata);
})
.style("stroke", function(d) {
return color(d.name);
});
// Creating Dots on line
segment.selectAll("dot")
.data(function(d) {
return d.linedata;
})
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) {
//return x(parseDate(new Date(d.mins))) + x.rangeBand() / 2;
return x(d.mins);
})
.attr("cy", function(d) {
return y(d.value);
})
.style("stroke", "white")
.style("fill", function(d) {
return color(this.parentNode.__data__.name);
})
.on("mouseenter", function(d) {
d3.select(this).transition().style("opacity", "0.25");
tooltip.html("<span style='color:" + color(this.parentNode.__data__.name) + ";'>" + this.parentNode.__data__.name + "</span>: " + (d.value + "s")).style("visibility", "visible").style("top", (event.pageY + 10) + "px").style("left", (event.pageX) + "px");
})
.on("mouseleave", function(d) {
d3.select(this).transition().style("opacity", "1");
tooltip.style("visibility", "hidden");
});
path {
fill: none;
stroke: black;
}
line {
stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div class="card-body" id="lineChart">
</div>
Finally, as a tip: don't mix D3 and jQuery.
var drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault;
start_x_old = +d3.event.x;
}
function dragged(e) {
xDrag.attr("transform", "translate(" + [( d3.event.x - start_x_old), 0] + ")");
xDrag1.attr("transform", "translate(" + [(d3.event.x - start_x_old), height] + ")");
}
function dragended(d) {
start_x_new = event.clientX;
console.log("old`enter code here`", start_x_old);
console.log("new", start_x_new);
//for future use
}`
This is the code that i wrote for drag where xDrag and xDrag1 are elements on which drag is being called. For the entire working code visit the fiddle https://jsfiddle.net/zaarkoevilor/vzqu1wrc/15/
You don't need to play around with start_x_old and start_x_new...
Just set a datum to your selections:
var xDrag = svg.selectAll(".serie")
.datum({x: 0})
.call(drag);
var xDrag1 = svg.selectAll(".axis--x")
.datum({x: 0})
.call(drag);
And use that in your drag:
function dragged(d) {
xDrag.attr("transform", "translate(" + [(d.x = d3.event.x), 0] + ")");
xDrag1.attr("transform", "translate(" + [(d.x = d3.event.x), height] + ")");
}
Here is your updated fiddle: https://jsfiddle.net/z0d8xpjg/
And the same code in the Stack snippet:
var start_x_old, start_x_new = 0,
x_diff, xtranslated,
svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = [{
labelName: "Financial Data",
no: 27,
yes: 10
}, {
labelName: "Production & completion data",
no: 24,
yes: 32
}, {
labelName: "Support Center Data",
no: 18,
yes: 21
}];
/*var drag = d3.drag()
.on('start.interrupt', function () {
xDrag.interrupt();
xDrag1.interrupt();
})
.on('start drag', function () {
xDrag.attr("transform", "translate(" + (d3.event.x) + "," + 0 +")")
xDrag1.attr("transform", "translate(" + (d3.event.x) + "," + height +")")
});*/
var drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault;
}
function dragged(d) {
//console.log("dragging");
//x_diff = start_x_old - start_x_new;
xDrag.attr("transform", "translate(" + [(d.x = d3.event.x), 0] + ")");
xDrag1.attr("transform", "translate(" + [(d.x = d3.event.x), height] + ")");
}
function dragended(d) {
xDrag.datum({
x: d3.event.x
});
xDrag1.datum({
x: d3.event.x
});
}
data.sort(function(a, b) {
return (b.yes + b.no) - (a.yes + a.no);
});
var x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(['#42c8c5', '#d05a59']);
var stack = d3.stack();
data.sort(function(a, b) {
return (b.yes + b.no) - (a.yes + a.no);
});
x.domain(data.map(function(d) {
return d.labelName;
}));
y.domain([0, d3.max(data, function(d) {
return (d.yes + d.no);
})]).nice();
z.domain(Object.keys(data[0]).slice(1));
g.selectAll(".serie")
.data(stack.keys(Object.keys(data[0]).slice(1))(data))
.enter().append("g")
.attr("class", "serie")
.attr("fill", function(d) {
return z(d.key);
})
.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("x", function(d) {
return x(d.data.labelName);
})
.attr("y", function(d) {
return y(d[1]);
})
.attr("height", function(d) {
return y(d[0]) - y(d[1]);
})
.attr("width", x.bandwidth());
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("transform", "rotate(-60)");
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks(10).pop()))
.attr("dy", "0.35em")
.attr("text-anchor", "start")
.attr("fill", "#000");
var xDrag = svg.selectAll(".serie")
.datum({
x: 0
})
.call(drag);
var xDrag1 = svg.selectAll(".axis--x")
.datum({
x: 0
})
.call(drag);
.bar {
fill: steelblue;
}
.axis path {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="700" height="500"></svg>
Note In this question, I asked how to add a distribution line to the chart.
This is my current status:
I don’t quite understand how to plot two distributions curves for the histograms. Like this:
This is my code (using D3.js version 4):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
.bar1 rect {
fill: rgba(0,0,255,0.6);
}
.bar1:hover rect{
fill: rgba(0,0,255,0.9);
}
.bar1 text {
fill: #fff;
font: 10px sans-serif;
}
.bar2 rect {
fill: rgba(255,0,0,0.6);
}
.bar2:hover rect{
fill: rgba(255,0,0,0.9);
}
.bar2 text {
fill: #fff;
font: 10px sans-serif;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
function draw(data) {
var allCongruentData = data.map(function(e){ return e.Congruent;});
var allIncongruentData = data.map(function(e){ return e.Incongruent;});
var formatCount = d3.format(",.0f");
var svg = d3.select("svg"),
margin = {top: 10, right: 30, bottom: 30, left: 30},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleLinear()
.rangeRound([0, width])
.domain([8, 36]);
var bins1 = d3.histogram()
.domain(x.domain())
.thresholds(x.ticks(40))
(allCongruentData);
// var curve = d3.line()
// .x(function(d) { return ???; })
// .y(function(d) { return y(d.Congruent); })
// .curve(d3.curveCatmullRom.alpha(0.5));
var bins2 = d3.histogram()
.domain(x.domain())
.thresholds(x.ticks(40))
(allIncongruentData);
var y = d3.scaleLinear()
.domain([0, d3.max(bins1, function(d) { return d.length; })])
.range([height, 0]);
var bar1 = g.selectAll(".bar1")
.data(bins1)
.enter().append("g")
.attr("class", "bar1")
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });
bar1.append("rect")
.attr("x", 0.5)
.attr("width", x(bins1[0].x1) - x(bins1[0].x0) - 1)
.attr("height", function(d) { return height - y(d.length); });
var bar2 = g.selectAll(".bar2")
.data(bins2)
.enter().append("g")
.attr("class", "bar2")
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });
bar2.append("rect")
.attr("x", 0.5)
.attr("width", x(bins2[0].x1) - x(bins2[0].x0) - 1)
.attr("height", function(d) { return height - y(d.length); });
bar1.append("text")
.attr("dy", ".75em") // why?
.attr("y", 6)
.attr("x", (x(bins1[0].x1) - x(bins1[0].x0)) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.length); });
bar2.append("text")
.attr("dy", ".75em") // why?
.attr("y", 6)
.attr("x", (x(bins2[0].x1) - x(bins2[0].x0)) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.length); });
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var legend = svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + (width - 245) + "," + 40 + ")")
.selectAll("g")
.data(["Congruent", "Incongruent"])
.enter().append("g");
legend.append("text")
.attr("y", function(d, i) {
return i * 30 + 5;
})
.attr("x", 200)
.text(function(d) {
return d;
});
legend.append("rect")
.attr("y", function(d, i) {
return i * 30 - 8;
})
.attr("x", 167)
.attr("width", 20)
.attr("height", 20)
.attr("fill", function(d) {
if (d == "Congruent") {
return 'rgba(0,0,255,0.6';
} else {
return 'rgba(255,0,0,0.6';
}
});
// g.append("path")
// .datum(data)
// .attr("d", line);
}
</script>
</head>
<body>
<h1>Stroop Test</h1>
<svg width="960" height="500"></svg>
<script type="text/javascript">
d3.csv("stroopdata.csv", function(d) {
d.Congruent = +d.Congruent;
d.Incongruent = +d.Incongruent;
return d;
}, draw);
</script>
</body>
</html>
The data looks like this:
Congruent,Incongruent
12.079,19.278
16.791,18.741
9.564,21.214
8.630,15.687
14.669,22.803
12.238,20.878
14.692,24.572
8.987,17.394
9.401,20.762
14.480,26.282
22.328,24.524
15.298,18.644
15.073,17.510
16.929,20.330
18.200,35.255
12.130,22.158
18.495,25.139
10.639,20.429
11.344,17.425
12.369,34.288
12.944,23.894
14.233,17.960
19.710,22.058
16.004,21.157
Any help appreciated.
I have a brush coordinated with a bar chart. When the brush is moved and resized the bar chart shows only the filtered bars. In the same page I have a pie chart that isn't coordinated with the bar chart and the brush, but I want it to be. I want that also the pie chart updates its content according to the filtered values. How can I do that?
This is my code, followed by the plnkr link where you can see what I've done so far:
<script type="text/javascript">
var margin = {
top: 20,
right: 20,
bottom: 70,
left: 40,
mid: 20
},
w = 750 - margin.left - margin.right,
h = 300 - margin.top - margin.bottom;
var barPadding = 1;
var padding = 20;
var miniHeight = 60;
var selected;
var svg = d3.select(".outer-wrapper .chart").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.mid + miniHeight + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var barsGroup = svg.append('g')
.attr("class","barsGroup");
var miniGroup = svg.append('g')
.attr("class","miniGroup")
.attr("transform","translate(" + 0 + "," + (margin.top + h + margin.mid) + ")");
var brushGroup = svg.append('g')
.attr("class","brushGroup")
.attr("transform","translate(" + 0 + "," + (margin.top + h + margin.mid) + ")");
var w2 = 400;
var h2 = 400;
var outerRadius = w2 / 2;
var innerRadius = w2 / 3;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.values; });
var color = d3.scale.category20c();
var svg2 = d3.select("body")
.append("svg")
.attr("width", w2)
.attr("height", h2);
d3.csv("data.csv", function(data) {
var dataset = d3.nest()
.key(function(d) {
return d.Year;
})
.sortKeys(d3.ascending)
.rollup(function(values) {
return values.length;
})
.entries(data)
.filter(function(d) {
return d.key != "UNK" && d.key != "VAR" && d.key != 199 && d.key != 211 && d.key != 2017;
});
//SCALES
var xScale = d3.scale.ordinal()
.domain(dataset.map(function(d) {
return d.key
}))
.rangeRoundBands([0, w], 0.05);
var xScaleBrush = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.values;
})])
.range([h, 0]);
//AXIS
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickValues([1900,1920,1930,1940,1950,1960,1970,1980,1990,2000, 2010]);
var yAxis = d3.svg.axis()
.scale(yScale)
.ticks(6)
.orient("left");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)");
svg.append("g")
.attr("class","x2 axis")
.attr("transform", "translate(" + 0 + "," + (margin.top + h + margin.mid + miniHeight) + ")" )
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)");
svg.append("g")
.attr("class", "y axis")
.append("g")
.attr("class", "axisLabel")
.append("text")
.attr("transform", "translate(" + -(margin.left * 0.8) + "," + (h/2) + "), rotate(-90)")
.style("text-anchor", "middle")
.text("Score");
var brush = d3.svg.brush()
.x(xScaleBrush)
.extent([0, w])
.on("brush", display);
brushGroup.append("g")
.attr("class", "brush")
.call(brush)
.selectAll("rect")
.attr("opacity", 0.5)
.attr("height", miniHeight);
function display() {
selected = xScaleBrush.domain()
.filter(function(d){
return (brush.extent()[0] <= xScaleBrush(d)) && (xScaleBrush(d) <= brush.extent()[1]);
});
var start;
var end;
/* Keep a minimum amount of bars on there to avoid any jank */
if (selected.length > 2) {
start = selected[0];
end = selected[selected.length - 1] + 1;
} else {
start = 0;
end = dataset.length;
}
var updatedData = dataset.slice(start, end);
updateBars(updatedData);
}
function update(grp, data, main) {
grp.selectAll("rect").data(data, function(d) {
return d.key;
})
.attr("x", function(d) {
return xScale(d.key);
})
.attr("y", function(d) {
return main ? yScale(d.values) : 0;
})
.attr("width", function (d) {
return xScale.rangeBand();
})
.attr("height", function(d) {
return main ? h - yScale(d.values) : miniHeight;
});
}
function enter(grp, data, main) {
grp.selectAll("rect").data(data, function(d) {
return d.key;
})
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(d.key);
})
.attr("y", function(d) {
return main ? yScale( d.values) : 0;
})
.attr("width", function(d) {
return xScale.rangeBand();
})
.attr("height", function(d) {
return main ? h - yScale(d.values) : miniHeight;
})
.attr("fill", function(d) {
var color = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.values;
})])
.range([200, 244]);
var deg = color(d.values);
return "hsl(" + deg + ", 100%, 50%)";
})
.on("mouseover", function(d) {
if(main){
d3.select(this)
.attr("fill", "orange");
var xPosition = parseFloat(d3.select(this).attr("x"));
var yPosition = parseFloat(d3.select(this).attr("y")) / 2 + 100;
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.style("z-index", "10")
.select("#value")
.text(d.values);
d3.select("#tooltip")
.select("#key")
.text("Film del " + d.key + " rilasciati su DVD");
d3.select("#tooltip").classed("hidden", false);
}
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(250)
.attr("fill", function(d) {
var color = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.values;
})])
.range([200, 244]);
var deg = color(d.values);
return "hsl(" + deg + ", 100%, 50%)";
});
d3.select("#tooltip").classed("hidden", true);
});
}
function exit(grp, data) {
grp.selectAll("rect").data(data, function(d) {
return d.key;
}).exit()
.remove();
}
function updateBars(data) {
xScale.domain(data.map(function(d) {
return d.key
}));
yScale.domain([0, d3.max(data, function(d) {
return d.values;
})]);
/* Update */
update(barsGroup, data, true);
/* Enter… */
enter(barsGroup, data, true);
/* Exit */
exit(barsGroup, data);
svg.select(".outer-wrapper .chart .y")
.transition()
.duration(10)
.call(yAxis);
svg.select(".outer-wrapper .chart .x")
.transition()
.duration(50)
.call(xAxis);
}
enter(miniGroup, dataset, false);
updateBars(dataset);
var dataset2 = d3.nest()
.key(function(d) { return d.Genre; })
.sortKeys(d3.ascending)
.rollup(function(values) { return values.length; })
.entries(data);
var text = svg2.append("text")
.attr("dx", 200)
.attr("dy", 200)
.attr("font-size", 30)
.style("text-anchor", "middle")
.attr("fill", "#36454f");
var text2 = svg2.append("text")
.attr("dx", 200)
.attr("dy", 230)
.attr("font-size", 20)
.style("text-anchor", "middle")
.attr("fill", "#36454f");
var text3 = svg2.append("text")
.attr("dx", 200)
.attr("dy", 260)
.attr("font-size", 20)
.style("text-anchor", "middle")
.attr("fill", "#36454f");
//Set up groups
var arcs = svg2.selectAll("g.arc")
.data(pie(dataset2))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")")
.on("mouseover", function(d) {
var total = data.length;
var percent = Math.round(1000 * d.value / total) / 10;
text.text(d.data.key).attr("class", "inner-circle");
text2.text(d.value + " DVD");
text3.text(percent +"%");
})
.on("mouseout", function(d) {
text.text(function(d) { return ""; });
text2.text(function(d) { return ""; });
text3.text(function(d) { return ""; });
});
//Draw arc paths
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
});
</script>
http://plnkr.co/edit/cwNl6zUSOM4yPmUtSRr4?p=preview
You can do that by transforming the part where you build the pieChart into a function that receives the new data.
The only adjustment is that the first thing I'm doing is removing the previous pie chart, to draw it again with the new data:
function updatePie(data){
svg2.selectAll("g.arc").remove();
Here is the complete code:
<script type="text/javascript">
var margin = {
top: 20,
right: 20,
bottom: 70,
left: 40,
mid: 20
},
w = 750 - margin.left - margin.right,
h = 300 - margin.top - margin.bottom;
var barPadding = 1;
var padding = 20;
var miniHeight = 60;
var selected;
var svg = d3.select(".outer-wrapper .chart").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.mid + miniHeight + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var barsGroup = svg.append('g')
.attr("class","barsGroup");
var miniGroup = svg.append('g')
.attr("class","miniGroup")
.attr("transform","translate(" + 0 + "," + (margin.top + h + margin.mid) + ")");
var brushGroup = svg.append('g')
.attr("class","brushGroup")
.attr("transform","translate(" + 0 + "," + (margin.top + h + margin.mid) + ")");
var w2 = 400;
var h2 = 400;
var outerRadius = w2 / 2;
var innerRadius = w2 / 3;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.values; });
var color = d3.scale.category20c();
var svg2 = d3.select("body")
.append("svg")
.attr("width", w2)
.attr("height", h2);
d3.csv("data.csv", function(data) {
var dataset = d3.nest()
.key(function(d) {
return d.Year;
})
.sortKeys(d3.ascending)
.rollup(function(values) {
return values.length;
})
.entries(data)
.filter(function(d) {
return d.key != "UNK" && d.key != "VAR" && d.key != 199 && d.key != 211 && d.key != 2017;
});
//SCALES
var xScale = d3.scale.ordinal()
.domain(dataset.map(function(d) {
return d.key
}))
.rangeRoundBands([0, w], 0.05);
var xScaleBrush = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.values;
})])
.range([h, 0]);
//AXIS
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickValues([1900,1920,1930,1940,1950,1960,1970,1980,1990,2000, 2010,2020,2030,2040,2050,2060]);
var yAxis = d3.svg.axis()
.scale(yScale)
.ticks(6)
.orient("left");
//Appendi asse x
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)");
//Asse x per brush
svg.append("g")
.attr("class","x2 axis")
.attr("transform", "translate(" + 0 + "," + (margin.top + h + margin.mid + miniHeight) + ")" )
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)");
svg.append("g")
.attr("class", "y axis")
.append("g")
.attr("class", "axisLabel")
.append("text")
.attr("transform", "translate(" + -(margin.left * 0.8) + "," + (h/2) + "), rotate(-90)")
.style("text-anchor", "middle")
.text("Score");
var brush = d3.svg.brush()
.x(xScaleBrush)
.extent([0, w])
.on("brush", display);
brushGroup.append("g")
.attr("class", "brush")
.call(brush)
.selectAll("rect")
.attr("opacity", 0.5)
.attr("height", miniHeight);
function display() {
selected = xScaleBrush.domain()
.filter(function(d){
return (brush.extent()[0] <= xScaleBrush(d)) && (xScaleBrush(d) <= brush.extent()[1]);
});
var start;
var end;
/* Keep a minimum amount of bars on there to avoid any jank */
if (selected.length > 2) {
start = selected[0];
end = selected[selected.length - 1] + 1;
} else {
start = 0;
end = dataset.length;
}
var updatedData = dataset.slice(start, end);
updateBars(updatedData);
updatePie(updatedData);
}
function update(grp, data, main) {
grp.selectAll("rect").data(data, function(d) {
return d.key;
})
.attr("x", function(d) {
return xScale(d.key);
})
.attr("y", function(d) {
return main ? yScale(d.values) : 0;
})
.attr("width", function (d) {
return xScale.rangeBand();
})
.attr("height", function(d) {
return main ? h - yScale(d.values) : miniHeight;
});
}
function enter(grp, data, main) {
grp.selectAll("rect").data(data, function(d) {
return d.key;
})
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(d.key);
})
.attr("y", function(d) {
return main ? yScale( d.values) : 0;
})
.attr("width", function(d) {
return xScale.rangeBand();
})
.attr("height", function(d) {
return main ? h - yScale(d.values) : miniHeight;
})
.attr("fill", function(d) {
var color = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.values;
})])
.range([200, 244]);
var deg = color(d.values);
return "hsl(" + deg + ", 100%, 50%)";
})
.on("mouseover", function(d) {
if(main){
d3.select(this)
.attr("fill", "orange");
var xPosition = parseFloat(d3.select(this).attr("x"));
var yPosition = parseFloat(d3.select(this).attr("y")) / 2 + 100;
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.style("z-index", "10")
.select("#value")
.text(d.values);
d3.select("#tooltip")
.select("#key")
.text("Film del " + d.key + " rilasciati su DVD");
d3.select("#tooltip").classed("hidden", false);
}
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(250)
.attr("fill", function(d) {
var color = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.values;
})])
.range([200, 244]);
var deg = color(d.values);
return "hsl(" + deg + ", 100%, 50%)";
});
d3.select("#tooltip").classed("hidden", true);
});
}
function exit(grp, data) {
grp.selectAll("rect").data(data, function(d) {
return d.key;
}).exit()
.remove();
}
function updateBars(data) {
xScale.domain(data.map(function(d) {
return d.key
}));
yScale.domain([0, d3.max(data, function(d) {
return d.values;
})]);
/* Update */
update(barsGroup, data, true);
/* Enter… */
enter(barsGroup, data, true);
/* Exit */
exit(barsGroup, data);
svg.select(".outer-wrapper .chart .y")
.transition()
.duration(10)
.call(yAxis);
svg.select(".outer-wrapper .chart .x")
.transition()
.duration(50)
.call(xAxis);
}
enter(miniGroup, dataset, false);
updateBars(dataset);
var dataset2 = d3.nest()
.key(function(d) { return d.Genre; })
.sortKeys(d3.ascending)
.rollup(function(values) { return values.length; })
.entries(data);
var text = svg2.append("text")
.attr("dx", 200)
.attr("dy", 200)
.attr("font-size", 30)
.style("text-anchor", "middle")
.attr("fill", "#36454f");
var text2 = svg2.append("text")
.attr("dx", 200)
.attr("dy", 230)
.attr("font-size", 20)
.style("text-anchor", "middle")
.attr("fill", "#36454f");
var text3 = svg2.append("text")
.attr("dx", 200)
.attr("dy", 260)
.attr("font-size", 20)
.style("text-anchor", "middle")
.attr("fill", "#36454f");
function updatePie(data){
svg2.selectAll("g.arc").remove();
var arcs = svg2.selectAll("g.arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")")
.on("mouseover", function(d) {
var total = data.length;
var percent = Math.round(1000 * d.value / total) / 10;
text.text(d.data.key).attr("class", "inner-circle");
text2.text(d.value + " DVD");
text3.text(percent +"%");
})
.on("mouseout", function(d) {
text.text(function(d) { return ""; });
text2.text(function(d) { return ""; });
text3.text(function(d) { return ""; });
});
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
}
updatePie(dataset2);
});
i am trying to create a stacked bar graph.
however i can't seem to get the alignment right.the graph has two axis.
because of the length of the y axis label it is partially blocked.
i tried solving this by using different CSS styles on the label and on the enclosing div,
but they did not have the desired affect.
i created a jsfidel to explain my case.
http://jsfiddle.net/2khbceut/1/
HTML
<title>Diverging Stacked Bar Chart with D3.js</title>
<body>
<div id="figure" align="center" style="margin-bottom: 50px;"></div>
</body>
javascript
$(document).ready(getTopolegy());
function getTopolegy(){
var data = null;
var links = parseTopology(data);
createChart(links);
}
function parseTopology(data){
var links=[{1:5,2:5,3:10,N:20,link_name: "Link 167772376>>167772375"}];
return links;
}
function jsonNameToId(name){
switch (allocated_priority) {
case "allocated_priority":
return 1;
case "allocated_default":
return 2;
case "spare_capacity":
return 3;
case "total":
return "N";
default:
return 999;
}
}
function createChart(data){
var margin = {top: 50, right: 20, bottom: 10, left: 65},
width = 1000 - margin.left - margin.right,
height = 1000 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], .3);
var x = d3.scale.linear()
.rangeRound([0, width]);
var color = d3.scale.ordinal()
.range(["#cccccc", "#92c6db", "#086fad"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("top");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
var svg = d3.select("#figure").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "d3-plot")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(["Allocated Priority %", "Allocated Default %", "Spare Capacity %"]);
// d3.csv("js/raw_data.csv", function(error, data) {
data.forEach(function(d) {
d["Allocated Priority %"] = +d[1]*100/d.N;
d["Allocated Default %"] = +d[2]*100/d.N;
d["Spare Capacity %"] = +d[3]*100/d.N;
var x0 = 0;
var idx = 0;
d.boxes = color.domain().map(function(name) { return {name: name, x0: x0, x1: x0 += +d[name], N: +d.N, n: +d[idx += 1]}; });
});
var min_val = d3.min(data, function(d) {
return d.boxes["0"].x0;
});
var max_val = d3.max(data, function(d) {
return d.boxes["2"].x1;
});
x.domain([min_val, max_val]).nice();
y.domain(data.map(function(d) { return d.link_name; }));
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
var vakken = svg.selectAll(".Link")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(0," + y(d.link_name) + ")"; });
var bars = vakken.selectAll("rect")
.data(function(d) { return d.boxes; })
.enter().append("g").attr("class", "subbar");
bars.append("rect")
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.x0); })
.attr("width", function(d) { return x(d.x1) - x(d.x0); })
.style("fill", function(d) { return color(d.name); });
bars.append("text")
.attr("x", function(d) { return x(d.x0); })
.attr("y", y.rangeBand()/2)
.attr("dy", "0.5em")
.attr("dx", "0.5em")
.style("font" ,"10px sans-serif")
.style("text-anchor", "begin")
.text(function(d) { return d.n !== 0 && (d.x1-d.x0)>3 ? d.n : "" });
vakken.insert("rect",":first-child")
.attr("height", y.rangeBand())
.attr("x", "1")
.attr("width", width)
.attr("fill-opacity", "0.5")
.style("fill", "#F5F5F5")
.attr("class", function(d,index) { return index%2==0 ? "even" : "uneven"; });
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x(0))
.attr("x2", x(0))
.attr("y2", height);
var startp = svg.append("g").attr("class", "legendbox").attr("id", "mylegendbox");
// this is not nice, we should calculate the bounding box and use that
var legend_tabs = [0, 150, 300];
var legend = startp.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(" + legend_tabs[i] + ",-45)"; });
legend.append("rect")
.attr("x", 0)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 22)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "begin")
.style("font" ,"10px sans-serif")
.text(function(d) { return d; });
d3.selectAll(".axis path")
.style("fill", "none")
.style("stroke", "#000")
.style("shape-rendering", "crispEdges")
d3.selectAll(".axis line")
.style("fill", "none")
.style("stroke", "#000")
.style("shape-rendering", "crispEdges")
var movesize = width/2 - startp.node().getBBox().width/2;
d3.selectAll(".legendbox").attr("transform", "translate(" + movesize + ",0)");
// });
}
i will appreciate any insight you have on this matter.