D3 Adding arrowheads to edges, directed graph - javascript

I would like to add directionality to my D3 graph. The json will have an attribute called "direction" and it will either be "--", "<-", or "->"
meaning:
source <- target (arrow from target to source)
source -> target (arrow from source to target)
source -- target (no direction)
I would like it to match the color of the links if possible (not crucial)
Any assistance would be welcome!
My script below
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
.axis {
opacity: 0.5;
font: 10px sans-serif;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.axis .domain {
fill: none;
stroke: #000;
stroke-opacity: .3;
stroke-width: 4px;
stroke-linecap: round;
}
.axis .halo {
fill: none;
stroke: #ddd;
stroke-width: 3px;
stroke-linecap: round;
}
text {
pointer-events: none;
font: 8px sans-serif;
stroke: none;
fill: black;
}
.slider .handle {
fill: #fff;
stroke: #000;
stroke-opacity: .5;
stroke-width: 1.25px;
cursor: grab;
}
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var x = d3.scale.linear()
.domain([0, 20])
.range([250, 80])
.clamp(true);
var brush = d3.svg.brush()
.y(x)
.extent([0, 0]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var links_g = svg.append("g");
var nodes_g = svg.append("g");
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + (width - 20) + ",0)")
.call(d3.svg.axis()
.scale(x)
.orient("left")
.tickFormat(function(d) {
return d;
})
.tickSize(0)
.tickPadding(12))
.select(".domain")
.select(function() {
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "halo");
var slider = svg.append("g")
.attr("class", "slider")
.call(brush);
slider.selectAll(".extent,.resize")
.remove();
var handle = slider.append("circle")
.attr("class", "handle")
.attr("transform", "translate(" + (width - 20) + ",0)")
.attr("r", 5);
svg.append("text")
.attr("x", width - 15)
.attr("y", 60)
.attr("text-anchor", "end")
.attr("font-size", "12px")
.style("opacity", 0.5)
.text("degree threshold")
d3.json("test.json", function(error, graph) {
if (error) throw error;
graph.links.forEach(function(d, i) {d.i = i;});
graph.nodes.forEach(function(d, i) {d.i = i;});
function brushed() {
var value = brush.extent()[0];
if (d3.event.sourceEvent) {
value = x.invert(d3.mouse(this)[1]);
brush.extent([value, value]);
}
handle.attr("cy", x(value));
var threshold = value;
var thresholded_links = graph.links.filter(function(d) {
return (d.max_degree > threshold);
});
force
.links(thresholded_links);
var link = links_g.selectAll(".link")
.data(thresholded_links, function(d) {
return d.i;
});
link.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
link.exit().remove();
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 + ")";
});
});
force.start();
//console.log(link);
link.each(function(d) {
d3.select(this).style("stroke", d.min_degree >= value ? "#3182bd" : "#ccc")
});
node.each(function(d) {
d3.select(this).select("circle").style("fill", d.degree > value ? color(1) : d.weight > 0 ? color(2) : "#ccc")
});
node.each(function(d) {
d3.select(this).select("text").style("fill", d.degree > value ? color(1) : d.weight > 0 ? color(2) : "#ccc")
});
}
force
.nodes(graph.nodes);
var node = nodes_g.selectAll(".node")
.data(graph.nodes)
.enter()
.append('g')
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 5)
.style("fill", function(d) {
return color(1);
})
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.name + "\ndegree:" + d.degree)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
node.append("text")
.attr("dx", 4)
.attr("dy", ".35em")
.text(function(d) {
return d.name;
});
brush.on("brush", brushed);
slider
.call(brush.extent([16.5, 16.5]))
.call(brush.event);
});
</script>
Here is a sample json: test.json

I implemented something like what I think you're trying to achieve in a geojson sankey. Maybe you can use some of the code. Here's a link to the block.
And part of the relevant code:
var arrow = L.polylineDecorator(line, { patterns: [{
offset: '55%',
symbol: L.Symbol.arrowHead({
polygon: true,
pathOptions: {
weight: lineWeight,
color: lineColor
}
})
}]});
Hope this helps.

Related

Trying to create line chart with Circle Tooltip

