Related
I am working on a d3js gantt chart - and I want to enhance this chart code so that it can incorporate a date/time scale at the top that morphs on view -- and also add a blue line that indicates the current date/time(moves - in real time?). I think also the lane height is not correct - and its a bit difficult to adjust the height of the lanes - add gutter if desired in the future design. There may be redundant grid lines in the scrubber area.
//current design
//new concept
//latest version
https://jsfiddle.net/2mvhjr7z/2/
//html
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Gantt chart with D3.js</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type='text/javascript' src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<div
class="timelinechart"
data-role="timelinechart"
data-width=500
data-height=300
data-data=""
>
</div>
</body>
</html>
//css
body {
background: #eeeeee;
}
#holder {
overflow: hidden;
}
/*
.chart {
shape-rendering: crispEdges;
}
*/
.timelinechart{
/*width:100%;
border: 1px solid red;*/
}
.timelinechart svg{
width:100%;
/*border: 1px solid green;*/
}
.timelinechartg{
}
.mini text {
font: 10px sans-serif;
}
.main text {
font: 12px sans-serif;
}
.miniItem {
/*fill: darksalmon;*/
stroke-width: 6;
}
.miniItem.future{
fill: #448875;
}
text.future {
fill: #f7b363;
}
.brush .extent {
stroke: #b6b8b9;
fill: #57585b;
fill-opacity: .365;
stroke-width: .2;
}
.laneImg{
border-radius: 25px;
}
//js
$(document).ready(function() {
var $this = $('.timelinechart');
var w = $this.data("width");
var h = $this.data("height");
var data = [
{
"label": "Argentina",
"times": [
{"text": "", "starting_time": 1355752800000, "ending_time": 1355752840000}
]
},
{
"label": "Bolivia",
"times": [
{"text": "", "starting_time": 1355752800000, "ending_time": 1355752900000}
]
},
{
"label": "Brazil",
"times": [
{"text": "", "starting_time": 1355752900000, "ending_time": 1355752935000}
]
},
{
"label": "Canada",
"times": [
{"text": "", "starting_time": 1355753000000, "ending_time": 1355753500000}
]
},
{
"label": "Chile",
"times": [
{"text": "", "starting_time": 1355753700000, "ending_time": 1355754000000}
]
}
];
var lanes = [];
var times = [];
$.each(data, function(index, value) {
lanes.push(value.label);
$.each(value.times, function(i, v) {
v["lane"] = index;
});
times.push(value.times);
});
var laneLength = lanes.length;
var items = [].concat.apply([], times);
$.each(items, function(i, v) {
v["id"] = i;
});
var timeBegin = d3.min(items, function(d) { return d["starting_time"]; });
var timeEnd = d3.max(items, function(d) { return d["ending_time"]; });
var m = [25, 80, 15, 105], //top right bottom left
w = w - m[1] - m[3],
h = h - m[0] - m[2],
miniHeight = 50,//laneLength * 12 + 50,
mainHeight = h - miniHeight - 50;
//scales
var x = d3.scaleTime()
.range([0, w])
.domain([timeBegin, timeEnd]);
var x1 = d3.scaleLinear()
.range([0, w]);
var y1 = d3.scaleLinear()
.range([0, mainHeight])
.domain([0, laneLength]);
var y2 = d3.scaleLinear()
.range([0, miniHeight])
.domain([0, laneLength]);
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth)
.tickFormat(d=>d3.timeFormat("%B %Y")(d));
var scaleFactor = (1/(timeEnd - timeBegin)) * (w);
var chartWidth = w + m[1] + m[3];
var chartHeight = h + m[0] + m[2];
var chart = d3.select($this[0])
.append("svg")
.attr("width", chartWidth)
.attr("height", chartHeight)
.attr("viewBox", "0 0 "+chartWidth+" "+chartHeight)
.attr("preserveAspectRatio", "xMidYMid meet")
.append("g")
.attr("class", "timelinechartg");
chart.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", w)
.attr("height", mainHeight);
var main = chart.append("g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")")
.attr("width", w)
.attr("height", mainHeight)
.attr("class", "main");
var mini = chart.append("g")
.attr("transform", "translate(" + m[3] + "," + (mainHeight + m[0]) + ")")
.attr("width", w)
.attr("height", miniHeight)
.attr("class", "mini");
var gX = chart.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + m[3] + "," + (mainHeight + miniHeight) + ")")
.call(xAxis);
//background colors
function colores_background(n) {
var colores_g = ["#f8dd2f", "#e9168a", "#448875", "#2b2d39", "#c3bd75", "#1b91dc"];
return colores_g[n % colores_g.length];
}
//foreground colors
function colores_foreground(n) {
var colores_g = ["#553814", "#311854", "#f7b363", "#c12f39", "#89191d", "#2b2d39"];
return colores_g[n % colores_g.length];
}
//main lanes and texts
main.append("g").selectAll(".laneLines")
.data(items)
.enter().append("line")
.attr("x1", 0)
.attr("y1", function(d) {return y1(d.lane);})
.attr("x2", w)
.attr("y2", function(d) {return y1(d.lane);})
.attr("stroke", "lightgray")
var defs = main.append('svg:defs');
main.append("g").selectAll(".laneText")
.data(lanes)
.enter().append("text")
.text(function(d) {return d;})
.attr("x", (-m[1] + 10))
.attr("y", function(d, i) {
return y1(i + .5);
})
.attr("dy", ".5ex")
.attr("text-anchor", "end")
.attr("class", "laneText");
//mini lanes and texts
mini.append("g").selectAll(".laneLines")
.data(items)
.enter().append("line")
.attr("x1", 0)
.attr("y1", function(d) {
return y2(d.lane);
})
.attr("x2", w)
.attr("y2", function(d) {
return y2(d.lane);
})
.attr("stroke", "lightgray");
var itemRects = main.append("g")
.attr("clip-path", "url(#clip)");
mini.append('rect')
.attr("class", "miniBar")
.attr("x", 0)
.attr("y", 10)
.attr("fill", "grey")
.attr("width", w)
.attr("height", 30);
var brush = d3.brushX()
.extent([[0, 0], [w, miniHeight]])
.on("brush", brushed);
mini.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", 1)
.attr("height", miniHeight - 1);
function brushed() {
var selection = d3.event.selection;
var timeSelection = selection.map(x.invert, x);
//console.log("selection: " + selection);
//console.log("start: " + timeSelection[0]);
//console.log("end: " + timeSelection[1]);
var rects;
var labels;
var minExtent = timeSelection[0];
var maxExtent = timeSelection[1];
var visItems = items.filter(function(d) {return d.starting_time < maxExtent && d.ending_time > minExtent;});
//mini.select(".brush")
//.call(brush.extent([minExtent, maxExtent]));
x1.domain([minExtent, maxExtent]);
//update main item rects
rects = itemRects.selectAll("rect")
.data(visItems, function(d) { return d.id; })
.attr("x", function(d) {return x1(d.starting_time);})
.attr("width", function(d) {return x1(d.ending_time) - x1(d.starting_time);});
rects.enter().append("rect")
.attr("class", function(d) {return "miniItem "+d.state;})
.attr("x", function(d) {return x1(d.starting_time);})
.attr("y", function(d) {return y1(d.lane) + .8;})
.attr("fill", "blue")
.attr("width", function(d) {return x1(d.ending_time) - x1(d.starting_time);})
.attr("height", function(d) {return .8 * y1(1);});
rects.exit().remove();
//update the item labels
labels = itemRects.selectAll("text")
.data(visItems, function (d) { return d.id; })
.attr("x", function(d) {return x1(Math.max(d.starting_time, minExtent) + 2);});
labels.enter().append("text")
.text(function(d) {return d.text;})
.attr("class", function(d) {return d.state;})
.attr("x", function(d) {return x1(Math.max(d.starting_time, minExtent));})
.attr("y", function(d) {return y1(d.lane + .5);})
.attr("fill", function(d, i) {
return colores_foreground(d.lane);
})
.attr("text-anchor", "start");
labels.exit().remove();
}
});
I added gutters. You can change the number to change the gutter ratio.
I added another axis bound to x1 scale,
var xAxisTop = d3.axisBottom(xTop)
.ticks(d3.timeMonth)
.tickFormat(d=>d3.timeFormat("%b %Y")(d));
and it is called in brushed as the following.
gXTop.call(xAxisTop);
If there's something I missed, or anything else you want added, please, comment.
Fiddle
I am doing research for a professor at my school and I am coding in d3. I am creating a graph that has tool tips and you can toggle the graph by clicking on the legend. However, when I toggle the lines disappears but I can't get the tool tips associated to that line. I know I need to somehow add an id to these circles but I have no idea how. I have included my code below to show what I have so far. Any help would be appreciated!
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: white;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
div.tooltip {
position: absolute;
text-align: center;
width: 170px;
height: 500px;
padding: 1px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 5px;
pointer-events: none;
}
.legend {
font-size: 16px;
font-weight: bold;
text-anchor: middle;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 20, right: 150, bottom: 60, left: 80},
width = 1160 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Parse the date / time
// Set the ranges
var x = d3.scaleTime().range([0, width-100]);
var formatxAxis=d3.format('.0f');
var y = d3.scaleLinear().range([height, 0]);
// Define the axe
var xAxis = d3.axisBottom().scale(x)
.tickFormat(formatxAxis).ticks(20);
var yAxis = d3.axisLeft().scale(y)
.ticks(5);
// Define the line
var valueline = d3.line()
.curve(d3.curveMonotoneX)
.x(function(d) { return x(d.year); })
.y(function(d) { return y(d.count); });
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Adds the svg canvas
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 + ")");
// Get the data
d3.json("satisfaction.json", function(error, data) {
data.forEach(function(d) {
d.year = +d.year;
d.count = +d.count;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.year; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
//nest the entries by symbol
var dataNest = d3.nest()
.key(function(d) {
return d.word;
})
.entries(data);
//define colors for the lines
var color = d3.scaleOrdinal(d3.schemeCategory10);
// spacing for the legend
legendSpace = width/dataNest.length;
var circleid = svg.selectAll("circle")
.data(dataNest)
.enter()
.append("g")
.attr("id", function(d){
return "circle" + d.key.replace(/\s+/g, '');
});
// Loop through each symbol / key
dataNest.forEach(function(d,i) {
svg.append("path")
.attr("class", "line")
.style("stroke", function() { // Add the colours dynamically
return d.color = color(d.key); })
.attr("id", 'tag'+d.key.replace(/\s+/g, '')) // assign ID
.attr("d", valueline(d.values));
// Add the Legend
svg.append("text")
.attr("x", width - margin.left + 50)
.attr("y", legendSpace/4 + i*(legendSpace/6))
.attr("class", "legend") // style the legend
.style("fill", function() { // Add the colours dynamically
return d.color = color(d.key); })
.on("click", function(){
// Determine if current line is visible
var active = d.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements based on the ID
d3.select("#tag"+d.key.replace(/\s+/g, ''))
.transition().duration(100)
.style("opacity", newOpacity);
// Update whether or not the elements are active
d.active = active;
// Hide or show the elements based on the ID
d3.select("circle" + d.key.replace(/\s+/g, ''))
.transition().duration(100)
.style("opacity", newOpacity);
})
.text(d.key);
});
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.year); })
.attr("cy", function(d) { return y(d.count); })
.on("click", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(d.word + "<br/>" + d.count + "<br/>" + d.songs)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 25) + "px");
})
.on("dblclick", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
;
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>
Right now, you are trying to set your id before you have added the circles to the graph. Instead, you can set the id when you're appending the circles to your graph.
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("id", function(d){ return "circle" + d.key.replace(/\s+/g, ''); })
// ^---- add the id here when you're appending the circles
.attr("r", 5)
.attr("cx", function(d) { return x(d.year); })
.attr("cy", function(d) { return y(d.count); })
.on("click", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(d.word + "<br/>" + d.count + "<br/>" + d.songs)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 25) + "px");
})
.on("dblclick", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
;
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 have trouble to add paths to a g element created by the nodes with selectAll:
// define the nodes
var node = svg.selectAll(".node").data(force.nodes()).enter()
.append("g").attr("id", function(d) {
return d.id;
}).attr("class", "node").style("fill", function(d) {
return color(d.group);
}).call(force.drag);
// add the nodes
...
// add the links and the arrows
var path = node.selectAll("g").data(force.links(), function(d) {
return d.id
}).append("path")
// .attr("class", function(d) { return "link " + d.type; })
.attr("class", "link").attr("marker-end", "url(#end)");
The input data looks like this:
{
"nodes": [
{
"id": 0,
"name": "N1",
"group": 4
},
{
"id": 1,
"name": "N2",
"group": 1
},
{
"id": 2,
"name": "N3",
"group": 1
}
],
"links": [
{
"id": 0,
"source": 0,
"target": 1
},
{
"id": 0,
"source": 0,
"target": 2
}
]
}
I'm trying to modify this example
My goal is that a node has a mouse over (.node:hover) including all the out-going links. With my simple data it would look like that node N1 would have a mouse over including the two links.
Thanks for your help!
Here is all the code:
<!DOCTYPE html>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.js"></script>
<style>
.node {
opacity: 0.8;
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
stroke-width: 0px;
}
.node:hover {
opacity: 1;
}
path.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
</style>
<body>
<script>
// get the data
d3.json("data2.json", function(error, graph) {
var color = d3.scale.category20();
var width = 960, height = 500;
var force = d3.layout.force().nodes(graph.nodes).links(graph.links)
.size([ width, height ]).linkDistance(300).charge(-300).on(
"tick", tick).start();
var svg = d3.select("body").append("svg").attr("width", width)
.attr("height", height);
// build the arrow.
svg.append("svg:defs").selectAll("marker").data([ "end" ]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String).attr("viewBox", "0 -5 10 10").attr("refX", 15)
.attr("refY", -1.5).attr("markerWidth", 6).attr(
"markerHeight", 6).attr("orient", "auto").append(
"svg:path").attr("d", "M0,-5L10,0L0,5");
// define the nodes
var node = svg.selectAll(".node").data(force.nodes()).enter()
.append("g").attr("id", function(d) {
return d.id;
}).attr("class", "node").style("fill", function(d) {
return color(d.group);
}).call(force.drag);
// add the nodes
node.append("circle").attr("r", function(d) {
return 3 * d.group
});
// add the text
node.append("text").attr("x", 12).attr("dy", ".35em").style(
"color", "black").text(function(d) {
return d.name;
});
// add the links and the arrows
var path = node.selectAll("g").data(force.links(), function(d) {
return d.id
}).append("path")
// .attr("class", function(d) { return "link " + d.type; })
.attr("class", "link").attr("marker-end", "url(#end)");
// add the curvy lines
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x, dy = d.target.y
- d.source.y, dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + ","
+ dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
});
</script>
</body>
</html>
I solved my issue with mouseover and mouseout:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta name="author" content="myborobudur">
<meta name="date" content="2014-09-02T00:00:00+01:00">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Job Recommender: d3js Graph Study</title>
<script src="http://d3js.org/d3.v3.js"></script>
<style>
.node {
opacity: 0.8;
stroke: #fff;
stroke-width: 1.5px;
}
.text {
pointer-events: none;
font: 12px sans-serif;
stroke-width: 0px;
color: #000;
}
.node:hover {
opacity: 1;
}
path.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<h2>Job Recommender: d3js Graph Study</h2>
<script>
function selectLinks(thisObject) {
changeLinkStyle(thisObject, function(node, link) {
link.style("stroke", node.attr("fillColor"));
link.style("stroke-width", "10px");
link.style("opacity", "0.5");
link.attr("marker-end", "");
});
}
function deSelectLinks(thisObject) {
changeLinkStyle(thisObject, function(node, link) {
link.style("stroke", "#666");
link.style("stroke-width", "1.5px");
link.style("opacity", "1");
link.attr("marker-end", "url(#end)");
});
}
function changeLinkStyle(thisObject, changeStyle) {
var source = d3.select(thisObject);
var sourceId = d3.select(thisObject).attr('id');
d3.selectAll('.link').each(function(d, i) {
var link = d3.select(this);
var linkSourceId = link.attr('source');
if (linkSourceId === sourceId) {
changeStyle.call(undefined, source, link);
}
});
}
// get the data
d3.json("data.json", function(error, graph) {
var color = d3.scale.category20();
var width = 1200, height = 800;
var force = d3.layout.force().nodes(graph.nodes).links(graph.links)
.size([ width, height ])
.linkDistance(300)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// build the arrow.
svg.append("svg:defs").selectAll("marker")
.data([ "end" ]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", "link")
.attr("source", function(d) { return d.source.id })
.attr("marker-end", "url(#end)");
// define the nodes
var node = svg.selectAll(".node").data(force.nodes())
.enter().append("g").call(force.drag);
// add the nodes
node.append("circle").attr("id", function(d) { return d.id })
.attr("fillColor", function(d) { return color(d.group); })
.attr("onmouseover", "selectLinks(this)")
.attr("onmouseout", "deSelectLinks(this)")
.attr("class", "node")
.attr("r", function(d) { return 3 * d.group })
.style("fill", function(d) { return color(d.group); });
// add the text
node.append("text").attr("x", 12)
.attr("dy", ".35em")
.attr("class", "text")
.text(function(d) { return d.name; });
// add the curvy lines
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x, dy = d.target.y - d.source.y, dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
});
</script>
</body>
</html>
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);
});