I have build the below code to create a customer vertical slider that can be plotted in an x and y axis plot.
I have taken input from a csv file and then using it to build the below output in d3.js.The code in d3.js is as below :
Edit 1: I am currently able to drag only the yellow rectangles only, but not all the rectangles. This is how the the modified graph looks like
Need your suggestions on -
1) How to drag all the rectangles individually
2) Keep the rectangle color belonging to a line same. For Example : all the rectangle with the first line will have same color and all the rectangles along the 2nd line will have another color and so on.
It will be of immense help, thanks !
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test Data</title>
<style>
div.tooltip {
position: absolute;
text-align: center;
width: 90px;
height: 32px;
padding: 1px;
font: 11px verdana;
background: rebeccapurple;
color: white;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
svg {
float: left;
border-bottom: solid 1px #ccc;
border-right: solid 1px #ccc;
margin-right: -1px;
margin-bottom: -1px;
}
rect {
opacity: 0.9;
}
svg {
float: left;
border-bottom: solid 1px #ccc;
border-right: solid 1px #ccc;
margin-right: -1px;
margin-bottom: -1px;
}
rect {
opacity: 0.9;
}
</style>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script type="text/javascript">
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
// load csv from the same directory
d3.csv("test.csv", function (data) {
data.forEach(function (d) {
x_value: +data.x_value; // convert to number with +
promotedprice: +data.promotedprice; // convert to number with +
nonpromotedprice: +data.nonpromotedprice;
avgprice: +data.avgprice;
brand: data.brand;
});
var svg = d3.select("body").append("svg")
.attr("width", 700)
.attr("height", 450)
// Attach the Promoted Price Rectangle
var g = svg.selectAll("rect")
.data(data)
.enter()
.append("g")
.classed('rect', true)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var accent = d3.scaleOrdinal(d3.schemeAccent);
/*.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragend));*/
/*g.append("rect")
.attr("width", 18)
.attr("height", function (d) { return (d.nonpromotedprice - d.promotedprice) + 100; })
.attr("x", function (d) { return d.x_value; })
.attr("y", function (d) { return (d.nonpromotedprice - d.promotedprice) / 2; })
.attr("fill", "#E6EAEE")
*/
g.append("line") // attach a line
.style("stroke", "gray")
.style("stroke-width", 12) // colour the line
.attr("x1", function (d) { return d.x_value; }) // x position of the first end of the line
.attr("y1", function (d) { return d.nonpromotedprice; }) // y position of the first end of the line
.attr("x2", function (d) { return d.x_value; }) // x position of the second end of the line
.attr("y2", function (d) { return d.promotedprice; });
g.append("rect")
.attr("width", 20)
.attr("height", 15)
.attr("x", function (d) { return d.x_value - 10; })
.attr("y", function (d) { return d.promotedprice; })
.attr("fill", "#F9EA55")
g.append("rect")
.attr("width", 20)
.attr("height", 15)
.attr("x", function (d) { return d.x_value - 10; })
.attr("y", function (d) { return d.nonpromotedprice; })
.attr("fill", "#ED8B00")
g.append("rect")
.attr("width", 20)
.attr("height", 15)
.attr("x", function (d) { return d.x_value - 10; })
.attr("y", function (d) { return d.avgprice; })
.attr("fill", "#28468E")
;
g.selectAll("rect")
.style('cursor', 'pointer');
g.selectAll("text")
.data(data)
.enter()
.insert("text", "line")
.text(function (d) { return d.brand; })
.style("text-anchor", "top")
.style("fill", "red")
.style("font-family", "Arial")
.style("font-size", 34);
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this).select("rect")
//.attr("x", d.x = d3.event.x)
.attr("y", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("active", false);
}
});
</script>
</body>
Thanks in Advance
Related
I would like help to correct my code to click the marker circle element to pause or resume transition of this element along the line. My code moves marker along a line and I can pause and resume this transition using on click on button element but I would like to be able to click on the marker circle itself, not the button. I have used various references including :
http://www.nytimes.com/interactive/2013/09/25/sports/americas-cup-course.html
http://jsfiddle.net/meetamit/UJuWX/3/
http://jsfiddle.net/Y62Hq/2/
D3 tween - pause and resume controls
I would ultimately like to be able animate a marker along a geo path, pause and resume this at points along the path and click through on these points.
this is my code so far:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Need help</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v3.min.js"></script>
<style type="text/css">
body{
font-family:"Helvetica Neue", Helvetica, sans-serif;
color: red;
}
button {
position: absolute;
top: 15px;
left: 10px;
background: #004276;
padding-right: 26px;
border-radius: 2px;
cursor: pointer;
}
circle {
fill: steelblue;
stroke: pink;
stroke-width: 3px;
}
.point{
fill:green;
}
.line{
fill: none;
stroke: red;
stroke-width: 4;
stroke-dasharray: 4px,8px;
}
</style>
</head>
<body>
<button>Start</button>
<script>
var w = 960,
h = 500;
var duration = 10000;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
var line = d3.line()
.x(function(d){return (d)[0];})
.y(function(d){return (d)[1];});
var data =
[
[480, 200],
[580, 400],
[680, 100],
[780, 300],
[180, 300],
[280, 100],
[380, 400]
];
//path to animate
var linepath = svg.append("path")
.data([data])
.attr("d", line)
.attr('class', 'line')
.attr("d", function(d){
console.log(this);
return line(d)
});
var points = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 7)
.attr("transform", function(d) { return "translate(" + (d) + ")"; })
.attr("class", "point");
var pauseValues = {
lastTime: 0,
currentTime: 0
};
var marker = svg.append("circle")
.attr("r", 19)
.attr("transform", "translate(" + (data[0]) + ")")
.on('click', function(d,i){
d3.select(this)
.style("fill", "orange")
.transition()
});
function transition() {
marker.transition()
.duration(duration - (duration * pauseValues.lastTime))
.attrTween("transform", translateAlong(linepath.node()))
.on("end", function(){
pauseValues = {
lastT: 0,
currentT: 0
};
transition()
});
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
t += pauseValues.lastTime;
var p = path.getPointAtLength(t * l);
pauseValues.currentTime = t;
return "translate(" + p.x + "," + p.y + ")";
};
};
}
d3.select('button').on('click',function(d,i){
var self = d3.select(this);
if (self.text() == "Pause"){
self.text('Start');
marker.transition()
.duration(0);
setTimeout(function(){
pauseValues.lastTime = pauseValues.currentTime;
}, 100);
}else{
self.text('Pause');
transition();
}
});
</script>
</body>
</html>
To check if the circle is moving in the click function use d3.active(), which...
... returns null if there is no such active transition on the specified node.
Like this:
.on('click', function(d, i) {
if (d3.active(this)) {
marker.transition();
setTimeout(function() {
pauseValues.lastTime = pauseValues.currentTime;
}, 100);
} else {
transition();
}
});
Here is your code with that change:
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v3.min.js"></script>
<style type="text/css">
body {
font-family: "Helvetica Neue", Helvetica, sans-serif;
color: red;
}
button {
position: absolute;
top: 15px;
left: 10px;
background: #004276;
padding-right: 26px;
border-radius: 2px;
cursor: pointer;
}
circle {
fill: steelblue;
stroke: pink;
stroke-width: 3px;
}
.point {
fill: green;
}
.line {
fill: none;
stroke: red;
stroke-width: 4;
stroke-dasharray: 4px, 8px;
}
</style>
<body>
<button>Start</button>
<script>
var w = 960,
h = 500;
var duration = 10000;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
var line = d3.line()
.x(function(d) {
return (d)[0];
})
.y(function(d) {
return (d)[1];
});
var data = [
[480, 200],
[580, 400],
[680, 100],
[780, 300],
[180, 300],
[280, 100],
[380, 400]
];
//path to animate
var linepath = svg.append("path")
.data([data])
.attr("d", line)
.attr('class', 'line')
.attr("d", function(d) {
return line(d)
});
var points = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 7)
.attr("transform", function(d) {
return "translate(" + (d) + ")";
})
.attr("class", "point");
var pauseValues = {
lastTime: 0,
currentTime: 0
};
var marker = svg.append("circle")
.attr("r", 19)
.attr("transform", "translate(" + (data[0]) + ")")
.on('click', function(d, i) {
if (d3.active(this)) {
marker.transition();
setTimeout(function() {
pauseValues.lastTime = pauseValues.currentTime;
}, 100);
} else {
transition();
}
});
function transition() {
marker.transition()
.duration(duration - (duration * pauseValues.lastTime))
.attrTween("transform", translateAlong(linepath.node()))
.on("end", function() {
pauseValues = {
lastT: 0,
currentT: 0
};
transition()
});
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
t += pauseValues.lastTime;
var p = path.getPointAtLength(t * l);
pauseValues.currentTime = t;
return "translate(" + p.x + "," + p.y + ")";
};
};
}
d3.select('button').on('click', function(d, i) {
var self = d3.select(this);
if (self.text() == "Pause") {
self.text('Start');
marker.transition()
.duration(0);
setTimeout(function() {
pauseValues.lastTime = pauseValues.currentTime;
}, 100);
} else {
self.text('Pause');
transition();
}
});
</script>
</body>
I have already created a force layout with images as nodes and mouse over text.
However, now I am trying to add the ability to search for a specific node using a text box and a search button. When a user searches for text I would like to select the matching node and its connections. The matched node and connections would change color. My code is below, but my search function is not working as intended. How do I change this code to get the desired behavior?
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
stroke: #777;
stroke-opacity: 0.3;
stroke-width: 1.5px;
}
.node circle {
fill: #ccc;
stroke: #000;
stroke-width: 1.5px;
}
.node text {
fill: red;
display: none;
font: 10px sans-serif;
}
.node:hover circle {
fill: #000;
}
.node:hover text {
display: inline;
}
.cell {
fill: none;
pointer-events: all;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<div>
<input id="targetNode" name="targetNode" type="text" value="Enter the text" />
<button onclick="search()">Search</button>
</div>
<script>
var width =1500,
height = 650
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(0.1)
.charge(-500)
.linkDistance(100)
.size([width, height]);
d3.json("miserables1.json", function(error, json) {
if (error) throw error;
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("svg:image")
.attr("xlink:href", function(d) {
return d.imagen;
})
.attr("x", function(d) {
return -25;
})
.attr("y", function(d) {
return -25;
})
.attr("height", 50)
.attr("width", 50)
.append("title")
.text(function(d){ return d.name });
function search() {
var userInput = document.getElementById("targetNode");
var theNode = d3.select("#c"+userInput.value);
return theNode;
}
function linkToNodes() {
force.links().forEach(function(link) {
linkedByIndex[link.source.index + "," + link.target.index] = 1;
});
}
function neighboring(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
force.on("tick", function() {
link
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
});
</script>
I'm not quite sure what you intend do happen when the user clicks search. Your search function tries to select "#c" + userInput.value, but I can't see anything on the page with that ID. If your data has a name field that you want it to be searchable on then you could set the id of each node to that name with the following:
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("id", d => "c" + d.name) // setting ID here
.attr("class", "node")
.call(force.drag);
This would allow your search function to get a reference to the node, but currently it is just returning it, and the calling function isn't going to do anything with that reference, if you want to highlight that that node as been selected you should put some code in here to do that, e.g:
function search() {
var userInput = document.getElementById("targetNode");
var theNode = d3.select("#c"+userInput.value);
theNode.style("color", "red");
}
I'm creating a population pyramid with D3.js using with a template taken from the visualisation group at Stanford. I can't figure out why initially I get tooltips and markers on the graph but once I transition to a different years these are no longer available. Anyone know how I could fix this? Apologies for the length of the code, I'm pretty new to D3/JavaScript so just figured I'd put everything up rather than trying to pinpoint where the error is.
Thanks
Northern Ireland Teacher Population Pyramid
body {
font: 12px sans-serif;
margin: 0;
padding: 5px;
color: #888;
}
h1 {
padding-left: 10px;
margin-bottom: 2px;
color: #333;
}
.source {
padding-left: 10px;
}
.source a, .source a:hover {
color: #888;
}
.label {
position: absolute;
top: 60px;
left: 15px;
font-size: 48px;
font-weight: bold;
color: #dedede;
}
.break {
border-bottom: solid 1px #dedede;
margin: 10px 15px 2px 15px;
width: 545px;
}
.years, .controls {
padding-top: 10px;
padding-left: 15;
width: 540;
text-align: center;
font-size: 12px;
}
.years span, .controls span {
padding-left: 2px;
padding-right: 2px;
}
.years .title {
font-size: 13px;
font-variant: small-caps;
letter-spacing: 1;
}
.years a, .controls a {
color: #888;
text-decoration: none;
}
.years a:hover, .controls a:hover {
color: #000;
text-decoration: underline;
}
.years a.y1890 {
color: #bbb;
}
.years a.active {
color: #000;
}
.controls a {
font-variant: small-caps;
letter-spacing: 1;
text-decoration: none;
}
svg {
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<h1>Northern Ireland Teachers Population Pyramid, 2010-2014</h1>
<div class="source">
Source: Teachers' Payroll and Pensions Administration System.
</div><script type="text/javascript">
document.onkeydown = function(event) {
var y = year;
switch (event.keyCode) {
case 37: // left arrow
y = Math.max(2010, year-1);
break;
case 39: // right arrow
y = Math.min(2014, year+1);
break;
case 32: // space bar
toggle();
return;
}
if (y != year) goto(y);
};
function isYear(d) { return d.year == year; }
function linkClass(y) { return "y"+y.toFixed(0) + (y==year?" active":""); }
function tooltipText(d) {
return d3.format(",")(d.people)
+ " " + (d.sex==1?"men":"women")
+ " aged " + (d.age==60?"60+":d.age+"-"+(d.age+4))
+ " in " + d.year;
}
function barWidth(d) { return x1(d.people); }
function goto(yr, dur) {
dur = dur || 300;
var old = year;
year = yr;
label.text(year);
div.selectAll("span.link a")
.attr("class", linkClass);
var fb = vis.selectAll("rect.female").data(fdat.filter(isYear), {
nodeKey: function(node) { return node.getAttribute("id"); },
dataKey: function(data) { return "f"+(data.year - data.age); }
});
fb.enter("svg:rect")
.attr("id", function(d) { return "f"+(d.year - d.age); })
.attr("class", "female")
.attr("fill", "pink")
.attr("transform", lTransform)
.attr("width", function(d) { return x1(d.people); })
.attr("y", yr>old ? 20 : -20)
.attr("height", y.rangeBand())
.attr("opacity", 0.0001)
.transition()
.duration(dur)
.attr("y", 0)
.attr("opacity", 1);
fb.exit().transition()
.duration(dur)
.attr("y", yr>old ? -20 : 30)
.attr("opacity", 0.0001)
.each("end", function() { d3.select(this).remove(); });
fb.transition()
.duration(dur)
.attr("transform", lTransform)
.attr("y", 0)
.attr("width", function(d) { return x1(d.people); })
.attr("opacity", 1);
fb.selectAll("title").text(tooltipText);
var mb = vis.selectAll("rect.male").data(mdat.filter(isYear), {
nodeKey: function(node) { return node.getAttribute("id"); },
dataKey: function(data) { return "m"+(data.year - data.age); }
});
mb.enter("svg:rect")
.attr("id", function(d) { return "m"+(d.year - d.age); })
.attr("class", "male")
.attr("fill", "steelblue")
.attr("transform", rTransform)
.attr("width", function(d) { return x1(d.people); })
.attr("y", yr>old ? 20 : -20)
.attr("height", y.rangeBand())
.attr("opacity", 0.0001)
.transition()
.duration(dur)
.attr("y", 0)
.attr("opacity", 1);
mb.exit().transition()
.duration(dur)
.attr("y", yr>old ? -20 : 30)
.attr("opacity",0.0001)
.each("end", function() { d3.select(this).remove(); });
mb.transition()
.duration(dur)
.attr("transform", rTransform)
.attr("y", 0)
.attr("width", function(d) { return x1(d.people); })
.attr("opacity", 1);
mb.select("title").text(tooltipText);
}
var timer = undefined;
function stop() {
clearInterval(timer);
timer = undefined;
ctrls.select("a.toggle").text("play");
}
function toggle() {
if (!timer) {
play();
} else {
stop();
}
}
function play(rev) {
rev = rev || false;
if (timer) { stop(); }
ctrls.select("a.toggle").text("stop");
var advance = function() {
var y = year + (rev?-1:1)*1;
if (y < 2010 || y > 2013) {
// stop at end points
stop();
return;
} else {
// else advance
goto(y, 800);
}
};
advance();
timer = setInterval(advance, 850);
}
var data = census,
maxp = data.reduce(function(a,b) { return Math.max(a,b.people); }, 0),
mdat = data.filter(function(d) { return d.sex==1; })
.sort(function(a,b) { return b.age - a.age; }),
fdat = data.filter(function(d) { return d.sex==2; })
.sort(function(a,b) { return b.age - a.age; });
var w = 250,
h = 9 * 40,
bins = d3.range(9),
year = 2010,
y = d3.scale.ordinal().domain(bins).rangeBands([0, h], 0.25),
x1 = d3.scale.linear().domain([0, maxp]).range([0, w]),
x2 = d3.scale.linear().domain([0, maxp]).range([w, 0]),
rf = "javascript:return false;";
var label = d3.select("body")
.append("div")
.attr("class", "label")
.text(year.toFixed(0));
var vis = d3.select("body")
.append("svg:svg")
.attr("width", 2*w + 40)
.attr("height", h + 40)
.append("svg:g")
.attr("transform", "translate(20,15)");
// pyramid bar chart
vis.append("svg:g")
.selectAll("text.ages")
.data(bins)
.enter("svg:text")
.filter(function(d) { return d%2==0; })
.attr("class", "ages")
.attr("x", w+15)
.attr("y", function(d) { return y(d) + y.rangeBand() + 7; })
.attr("fill", "#888")
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.text(function(d) { return (60-d*5).toFixed(0); });
var rTransform = function(d,i) {
return "translate("+(w+30)+","+y(i).toFixed(2)+")";
}
var lTransform = function(d,i) {
return "translate("+x2(d.people).toFixed(2)+","+y(i).toFixed(2)+")";
}
var lEnter = function(d,i) {
return "translate("+w+","+y(i).toFixed(2)+")";
}
var mbars = vis.selectAll("rect.male")
.data(mdat.filter(isYear))
.enter("svg:rect")
.attr("id", function(d) { return "m"+(d.year - d.age); })
.attr("class", "male")
.attr("fill", "steelblue")
.attr("transform", rTransform)
.attr("width", barWidth)
.attr("height", y.rangeBand())
.attr("y", 0)
.attr("opacity", 1);
mbars.append("svg:title").text(tooltipText);
var fbars = vis.selectAll("rect.female")
.data(fdat.filter(isYear))
.enter("svg:rect")
.attr("id", function(d) { return "f"+(d.year - d.age); })
.attr("class", "female")
.attr("fill", "pink")
.attr("opacity", 1)
.attr("transform", lTransform)
.attr("width", barWidth)
.attr("height", y.rangeBand())
.attr("y", 0)
.attr("opacity", 1);
fbars.append("svg:title").text(tooltipText);
// animated intro for bars
mbars.attr("width", 0)
.transition()
.duration(100)
.delay(function(d,i) { return 30*i; })
.attr("width", barWidth);
fbars.attr("width", 0)
.attr("transform", lEnter)
.transition()
.duration(100)
.delay(function(d, i) { return 30*i; })
.attr("width", barWidth)
.attr("transform", lTransform);
// age label
vis.append("svg:text")
.attr("x", w+15)
.attr("y", h+8)
.attr("dy", ".71em")
.attr("fill", "#888")
.attr("text-anchor", "middle")
.attr("font-size", "13px")
.attr("font-variant", "small-caps")
.attr("letter-spacing", 1)
.text("age");
// gridlines and labels for right pyramid
var rules1 = vis.selectAll("g.rule1")
.data(x1.ticks(5))
.enter("svg:g")
.filter(function(d) { return d > 0; })
.attr("class", "rule1")
.attr("transform", function(d) { return "translate("+(w+30+x1(d))+",0)";});
rules1.append("svg:line")
.attr("y1", h - 2)
.attr("y2", h + 4)
.attr("stroke", "#bbb");
rules1.append("svg:line")
.attr("y1", 0)
.attr("y2", h)
.attr("stroke", "white")
.attr("stroke-opacity", .3);
rules1.append("svg:text")
.attr("y", h + 9)
.attr("dy", ".71em")
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.attr("fill", "#bbb")
.text(function(d) { return (d).toFixed(0); });
// gridlines and labels for left pyramid
var rules2 = vis.selectAll("g.rule2")
.data(x2.ticks(5))
.enter("svg:g")
.filter(function(d) { return d > 0; })
.attr("class", "rule2")
.attr("transform", function(d) { return "translate("+(x2(d))+",0)";});
rules2.append("svg:line")
.attr("y1", h - 2)
.attr("y2", h + 4)
.attr("stroke", "#bbb");
rules2.append("svg:line")
.attr("y1", 0)
.attr("y2", h)
.attr("stroke", "white")
.attr("stroke-opacity", .3);
rules2.append("svg:text")
.attr("y", h + 9)
.attr("dy", ".71em")
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.attr("fill", "#bbb")
.text(function(d) { return (d).toFixed(0); });
d3.select("body")
.append("div")
.attr("class", "break");
var div = d3.select("body")
.append("div")
.attr("class", "years");
div.append("span")
.attr("class", "title")
.text("year");
var ctrls = d3.select("body")
.append("div")
.attr("class", "controls");
ctrls.append("span").append("a")
.attr("class", "control back")
.attr("href", "javascript:play(true);")
.text("<< ");
ctrls.append("span").append("a")
.attr("class", "control toggle")
.attr("href", "javascript:toggle();")
.text("play");
ctrls.append("span").append("a")
.attr("class", "control forward")
.attr("href", "javascript:play();")
.text(" >>");
div.selectAll("span.link")
.data(d3.range(2010, 2014, 1))
.enter("span")
.attr("class", "link")
.append("a")
.attr("class", linkClass)
.attr("href", function(d) { return d==1890?null:"javascript:goto("+d+");"; })
.text(function(d) { return d.toFixed(0); });
</script>
The trick is to replace
var fb = vis.selectAll("rect.female").data(fdat.filter(isYear), {
nodeKey: function(node) { return node.getAttribute("id"); },
dataKey: function(data) { return "f"+(data.year - data.age); }
});
with
var fb = vis.selectAll("rect.female").data(fdat.filter(isYear));
(And the same for variable mb).
SVG elements are rendered in the order that you append them to the DOM. At the beginning, the grid lines are on top of the bars, but after you redraw the bars, they are on top of the grid lines. You need to append the lines after the bars. If you change the lines from white to another color you can see where they are -- that should help with debugging.
I know it is possible to display an image in a D3 tooltip. What I am trying to do is to display a bar graph in a tooltip (i.e when the mouse hovers over the object a bar graph appears). I have adapted code from http://bl.ocks.org/jarobertson/1483052#gistfile1.html and combined it with the bar graph code by Robert Lewand. And well, it doesn't work. I dont even get any errors in the console that could perhaps put me on the right path. Is it possible to do? Here is my code:
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.27.1"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
div.tooltip {
position: absolute;
text-align: center;
width: 500px;
height: 550px;
padding: 8px;
font: 10px sans-serif;
background: #ddd;
border: solid 1px #aaa;
border-radius: 8px;
pointer-events: none;
}
.chart rect {
fill: steelblue;
}
.chart text {
fill: white;
font: 10px sans-serif;
text-anchor: middle;
}
</style>
</head>
<body>
<script type="text/javascript">
var w = 960,
h = 500;
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
svg.append("svg:g")
.attr("transform", "translate(480,50)rotate(60)scale(2)")
.append("svg:rect")
.attr("width", 140)
.attr("height", 140)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
function mouseover() {
div.transition()
.duration(500)
.style("opacity", 1);
}
// where the tooltip previosly contained an image
function mousemove() {
div
.html("<h1>Bar Graph</h1><br> <svg class='chart'></svg>")
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
}
function mouseout() {
div.transition()
.duration(500)
.style("opacity", 1e-6);
}
// make bar graph
var width = 300,
height = 300;
var y = d3.scale.linear()
.range([height, 0]);
var chart = d3.select(".chart")
.attr("width", width)
.attr("height", height);
d3.tsv("data.tsv", type, function(error, data) {
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var barWidth = width / data.length;
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(" + i * barWidth + ",0)"; });
bar.append("rect")
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", barWidth / 2)
.attr("y", function(d) { return y(d.value) + 3; })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
</body>
</html>
Thanks in advance!
apologies, the data.tsv file contains the following:
Sentiment value
Strongly positive 211
Positive 222
Neutral 654
Negative 618
Strongly negative 343
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.27.1"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
div.tooltip {
position: absolute;
text-align: center;
width: 500px;
height: 550px;
padding: 8px;
font: 10px sans-serif;
background: #ddd;
border: solid 1px #aaa;
border-radius: 8px;
pointer-events: none;
}
.chart rect {
fill: steelblue;
}
.chart text {
fill: white;
font: 10px sans-serif;
text-anchor: middle;
}
</style>
<script type="text/javascript">
var w = 960,
h = 500;
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
svg.append("svg:g")
.attr("transform", "translate(480,50)rotate(60)scale(2)")
.append("svg:rect")
.attr("width", 140)
.attr("height", 140)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
function mouseover() {
div.transition()
.duration(500)
.style("opacity", 1);
}
// where the tooltip previosly contained an image
function mousemove() {
div
.html("<h1>Bar Graph</h1><br> <svg class='chart'></svg>")
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
}
function mouseout() {
div.transition()
.duration(500)
.style("opacity", 1e-6);
}
// make bar graph
var width = 300,
height = 300;
var y = d3.scale.linear()
.range([height, 0]);
var chart = d3.select(".chart")
.attr("width", width)
.attr("height", height);
d3.tsv("data.tsv", type, function(error, data) {
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var barWidth = width / data.length;
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(" + i * barWidth + ",0)"; });
bar.append("rect")
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", barWidth / 2)
.attr("y", function(d) { return y(d.value) + 3; })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
'data.tsv' file is not with us,
and we have written only
function mousemove() {
div
.html("<h1>Bar Graph</h1><br> <svg class='chart'></svg>")
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
}
above function will place 'Bar Graph' text and one svg element in tooltip.
Hope you will get it.
If not ask for more......
I a trying to make a map with D3 and TopoJSON for the Netherlands, including the provinces.
This is the code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script type="text/javascript" src="http://localhost/webserver/d3/d3.js"></script>
<script type="text/javascript" src="http://localhost/webserver/topojson/topojson.js"></script>
<style type="text/css">
div.bar {
display: inline-block;
width: 20px;
height: 75px;
background-color: teal;
margin-right: 2px;
}
.pumpkin {
fill: rgba(128, 0, 128, 0.75);
stroke: yellow;
stroke-width: 5;
}
.apple {
fill: rgba(0, 255, 0, 0.55);
stroke: green;
stroke-width: 15;
}
.orange {
fill: rgba(255, 255, 0, 0.55);
stroke: orange;
stroke-width: 10;
}
.subunit { fill: #cdc; }
.subunit-label {
fill: #777;
fill-opacity: .25;
font-size: 30px;
font-weight: 300;
text-anchor: middle;}
.provincie {fill: none; }
.Utrecht {fill: #ddd; }
.Zuid-Holland {fill: #dde; }
.Noord-Holland {fill: #dee; }
.Drenthe {fill: #aae; }
.Gelderland {fill: #eee; }
.Friesland {fill: #ddc; }
.Groningen {fill: #dcc; }
.Limburg {fill: #ccc; }
.Noord-Brabant {fill: #ddb; }
.Overijssel {fill: #dbb; }
.Zeeland {fill: #bbb; }
</style>
</head>
<body>
<script type="text/javascript">
var width = 960, height = 860;
var projection = d3.geo.albers()
.center([6, 49.40])
.rotate([0, -1.9])
.parallels([50, 60])
.scale(11000)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("http://localhost/webserver/data/nl.json", function(error, nl) {
svg.selectAll(".subunit")
.data(topojson.object(nl, nl.objects.subunits).geometries)
.enter().append("path")
.attr("class", function(d) { return "subunit " + d.id; })
.attr("d", path);
svg.selectAll(".subunit-label")
.data(topojson.object(nl, nl.objects.subunits).geometries)
.enter().append("text")
.attr("x", -20)
.attr("y", -50)
.attr("class", function(d) { return "subunit-label " + d.id; })
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.properties.name; });
svg.selectAll(".provincie")
.data(topojson.object(nl, nl.objects.provincies).geometries)
.enter().append("path")
.attr("class", function(d) { return "provincie " + d.properties.name; })
.attr("d", path);
svg.append("path")
.datum(topojson.object(nl, nl.objects.places))
.attr("d", path)
.attr("class", "place");
svg.selectAll(".place-label")
.data(topojson.object(nl, nl.objects.places).geometries)
.enter().append("text")
.attr("class", "place-label")
.attr("transform", function(d) { return "translate(" + projection(d.coordinates) + ")"; })
.attr("dy", ".35em").text(function(d) { return d.properties.name; });
svg.selectAll(".place-label")
.attr("x", function(d) { return d.coordinates[0] > -1 ? 10 : -10; })
.style("text-anchor", function(d) { return d.coordinates[0] > -1 ? "start" : "end"; });
});
</script>
</body>
</html>
The result is the map of the Netherlands, however it doesn't contain the provinces (with colors and borders).
I get the following error:
Uncaught TypeError: Cannot read property 'type' of undefined topojson.js:187
This is line 186 and 187:
function geometry(o) {
var t = o.type, g = t === "GeometryCollection" ? {type: t, geometries: o.geometries.map(geometry)}
It looks like you are referencing an object that doesn't exist in your topology.
Is it possible your TopoJSON file uses the English spelling of "provinces" rather than the Dutch "provincies"? If it did, then nl.objects.provincies would be null, and you'd either need to refer to nl.objects.provinces in your code, or edit the TopoJSON file to use the Dutch spelling "provincies" instead.
Can you post the contents of nl.json so we can take a look (say, on dropbox)?