I am trying to create an interactive line chart with circle tooltip like the following https://bl.ocks.org/alandunning/cfb7dcd7951826b9eacd54f0647f48d3 .
My code is below:
HTML
<head>
<style>
circle {
fill: steelblue;
}
body {
font: 12px Arial;
}
path {
stroke: steelblue;
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: 80px;
height: 64px;
padding: 2px;
font: 14px sans-serif;
color: black;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: #F1F3F3;
stroke: #6F257F;
stroke-width: 5px;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3, 3;
}
</style>
</head>
<body>
<svg class='line-chart2'></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="./regression.js"></script>
</body>
regression.js
var gdp = [387.65, 410.32, 415.73, 452.69, 462.14,
478.96, 508.06, 599.59, 699.68, 808.90,
920.31, 1201.11, 1186.95, 1323.94, 1656.61,
1823.04, 1827.63, 1856.72, 2039.12, 2102.39,
2274.22, 2600.81
]; //y or GDP of India
var years = ['1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017'];
var data_gdp = []
for (i = 0; i < years.length; i++) {
data_gdp.push({
year: years[i],
value: gdp[i]
})
}
function drawChart_gdp(data, class_name) {
var svgWidth = 1200,
svgHeight = 400;
var margin = {
top: 60,
right: 60,
bottom: 60,
left: 60
};
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
var svg = d3.select(class_name)
.attr("width", svgWidth)
.attr("height", svgHeight);
var bisectDate = d3.bisector(function(d) {
return d.year;
}).left;
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")"
);
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var line = d3.line()
.x(function(d) {
return x(new Date(parseInt(d.year), 0))
})
.y(function(d) {
return y(d.value)
})
x.domain(d3.extent(data, function(d) {
return new Date(parseInt(d.year), 0);
}));
y.domain(d3.extent(data, function(d) {
return d.value
}));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("fill", "#000")
.text("Year")
.attr("dy", "1.90em")
.attr("y", 5)
.attr("x", 500)
.attr("font-size", "20px")
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", -80)
.attr("x", -55)
.attr("dy", "1.90em")
.attr("text-anchor", "center")
.attr("font-size", "20px")
.text("GDP ($)")
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
var focus = g.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("line")
.attr("class", "x-hover-line hover-line")
.attr("y1", 0)
.attr("y2", height);
focus.append("line")
.attr("class", "y-hover-line hover-line")
.attr("x1", width)
.attr("x2", width);
focus.append("circle")
.attr("r", 7.5);
focus.append("text")
.attr("x", 15)
.attr("dy", ".31em");
svg.append("rect")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", function() { //problem in this function
var x0 = d3.timeFormat("%Y")(x.invert(d3.mouse(this)[0])),
i = bisectDate(data, x0, 1);
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.year > d1.year - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");
focus.select("text").text(function() {
return d.value;
});
focus.select(".x-hover-line").attr("y2", height - y(d.value));
focus.select(".y-hover-line").attr("x2", width + width);
});
}
drawChart_gdp(data_gdp, '.line-chart2');
I am suspecting that in the example the data is being pulled from json file but here is pulled from an array where the problem comes for and also I think the kind of data is little bit different too. My goals is just to create a circle tool which shows the value of y-axis.
The problem you're facing now is almost the opposite of the one you faced in your previous question: in that question, the problem was that you were dealing with date objects as if they were strings.
Now, you're dealing with strings as if they were date objects. In your data, the year is just a string, like "1996".
So, this:
focus.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");
Should be:
focus.attr("transform", "translate(" + x(d3.timeParse("%Y")(d.year)) + "," + y(d.value) + ")");
//parsing to a date here----------------------^
Here is the code with that change:
var gdp = [387.65, 410.32, 415.73, 452.69, 462.14,
478.96, 508.06, 599.59, 699.68, 808.90,
920.31, 1201.11, 1186.95, 1323.94, 1656.61,
1823.04, 1827.63, 1856.72, 2039.12, 2102.39,
2274.22, 2600.81
]; //y or GDP of India
var years = ['1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017'];
var data_gdp = []
for (i = 0; i < years.length; i++) {
data_gdp.push({
year: years[i],
value: gdp[i]
})
}
function drawChart_gdp(data, class_name) {
var svgWidth = 1200,
svgHeight = 400;
var margin = {
top: 60,
right: 60,
bottom: 60,
left: 60
};
var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;
var svg = d3.select(class_name)
.attr("width", svgWidth)
.attr("height", svgHeight);
var bisectDate = d3.bisector(function(d) {
return d.year;
}).left;
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")"
);
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var line = d3.line()
.x(function(d) {
return x(new Date(parseInt(d.year), 0))
})
.y(function(d) {
return y(d.value)
})
x.domain(d3.extent(data, function(d) {
return new Date(parseInt(d.year), 0);
}));
y.domain(d3.extent(data, function(d) {
return d.value
}));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("fill", "#000")
.text("Year")
.attr("dy", "1.90em")
.attr("y", 5)
.attr("x", 500)
.attr("font-size", "20px")
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", -80)
.attr("x", -55)
.attr("dy", "1.90em")
.attr("text-anchor", "center")
.attr("font-size", "20px")
.text("GDP ($)")
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
var focus = g.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("line")
.attr("class", "x-hover-line hover-line")
.attr("y1", 0)
.attr("y2", height);
focus.append("line")
.attr("class", "y-hover-line hover-line")
.attr("x1", width)
.attr("x2", width);
focus.append("circle")
.attr("r", 7.5);
focus.append("text")
.attr("x", 15)
.attr("dy", ".31em");
svg.append("rect")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", function() { //problem in this function
var x0 = d3.timeFormat("%Y")(x.invert(d3.mouse(this)[0])),
i = bisectDate(data, x0, 1);
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.year > d1.year - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d3.timeParse("%Y")(d.year)) + "," + y(d.value) + ")");
focus.select("text").text(function() {
return d.value;
});
focus.select(".x-hover-line").attr("y2", height - y(d.value));
focus.select(".y-hover-line").attr("x2", width + width);
});
}
drawChart_gdp(data_gdp, '.line-chart2');
<head>
<style>
circle {
fill: steelblue;
}
body {
font: 12px Arial;
}
path {
stroke: steelblue;
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: 80px;
height: 64px;
padding: 2px;
font: 14px sans-serif;
color: black;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: #F1F3F3;
stroke: #6F257F;
stroke-width: 5px;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3, 3;
}
</style>
</head>
<body>
<svg class='line-chart2'></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
</body>
As you know by now, all this confusion comes to the fact that you have strings in your data array, but x is a time scale. My advice: parse the strings in your data array, creating date objects, and be consistent in your code, dealing all data as date objects, not strings.

