I am trying to make this swimlane chart work with latest d3.js version.
So far I have managed to migrate some of the d3.js v2 API's to d3.js V4.5 API but there are some issues that I am not able to figure out.
I am getting "Expected length, "NaN" error which seems to be coming from .call(brush)
plunker with changes I made
function display () {
var rects, labels
, minExtent = d3.timeDay(brush.extent()[0])
, maxExtent = d3.timeDay(brush.extent()[1])
, visItems = items.filter(function (d) { return d.start < maxExtent && d.end > minExtent});
mini.select('.brush').call(brush.extent([minExtent, maxExtent]));
x1.domain([minExtent, maxExtent]);
//x1Offset.range([0, x1(d3.time.day.ceil(now) - x1(d3.time.day.floor(now)))]);
// shift the today line
main.select('.main.todayLine')
.attr('x1', x1(now) + 0.5)
.attr('x2', x1(now) + 0.5);
// update the axis
main.select('.main.axis.date').call(x1DateAxis);
main.select('.main.axis.month').call(x1MonthAxis)
.selectAll('text')
.attr('dx', 5)
.attr('dy', 12);
// upate the item rects
rects = itemRects.selectAll('rect')
.data(visItems, function (d) { return d.id; })
.attr('x', function(d) { return x1(d.start); })
.attr('width', function(d) { return x1(d.end) - x1(d.start); });
rects.enter().append('rect')
.attr('x', function(d) { return x1(d.start); })
.attr('y', function(d) { return y1(d.lane) + .1 * y1(1) + 0.5; })
.attr('width', function(d) { return x1(d.end) - x1(d.start); })
.attr('height', function(d) { return .8 * y1(1); })
.attr('class', function(d) { return 'mainItem ' + d.class; });
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.start, minExtent)) + 2; });
labels.enter().append('text')
.text(function (d) { return 'Item\n\n\n\n Id: ' + d.id; })
.attr('x', function(d) { return x1(Math.max(d.start, minExtent)) + 2; })
.attr('y', function(d) { return y1(d.lane) + .4 * y1(1) + 0.5; })
.attr('text-anchor', 'start')
.attr('class', 'itemLabel');
labels.exit().remove();
}
Related
Hi i am trying to customize this example
http://bl.ocks.org/tjdecke/raw/5558084/
I am trying to have 3 rect under a 1 hour (hour label).
function (error, data) {
console.log(data);
var colorScale = d3.scale.quantile()
.domain([0, buckets - 1, d3.max(data, function (d) {
return d.value;
})])
.range(colors);
var cards = svg.selectAll(".hour")
.data(data, function (d) {
return d.day + ':' + d.hour;
});
cards.append("title");
cards.enter().append("rect")
.attr("x", function (d) {
return (d.hour - 1) * gridSize;
})
.attr("y", function (d) {
return (d.day - 1) * gridSize;
})
.attr("rx", 4)
.attr("ry", 4)
.attr("class", "hour bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", colors[0]).append('rect');
cards.transition().duration(1000)
.style("fill", function (d) {
return colorScale(d.value);
});
cards.select("title").text(function (d) {
return d.value;
});
cards.exit().remove();
here is code that I tried to customize.
I am pretty much new to d3 and i'm working on a d3 project with a friend for a couple of weeks now.
We built a website containing a sankey diagram and a filter that influences the thickness of links and nodes. Therefore the filter has updateSankey() as an event Handler for the change event.
The links are black with stroke-opacity: 0.15
Lately we tried to introduce a feature that appends a linear gradient to a path onmouseover and removes it onmouseout
To make this work we added an eventHandler to each path which calls a function on both the events. in the functions we append or remove the linear gradient. The gradient goes from the color of the source-node to the color of the target-node.
The problem: after filtering, when all the links have been transitioned the source-node and target-node inside the eventhandler isn't updated and therefore the gradient has wrong colors.
this is how it should look like, it works properly if i don't change the filter on the left
as soon as i change the filter on the left, the colors get messed up
I think we have to do a transition to update these colors, but i have absolutely no idea how and where i have to do this, so i would be glad if you guys could help me.
Down below you find all relevant functions as they currently are.
Greetings and thanks alot in advance
bäsi
/**
* Initialize Sankey
*/
function initSankey() {
/*simple initialisation of the sankey, should explain itself*/
svg = d3.select("svg"),
width = +svg.attr("width") - 2*marginleft,
height = +svg.attr("height") - margintop;
formatNumber = d3.format(",.0f"),
format = function (d) { return formatNumber(d) + " %"; },
color = d3.scaleOrdinal(d3.schemeCategory10);
sankey = d3.sankey()
.nodeWidth(15)
.nodePadding(10)
.extent([[1, 1], [width - 1, height - 6]])
.iterations(0);
t = d3.transition()
.duration(1500)
.ease(d3.easeLinear);
//set attributes for all links
titleGroup = svg.append("g")
.attr("class", "titles")
.attr("font-family", "sans-serif")
.attr("font-size", "150%");
diagram= svg.append("g")
.attr("class", "sankey")
.attr("transform", "translate(" + marginleft + "," + margintop + ")");
linkGroup = diagram.append("g")
.attr("class", "links")
.attr("fill", "none");
//.attr("stroke", "#000")
//.attr("stroke-opacity", 0.2);
//set attributes for all nodes
nodeGroup = diagram.append("g")
.attr("class", "nodes")
.attr("font-family", "sans-serif")
.attr("font-size", 10);
}
/**
* for the filtering and transition by selecting a filter we need to update the sankey and "draw" it new
* */
function updateSankey() {
flush();
filter();
calculateLinks();
switch (lang)
{
case "ger":
d3.json("data/labels-ger.json", helper);
break;
case "fra":
d3.json("data/labels-fr.json", helper);
break;
case "eng":
d3.json("data/labels-en.json", helper);
break;
default:
d3.json("data/labels.json", helper);
}
}
/**
* the main function for "drawing" the saneky, takes the customLinks that where calculated and returns the saneky
* */
function helper(error, labels) {
if (error)
throw error;
labels.links = customLinks;
sankey(labels);
var links = linkGroup.selectAll('path')
.data(labels.links);
//Set attributes for each link separately
links.enter().append("g")
.attr("id",function (d,i) {return "path"+i;})
.attr("from",function (d) { return d.source.name; })
.attr("to",function (d) { return d.target.name; })
.append("path")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.15)
.attr("display", function (d) {
/* don't display a link if the link is smaller than 4%, else it will be just displayed*/
if(d.value < 4.0){return "none";}
else{return "inline";}
})
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke-width", function (d) {return Math.max(1, d.width); })
.on("mouseover",function (d,id) {
var pathGroup = svg.select('#path' + id);
var path = pathGroup.select("path");
/*var from = document.getElementById("path" + id).__data__.source;
var to = document.getElementById("path" + id).__data__.target;
console.log(from)
console.log(to)
*/
var pathGradient = pathGroup.append("defs")
.append("linearGradient")
.attr("id","grad" + id)
.attr("gradientUnit","userSpaceOnUse")
.attr("style","mix-blend-mode: multiply;")
.attr("x1","0%")
.attr("x2","100%")
.attr("y1","0%")
.attr("y2","0%");
pathGradient.append("stop")
.attr("class","from")
.attr("offset","0%")
.attr("style", function (d) {
var color = setColor(d.source);
return "stop-color:" + color + ";stop-opacity:1";
});
pathGradient.append("stop")
.attr("class","to")
.attr("offset","100%")
.attr("style",function (d) {
var color = setColor(d.target);
return "stop-color:" + color + ";stop-opacity:1";
});
path.attr("stroke","url(#grad"+id+")")
.attr("stroke-opacity","0.95");
})
//.attr("onmouseover",function (d,i) { return "appendGradient(" + i + ")" })
.on("mouseout",function (d, id) {
pathGroup = svg.select('#path' + id);
var path = pathGroup.select("path");
var pathGradient = pathGroup.select("defs")
.remove();
path.attr("stroke","#000")
.attr("stroke-opacity","0.15");
})
//.attr("onmouseout",function (d,i) { return "removeGradient(" + i + ")" })
.append("title")
.text(function (d) {
//tooltip info for the links
return d.source.name + " → " + d.target.name + "\n" + format(d.value); });
linkGroup.selectAll("g").transition(t)
.attr("id",function (d,i) {return "path"+i;})
.attr("from",function (d) { return d.source.name; })
.attr("to",function (d) { return d.target.name; });
links.transition(t)
.attr("display", function (d) {
//again if the link is smaller than 4% don't display it, we have to do this method again because of the
// transition, if another filter is selected
if(d.value < 4.0){return "none";}
else{return "inline";}
})
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke-width", function (d) { return Math.max(1, d.width); })
.select('title')
.text(function (d) {
//same argumentation as above, we need the method again for the transition
return d.source.name + " → " + d.target.name + "\n" + format(d.value); });
//remove the unneeded links
links.exit().remove();
var nodes = nodeGroup.selectAll('.node')
.data(labels.nodes);
var nodesEnter = nodes.enter()
.append("g")
.attr('class', 'node');
//set attributes for each node separately
nodesEnter.append("rect")
.attr("x", function (d) { return d.x0; })
.attr("y", function (d) { return d.y0; })
.attr("height", function (d) { return d.y1 - d.y0; })
.attr("width", function (d) {
var width = d.x1 - d.x0;
if(d.value > 0)
{
//this is used for the years above the nodes, every x position of all nodes is pushed in an array
columnCoord.push(d.x0 + width/2);
}
return width;
})
.attr("fill", setColor)
.attr("stroke", "#000")
.attr("fill-opacity", 0.5)
//specify Pop-Up when hovering over node
nodesEnter.append("title")
.text(function (d) { return d.name + "\n" + format(d.value); });
//Update selection
var nodesUpdate = nodes.transition(t);
//same as the links we have to state the methods again in the update
nodesUpdate.select("rect")
.attr("y", function (d) { return d.y0; })
.attr("x", function (d) { return d.x0; })
.attr("height", function (d) { return d.y1 - d.y0; });
nodesUpdate.select("title")
.text(function (d) { return d.name + "\n" + format(d.value); });
//Exit selection
nodes.exit().remove();
//we filter all arrays
columnCoord = filterArray(columnCoord);
if(!titlesDrawn)
{
drawTitles();
titlesDrawn = true;
}
}
I am trying to upgrade this timeline chart code from v3 to v4. I have a bug with the brush I am unsure how to resolve.
//current jsfiddle
http://jsfiddle.net/0ht35rpb/173/
I am getting the error "Cannot read property '0' of undefined"
I've tried to follow this example
https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172
are the brush properties different? The extent properties still valid?
//brush
var brush = d3.brushX()
//.x(x)
.on("brush", display);
function display() {
var rects, labels,
minExtent = brush.extent()[0],
maxExtent = brush.extent()[1],
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";})
.attr("x", function(d) {return x1(d.starting_time);})
.attr("y", function(d) {return y1(d.lane) + 10;})
.attr("fill", function(d, i) {
return colores_background(d.lane);
})
.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("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'm trying to combine a D3 Pack Layout with links. It works in the initial state but when I try to zoom the positions of the svg path elements are not updated. How can I translate paths such that they are aligned to the nodes when the user performs zooming? Below is my zoom function.
To check the whole code, see js fiddle. I hope somebody can help me!
function zoom(d, i) {
var k = r / d.r / 2;
x.domain([d.x - d.r, d.x + d.r]);
y.domain([d.y - d.r, d.y + d.r]);
var t = vis.transition()
.duration(d3.event.altKey ? 7500 : 750);
t.selectAll("circle")
.attr("cx", function(d) {
return x(d.x);
})
.attr("cy", function(d) {
return y(d.y);
})
.attr("r", function(d) {
return k * d.r;
});
t.selectAll("text")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y);
})
.style("opacity", function(d) {
return k * d.r > 20 ? 1 : 0;
});
console.log(t.selectAll('path'));
d3.selectAll('path')
.attr('class', function() {
// TODO: diversify
return 'child-branch';
})
.style('stroke', function(d) {
return d3.rgb(d3.scale.category20(d.target.by_alliance_with + 1)).darker();
})
.attr('d', function(d) {
console.log("d", d);
return linkArc(d);
});
I have a Marimekko chart where the bars are aligned vertically (with a limited number of bars).
However, the eventual chart will have so many bars that it would be better with a horizontal layout which would support a larger number of values.
I've tried to modify the chart by reversing x and y values but the result does not work properly. I want the first month in the data to appear at the top of the chart.The working code with vertical bars (no data) is below and here.
var width = 700,
height = 500,
margin = 20;
var color = d3.scale.category20();
var x = d3.scale.linear()
.range([0, width - 3 * margin]);
var y = d3.scale.linear()
.range([0, height - 2 * margin]);
var n = d3.format(",d"),
p = d3.format("%");
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 2 * margin + "," + margin + ")");
d3.json("/mydrupal/sites/default/d3_files/json/marimekko6.json",
function(error,data) {
var offset = 0;
// Nest values by month. We assume each month + cause is unique.
var months = d3.nest()
.key(function(d) {
return d.month;
})
.entries(data);
// Compute the total sum, the per-month sum, and the per-cause offset.
// You can use reduce rather than reduceRight to reverse the ordering.
// We also record a reference to the parent cause for each month.
var sum = months.reduce(function(v, p) {
return (p.offset=v) + (p.sum=p.values.reduceRight(function(v, d) {
d.parent = p;
return (d.offset = v) + d.deaths;
}, 0));
}, 0);
// Add a group for each cause.
var months = svg.selectAll(".month")
.data(months)
.enter()
.append("g")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.key;
})
.attr("transform", function(d) {
return "translate(" + x(d.offset / sum) + ")";
});
// Add a rect for each month.
var causes = months.selectAll (".cause")
.data(function(d) {
return d.values;
})
.enter()
.append("a")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.cause + " " + d.parent.key + ": " + n(d.deaths);
});
causes.append("rect")
.attr("y", function(d) {
return y(d.offset / d.parent.sum);
})
.attr("height", function(d) {
return y(d.deaths / d.parent.sum);
})
.attr("width", function(d) {
return x(d.parent.sum / sum);
})
.style("fill", function(d) {
return color(d.cause);
});
// see http://stackoverflow.com/questions/17574621/
// text-on-each-bar-of-a-stacked-bar-chart-d3-js
causes.append("text")
.text(function(d) {
return d.cause + " " + n(d.deaths);
})
.attr("x", 5)
.attr("y", function(d) {
return (y(d.offset / d.parent.sum)+20);
})
.attr("class", "label");
causes.append("text")
.text(function(d) {
return (" Total: " + d.parent.sum);
}) // total
.attr("x", 5)
.attr("y", function(d) {
return 450;
})
.attr("class", "label2");
causes.append("text")
.text(function(d) {
return d.parent.key;
}) // month
.attr("x", 5)
.attr("y", function(d) {
return 480;
})
.attr("class", "label2");
});
Here's the fix. Basically you need to change x and y as well as width and the height.
// Add a group for each cause.
var months = svg.selectAll(".month")
.data(months)
.enter().append("g")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.key; })
.attr("transform", function(d) {
return "translate(0," + y(d.offset / sum) + ")";
});
// Add a rect for each month.
var causes = months.selectAll (".cause")
.data(function(d) {
return d.values; })
.enter().append("a")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.cause + " " + d.parent.key + ": " + n(d.deaths); });
causes.append("rect")
.attr("x", function(d) {
return x(d.offset / d.parent.sum); })
.attr("width", function(d) {
return x(d.deaths / d.parent.sum); })
.attr("height", function(d) {
return y(d.parent.sum / sum); })
.style("fill", function(d) {
return color(d.cause);
});