How to change the color of a single line in a Voronoi diagram?

I am working with D3 voronoi. I have several lines and would like to have only one of them have a specific color, while all the others have a different color. I have tried using an if statement, but to no avail. Below is my code:
Style:
.axis--y path {
display: none;
fill: none;
stroke: #17BED5;
stroke-width: 1.0;
}
.axis--y line {
stroke: lightgrey;
opacity: 1
}
.cities {
fill: none;
stroke: #000000;
stroke-linejoin: round;
stroke-linecap: round;
stroke-width: 1px;
}
.city--hover {
stroke: #4575b4;
stroke-width: 2px;
}
.focus text {
text-anchor: middle;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
.voronoi path {
fill: none;
pointer-events: all;
}
.voronoi--show path {
stroke: red;
stroke-opacity: 0.2;
}
#form {
position: absolute;
top: 20px;
right: 30px;
}
</style>
JavaScript:
var years,
yearKeys,
yearParse = d3.timeParse("%Y");
//Sets out the margins of the chart frm the edges of the page/screen.
var svg = d3.select("svg"),
margin = {top: 20, right: 30, 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 + ")");
//Now we define the x and y axes and their respective scales. X will be the date, and y will be the unemployment rates.
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var voronoi = d3.voronoi()
.x(function(d) {
return x(d.date); })
.y(function(d) {
return y(d.value); })
.extent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]]);
//And we set out the values of our lines.
var line = d3.line()
.x(function(d) {
return x(d.date); })
.y(function(d) {
return y(d.value); });
//Now we call our chart.
d3.csv("data/eloVoronoi.csv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(years));
y.domain([1500, d3.max(data, function(c) {
return d3.max(c.values, function(d) {
return d.value; }); })]).nice();
//We tell the computer that we want our x-axis to be at the bottom.
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
//And the y axis on the left, with some style attributes.
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y)
.ticks(6)
.tickSize(-width))
.append("text")
.attr("transform", "rotate(360)")
.attr("x", 4)
.attr("y", -14)
.attr("dy", "1em")
.style("text-anchor", "start")
.style("fill", "black")
.style("font-weight", "bold")
.text("Elo Rating");
g.append("g")
.attr("class", "cities")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("d", function(d) { d.line = this;
return line(d.values); });
var focus = g.append("g")
.attr("transform", "translate(-100,-100)")
.attr("class", "focus");
focus.append("circle")
.attr("r", 3.5);
focus.append("text")
.attr("y", -10);
//This is where we group all the vornoi together so tht we can apply the mouseovers all together, instead of one by one.
var voronoiGroup = g.append("g")
.attr("class", "voronoi");
//The mouseovers/outs.
voronoiGroup.selectAll("path")
.data(voronoi.polygons(d3.merge(data.map(function(d) {
return d.values; }))))
.enter().append("path")
.attr("d", function(d) {
return d ? "M" + d.join("L") + "Z" : null; })
.on("mouseover", mouseover)
.on("mouseout", mouseout);
//
d3.select("#show-voronoi")
.property("disabled", false)
.on("change", function() { voronoiGroup.classed("voronoi--show", this.checked); });
//Helper function to assist in the mouseover.
function mouseover(d) {
d3.select(d.data.city.line).classed("city--hover", true);
d.data.city.line.parentNode.appendChild(d.data.city.line);
focus.attr("transform", "translate(" + x(d.data.date) + "," + y(d.data.value) + ")");
focus.select("text").text(d.data.city.name);
}
//And a helper function for the mouseout.
function mouseout(d) {
d3.select(d.data.city.line).classed("city--hover", false);
focus.attr("transform", "translate(-100,-100)");
}
});
//Now we use an if statement to call our chart onto the page.
function type(d, i, columns) {
if (!years) yearKeys = columns.slice(1), years = yearKeys.map(yearParse);
var c = {name: d.name.replace(/ (msa|necta div|met necta|met div)$/i, ""), values: null};
c.values = yearKeys.map(function(k, i) {
return {city: c, date: years[i], value: d[k] / 1}; });
return c;
}

Ordinal brushing in parallel coordinates in d3 V4?

I am looking for parallel coordinate visualization in d3.js that has both ordinal and numerical values in d3 V4. I have created a sample http://plnkr.co/edit/TiM6ZsvMTTBhh8ZdMoFQ?p=preview
I am trying to brush the 'name' column as well. It seems there are lots of changes in d3 brush in V4. Also is there a way where I can snap the brushing area in both cases of ordinal and numerical columns.
Any help would be highly appreciated.
Thanks
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.background path {
fill: none;
stroke: #ddd;
stroke-opacity: .4;
shape-rendering: crispEdges;
}
.foreground path {
fill: none;
stroke: steelblue;
stroke-opacity: .7;
}
.brush .extent {
fill-opacity: .3;
stroke: #fff;
shape-rendering: crispEdges;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
cursor: move;
}
</style>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script>
var margin = {top: 30, right: 10, bottom: 10, left: 10},
width = 600 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(1),
y = {},
dragging = {};
var line = d3.line(),
//axis = d3.axisLeft(x),
background,
foreground,
extents;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var quant_p = function(v){return (parseFloat(v) == v) || (v == "")};
d3.csv("cars.csv", function(error, cars) {
// Extract the list of dimensions and create a scale for each.
//cars[0] contains the header elements, then for all elements in the header
//different than "name" it creates and y axis in a dictionary by variable name
console.log("ok");
dimensions = d3.keys(cars[0]);
x.domain(dimensions);
dimensions.forEach(function(d) {
var vals = cars.map(function(p) {return p[d];});
if (vals.every(quant_p)){
y[d] = d3.scaleLinear()
.domain(d3.extent(cars, function(p) {
return +p[d]; }))
.range([height, 0])
}
else{
y[d] = d3.scalePoint()
.domain(vals.filter(function(v, i) {return vals.indexOf(v) == i;}))
.range([height, 0],1);}
})
extents = dimensions.map(function(p) { return [0,0]; });
// Add grey background lines for context.
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path);
// Add blue foreground lines for focus.
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path);
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; })
.call(d3.drag()
.subject(function(d) { return {x: x(d)}; })
.on("start", function(d) {
dragging[d] = x(d);
background.attr("visibility", "hidden");
})
.on("drag", function(d) {
dragging[d] = Math.min(width, Math.max(0, d3.event.x));
foreground.attr("d", path);
dimensions.sort(function(a, b) { return position(a) - position(b); });
x.domain(dimensions);
g.attr("transform", function(d) { return "translate(" + position(d) + ")"; })
})
.on("end", function(d) {
delete dragging[d];
transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
transition(foreground).attr("d", path);
background
.attr("d", path)
.transition()
.delay(500)
.duration(0)
.attr("visibility", null);
}));
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(d3.axisLeft(y[d]));})
//text does not show up because previous line breaks somehow
.append("text")
.attr("fill", "black")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d; });
// Add and store a brush for each axis.
g.append("g")
.attr("class", "brush")
.each(function(d) {
d3.select(this).call(y[d].brush = d3.brushY().extent([[-8, 0], [8,height]]).on("brush start", brushstart).on("brush", brush_parallel_chart));
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
});
function position(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
function transition(g) {
return g.transition().duration(500);
}
// Returns the path for a given data point.
function path(d) {
return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; }));
}
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
// Handles a brush event, toggling the display of foreground lines.
function brush_parallel_chart() {
for(var i=0;i<dimensions.length;++i){
if(d3.event.target==y[dimensions[i]].brush) {
extents[i]=d3.event.selection.map(y[dimensions[i]].invert,y[dimensions[i]]);
}
}
foreground.style("display", function(d) {
return dimensions.every(function(p, i) {
if(extents[i][0]==0 && extents[i][0]==0) {
return true;
}
return extents[i][1] <= d[p] && d[p] <= extents[i][0];
}) ? null : "none";
});
}
</script>

D3.js SVG wired size

I'm new to D3 and doing a project with student data from my college. I was following some tutorials to create a multi-line chart and I can't figure out what happened to my SVG. I set my height and width but it displays wired on screen. Here is my code:
<body>
<p>Students' Major by Gender.</p>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var MARGINS = {top: 50, right: 20, bottom: 50, left: 50},
WIDTH = 1500 - MARGINS.left - MARGINS.right,
HEIGHT = 800 - MARGINS.top - MARGINS.bottom;
var xScale = d3.scale.linear().range([0, WIDTH]),
yScale = d3.scale.linear().range([HEIGHT, 0]),
xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom").ticks(5),
yAxis = d3.svg.axis()
.scale(yScale)
.orient("left").ticks(5);
var svg = d3.select("body")
.append("svg")
.attr("WIDTH", WIDTH + MARGINS.left + MARGINS.right)
.attr("HEIGHT", HEIGHT + MARGINS.top + MARGINS.bottom)
.append("g")
.attr("transform", "translate(" + MARGINS.left + "," + MARGINS.top + ")");
var colorScale = ["#FF0000", "#0000FF"];
var color = d3.scale.ordinal().range(colorScale);
d3.json("data/majors/major_ARCH.json", function(data) {
data.forEach(function(d) {
d.Year = +d.Year;
d.Gndr = d.Gndr;
d.Count = +d.Count;
});
function sortByDateAscending(a, b) {
//Years will be cast to numbers automagically:
return a.Year - b.Year;
}
data = data.sort(sortByDateAscending);
console.log(data);
var dataNest = d3.nest()
.key(function(d) { return d.Gndr; })
.entries(data);
var lSpace = WIDTH/dataNest.length;
var GenderLine = d3.svg.line()
.x(function(d) {
return xScale(d.Year);
})
.y(function(d) {
return yScale(d.Count);
})
xScale.domain([d3.min(data, function(d) {
return d.Year;
}), d3.max(data, function(d) {
return d.Year;
})]);
yScale.domain([d3.min(data, function(d) {
return d.Count;
}), d3.max(data, function(d) {
return d.Count;
})]);
dataNest.forEach(function(d, i) {
svg.append("path")
.attr('d', GenderLine(d.values, xScale, yScale))
.attr('stroke', function() {
return d.color = color(d.key);
})
.attr('stroke-width', 2)
.attr('id', 'line_' + d.key)
.attr('fill', 'none');
svg.append("text")
.attr("x", (lSpace / 2) + i * lSpace)
.attr("y", HEIGHT)
.style("fill", "black")
.attr("class", "legend")
.on('click', function() {
var active = d.active ? false : true;
var opacity = active ? 0 : 1;
d3.select("#line_" + d.key).style("opacity", opacity);
d.active = active;
})
.attr("stroke", function() {
return d.color = color(d.key);
})
.text(d.key);
})
svg.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("r", 5)
.attr("cx", function(d) { return xScale(d.Year); })
.attr("cy", function(d) { return yScale(d.Count); })
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9)
div.html(d.Year + "<br/>" +d.Count)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + HEIGHT + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
CSS:
.axis path {
fill: none;
stroke: #777;
shape-rendering: crispEdges;
}
.axis text {
font-family: Lato;
font-size: 13px;
}
/* Legend */
.legend {
font-size: 14px;
font-weight: bold;
}
/*Tooltip*/
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
I tried to follow a post (Resize svg when window is resized in d3.js) to create a responsive svg but it was not working...
The two tutorials that I used to create my chart are:
http://bl.ocks.org/d3noob/a22c42db65eb00d4e369
https://code.tutsplus.com/tutorials/building-a-multi-line-chart-using-d3js-part-2--cms-22973
I want to make my svg responsive, and know the problems with my code. Thanks!

Enable scroll on the axis of D3 chart

I have a simple stacked bar chart :
The code is here.
I would like to have scroll-bar on the axis but as you can see in the link the scroll appears for the div container with the help of CSS.
But i need something like this chart with scroll!
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<script src="https://ajax.goquery.min.js"></script>
<style>
.axis path,
.axis line {
fill: none;
stroke: #BDBDBD;
}
.axis text {
font-family: 'Open Sans regular', 'Open Sans';
font-size: 13px;
}
.y.axis{
direction: ltr;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
.rect {
stroke: lightgrey;
fill-opacity: 0.6;
}
.wrapperDiv {
Width: 984px;
height: 35px;
border: thin solid black;
#margin-top: 36px;
#margin-bottom: 48px;
#margin-right: 20px;
#margin-left: 0px;
}
.divChart {
float:left;
font-size:13px;
color : #424242;
font-family: 'Open Sans regular', 'Open Sans';
#border: thin solid white;
margin-top: -0px;
#margin-bottom: 48px;
#margin-right: 150px;
margin-left: 50px;
#background-color: lightgrey;
width: 984px;
height: 500px;
#padding: 25px;
border: thin solid navy;
#margin: 25px;
#max-height:500px;
overflow-y:scroll;
direction: rtl;
}
</style>
</head>
<body>
<div class="divChart" id="wrapper-chart">
<div id ="chartID" ></div>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script><script>
<script>
var dataset = [{"key":"Completion","values":[{"name":"Module 1","value":0},{"name":"Module 2","value":0},{"name":"Module 3","value":0},{"name":"Module 4","value":0},{"name":"Module 5","value":0},{"name":"Module 6","value":0},{"name":"Module 7","value":0},{"name":"Module 8","value":0.56},{"name":"Module 9","value":13.24},{"name":"Module 10","value":12.66}]},{"key":"NonCompletion","values":[{"name":"Module 1","value":100},{"name":"Module 2","value":100},{"name":"Module 3","value":100},{"name":"Module 4","value":100},{"name":"Module 5","value":100},{"name":"Module 6","value":100},{"name":"Module 7","value":100},{"name":"Module 8","value":99.44},{"name":"Module 9","value":86.76},{"name":"Module 10","value":87.34}]}];
function intChart(chartID, dataset) {
var margins = {top: 20, right: 20, bottom: 30, left: 120};
var width = 880 - margins.left -margins.right;
var height = 5250- margins.top - margins.bottom;
var old_width = width,old_height= height;
var module_fixed = 80;
height = Math.floor((dataset[0].values.length * height)/module_fixed)
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1,.1)
var y = d3.scale.linear().rangeRound([height, 0], .1);
var series = dataset.map(function(d) {
return d.key;
});
dataset = dataset.map(function(d) {
return d.values.map(function(o, i) {
// Structure it so that your numeric
// axis (the stacked amount) is y
return {
y: o.value,
x: o.name
};
});
});
var stack = d3.layout.stack();
stack(dataset);
var dataset = dataset.map(function(
group) {
return group.map(function(d) {
// Invert the x and y values, and y0 becomes x0
return {
x: d.y,
y: d.x,
x0: d.y0
};
});
});
var xMax = d3.max(dataset, function(
group) {
return d3.max(group, function(d) {
return d.x + d.x0;
});
});
var xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]);
var moduleName = dataset[0]
.map(function(d) {
return d.y;
});
var yScale = d3.scale.ordinal()
.domain(moduleName)
.rangeRoundBands([height,0]);
var svg = d3.select('#chartID')
.append('svg')
.attr("width", width + margins.left +
margins.right)
.attr("height", height + margins.top +
margins.bottom)
.append('g')
.attr('transform', 'translate(60,' + margins.top +
')');
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.ticks(2)
.tickSize(0)
.tickPadding(20)
.tickFormat(function(d) {
return d + "%";
});
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(0);
var colours = d3.scale.ordinal().range(
["#8bc34a", "#ff8a65"]);
var groups = svg.selectAll('g')
.data(dataset)
.enter()
.append('g').attr('class', 'stacked')
.style('fill', function(d, i) {
return colours(i);
});
var rects = groups.selectAll(
'stackedBar')
.data(function(d, i) {
return d;
})
.enter()
.append('rect')
.attr('class', 'stackedBar')
.attr('x', function(d) {
return xScale(d.x0);
})
.attr('y', function(d, i) {
return yScale(d.y);
})
.attr('height', 48)
.attr('width', 0)
rects.transition()
.delay(function(d, i) {
return i * 50;
})
.attr("x", function(d) {
return xScale(d.x0);
})
.attr("width", function(d) {
return xScale(d.x);
})
.duration(3000);
//Added
x.domain(dataset.map(function(d) {
return d.value;
}));
y.domain([0, d3.max(dataset, function(
d) {
return d.name;
})]);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' +height + ')')
.call(xAxis)
.append("text")
.attr("transform", "rotate(360)")
.attr("y",10)
.attr("x", 140)
.attr("dy", ".30em")
.text("Percentage of Students");
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.5em")
.attr("dy", ".15em")
.attr("y", "-")
.attr("opacity", 1)
.attr("transform", function(d) {
return "rotate(-40)"
})
// Draw Y-axis grid lines
svg.selectAll("line.y")
.data(y.ticks(2))
.enter().append("line")
.attr("class", "y")
.attr("x1", 0)
.attr("x2", 450)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#ccc");
}
$(document).ready(function(){
intChart("chartID", dataset);
});
</script>
Would appreciate any help.
Thanks in advance.
I don't think this can be done using D3 only. If you want to use CSS, you need to fix the position of the x-axis. You can add a separate DIV and SVG container for the X axis (these are not scrollable), and the rest of the chart in another.
I modified you code to do this see here. Please note that you code needs a lot of cleaning, as there are several non-functional parts that makes it really confusing.
The modifications are as follows:
HTML
Added a new DIV (xaxis)
<div id="wrapper-chart">
<div class="divChart" id="chartID"></div>
<div id="xaxis"></div>
</div>
CSS
Added styling for the new div (same as divChart but without the scrolling)
#xaxis {
float: left;
font-size: 13px;
color: #424242;
font-family: 'Open Sans regular', 'Open Sans';
width: 984px;
direction: rtl;
}
JS
A new SVG container for the x-axis. Notice the height attribute.
var xaxis_svg = d3.select('#xaxis')
.append('svg')
.attr("width", width + margins.left + margins.right)
.attr("height", margins.bottom)
.append('g')
.attr('transform', 'translate(60,0)');
Append the x-axis to the container.
xaxis_svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + 0 + ')')
.call(xAxis)
.append("text")
.attr("y", 10)
.attr("x", 140)
.attr("dy", ".30em")
.text("Percentage of Students");
Hope this helps.

Categories