Morph areas into bars with D3? - javascript

I'm able to generate the following graph using D3 areas:
I want to create the following animation. When the webpage loads, you see the first figure. Then, each of the areas morphs into a bar. Finally, I would like to allow users to toggle between the two figures by clicking "B" or "D".
I was able to add the buttons and the corresponding bars to my figure, but I'm having troubles figuring out how to do the animation. This is the code that I have right now:
HTMLWidgets.widget({
name: 'IMposterior',
type: 'output',
factory: function(el, width, height) {
// TODO: define shared variables for this instance
return {
renderValue: function(opts) {
console.log("threshold: ", opts.threshold);
console.log("bars: ", opts.bars);
var margin = {left:50,right:50,top:40,bottom:125};
xMax = d3.max(opts.data, function(d) { return d.x ; });
yMax = d3.max(opts.data, function(d) { return d.y ; });
xMin = d3.min(opts.data, function(d) { return d.x ; });
yMin = d3.min(opts.data, function(d) { return d.y ; });
var y = d3.scaleLinear()
.domain([0,yMax])
.range([height-margin.bottom,0]);
var x = d3.scaleLinear()
.domain([xMin,xMax])
.range([0,width]);
var yAxis = d3.axisLeft(y);
var xAxis = d3.axisBottom(x);
var area = d3.area()
.x(function(d){ return x(d.x) ;})
.y0(height-margin.bottom)
.y1(function(d){ return y(d.y); });
var svg = d3.select(el).append('svg').attr("height","100%").attr("width","100%");
var chartGroup = svg.append("g").attr("transform","translate("+margin.left+","+margin.top+")");
chartGroup.append("path")
.attr("d", area(opts.data.filter(function(d){ return d.x< -opts.MME ;})))
.style("fill", opts.colors[0]);
if(opts.MME !==0){
chartGroup.append("path")
.attr("d", area(opts.data.filter(function(d){ return (d.x < opts.MME & d.x > -opts.MME) ;})))
.style("fill", opts.colors[1]);
}
chartGroup.append("path")
.attr("d", area(opts.data.filter(function(d){ return d.x > opts.MME ;})))
.style("fill", opts.colors[2]);
chartGroup.append("g")
.attr("class","axis x")
.attr("transform","translate(0,"+(height-margin.bottom)+")")
.call(xAxis);
var tooltip = d3.tip()
.attr('class', 'd3-tip chart-data-tip')
.offset([30, 0])
.direction('s')
.html(function(d, i) {
return "<strong>" + d + "</strong> <span style='color:" + "white" + "'>"+ "</span>";
});
svg.call(tooltip);
chartGroup.selectAll("path").data(opts.text).on('mouseover', tooltip.show).on('mouseout', tooltip.hide);
// Bars
var yBar = d3.scaleLinear()
.domain([0,1])
.range([height-margin.bottom,0]);
var xBar = d3.scaleBand()
.domain(opts.bars.map(function(d) { return d.x; }))
.rangeRound([0, width]).padding(0.1);
var yAxisBar = d3.axisLeft(yBar);
var xAxisBar = d3.axisBottom(xBar);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g.append("g")
.attr("class", "axis axis--x")
.attr("transform","translate(0,"+(height-margin.bottom)+")")
.call(d3.axisBottom(xBar));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(yBar).ticks(10, "%"))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Probability");
g.selectAll(".bar")
.data(opts.bars)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xBar(d.x); })
.attr("y", function(d) { return yBar(d.y); })
.attr("width", xBar.bandwidth())
.style("fill", function(d) { return d.color; })
.attr("height", function(d) { return height - margin.bottom - yBar(d.y); });
// Add buttons
//container for all buttons
var allButtons= svg.append("g")
.attr("id","allButtons");
//fontawesome button labels
var labels= ["B", "D"];
//colors for different button states
var defaultColor= "#E0E0E0";
var hoverColor= "#808080";
var pressedColor= "#000000";
//groups for each button (which will hold a rect and text)
var buttonGroups= allButtons.selectAll("g.button")
.data(labels)
.enter()
.append("g")
.attr("class","button")
.style("cursor","pointer")
.on("click",function(d,i) {
updateButtonColors(d3.select(this), d3.select(this.parentNode));
d3.select("#numberToggle").text(i+1);
})
.on("mouseover", function() {
if (d3.select(this).select("rect").attr("fill") != pressedColor) {
d3.select(this)
.select("rect")
.attr("fill",hoverColor);
}
})
.on("mouseout", function() {
if (d3.select(this).select("rect").attr("fill") != pressedColor) {
d3.select(this)
.select("rect")
.attr("fill",defaultColor);
}
});
var bWidth= 40; //button width
var bHeight= 25; //button height
var bSpace= 10; //space between buttons
var x0= 20; //x offset
var y0= 10; //y offset
//adding a rect to each toggle button group
//rx and ry give the rect rounded corner
buttonGroups.append("rect")
.attr("class","buttonRect")
.attr("width",bWidth)
.attr("height",bHeight)
.attr("x",function(d,i) {return x0+(bWidth+bSpace)*i;})
.attr("y",y0)
.attr("rx",5) //rx and ry give the buttons rounded corners
.attr("ry",5)
.attr("fill",defaultColor);
//adding text to each toggle button group, centered
//within the toggle button rect
buttonGroups.append("text")
.attr("class","buttonText")
.attr("x",function(d,i) {
return x0 + (bWidth+bSpace)*i + bWidth/2;
})
.attr("y",y0+bHeight/2)
.attr("text-anchor","middle")
.attr("dominant-baseline","central")
.attr("fill","white")
.text(function(d) {return d;});
function updateButtonColors(button, parent) {
parent.selectAll("rect")
.attr("fill",defaultColor);
button.select("rect")
.attr("fill",pressedColor);
}
},
resize: function(width, height) {
// TODO: code to re-render the widget with a new size
}
};
}
});
And this is the figure that that code produces:

This does the trick:
HTMLWidgets.widget({
name: 'IMPosterior',
type: 'output',
factory: function(el, width, height) {
// TODO: define shared variables for this instance
return {
renderValue: function(opts) {
//transition
var transDuration = 1000;
var dataDiscrete = opts.bars.map((b, i) => {
b.y = Number(b.y);
b.desc = opts.text[i];
return b;
});
var distParams = {
min: d3.min(opts.data, d => d.x),
max: d3.max(opts.data, d => d.x)
};
distParams.cuts = [-opts.MME, opts.MME, distParams.max];
opts.data = opts.data.sort((a,b) => a.x - b.x);
var dataContinuousGroups = [];
distParams.cuts.forEach((c, i) => {
let data = opts.data.filter(d => {
if (i === 0) {
return d.x < c;
} else if (i === distParams.cuts.length - 1) {
return d.x > distParams.cuts[i - 1];
} else {
return d.x < c && d.x > distParams.cuts[i - 1];
}
});
data.unshift({x:data[0].x, y:0});
data.push({x:data[data.length - 1].x, y:0});
dataContinuousGroups.push({
color: opts.colors[i],
data: data
});
});
var margin = {
top: 50,
right: 20,
bottom: 80,
left: 70
},
dims = {
width: width - margin.left - margin.right,
height: height - margin.top - margin.bottom
};
var xContinuous = d3.scaleLinear()
.domain([distParams.min - 1, distParams.max + 1])
.range([0, dims.width]);
var xDiscrete = d3.scaleBand()
.domain(dataDiscrete.map(function(d) { return d.x; }))
.rangeRound([0, dims.width]).padding(0.1);
var y = d3.scaleLinear()
.domain([0, 1])
.range([dims.height, 0]);
var svg = d3.select(el).append("svg")
.attr("width", dims.width + margin.left + margin.right)
.attr("height", dims.height + margin.top + margin.bottom);
var g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xAxis = d3.axisBottom()
.scale(xDiscrete);
var yAxis = d3.axisLeft()
.scale(y)
.ticks(10)
.tickFormat(d3.format(".0%"));
var yLabel = g.append("text")
.attr("class", "y-axis-label")
.attr("transform", "rotate(-90)")
.attr("y", -52)
.attr("x", -160)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style("font-size", 14 + "px")
.text("Probability");
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + dims.height + ")")
.call(xAxis);
g.append("g")
.attr("class", "y axis")
.call(yAxis);
var areas = g.selectAll(".area")
.data(dataDiscrete)
.enter().append("path")
.attr("class", "area")
.style("fill", function(d) { return d.color; })
.attr("d", function(d, i) {
let numPts = dataContinuousGroups[i].data.length - 2;
var path = d3.path()
path.moveTo(xDiscrete(d.x), y(0));
for (j=0; j<numPts; j++) {
path.lineTo(xDiscrete(d.x) + j*xDiscrete.bandwidth()/(numPts-1), y(d.y))
}
path.lineTo(xDiscrete(d.x) + xDiscrete.bandwidth(), y(0));
return path.toString();
});
var tooltip = d3.tip()
.attr('class', 'd3-tip chart-data-tip')
.offset([30, 0])
.direction('s')
.html(function(d, i) {
return "<span>" + dataDiscrete[i].desc + "</span>";
});
g.call(tooltip);
areas
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide);
var thresholdLine = g.append("line")
.attr("stroke", "black")
.style("stroke-width", "1.5px")
.style("stroke-dasharray", "5,5")
.style("opacity", 1)
.attr("x1", 0)
.attr("y1", y(opts.threshold))
.attr("x2", dims.width)
.attr("y2", y(opts.threshold));
var updateXAxis = function(type, duration) {
if (type === "continuous") {
xAxis.scale(xContinuous);
} else {
xAxis.scale(xDiscrete);
}
d3.select(".x").transition().duration(duration).call(xAxis);
};
var updateYAxis = function(data, duration) {
var extent = d3.extent(data, function(d) {
return d.y;
});
extent[0] = 0;
extent[1] = extent[1] + 0.2*(extent[1] - extent[0]);
y.domain(extent);
d3.select(".y").transition().duration(duration).call(yAxis);
};
var toggle = function(to, duration) {
if (to === "distribution") {
updateYAxis(dataContinuousGroups[0].data.concat(dataContinuousGroups[1].data).concat(dataContinuousGroups[2].data), 0);
updateXAxis("continuous", duration);
areas
.data(dataContinuousGroups)
.transition()
.duration(duration)
.attr("d", function(d) {
var gen = d3.line()
.x(function(p) {
return xContinuous(p.x);
})
.y(function(p) {
return y(p.y);
});
return gen(d.data);
});
thresholdLine
.style("opacity", 0);
g.select(".y.axis")
.style("opacity", 0);
g.select(".y-axis-label")
.style("opacity", 0);
} else {
y.domain([0, 1]);
d3.select(".y").transition().duration(duration).call(yAxis);
updateXAxis("discrete", duration);
areas
.data(dataDiscrete)
.transition()
.duration(duration)
.attr("d", function(d, i) {
let numPts = dataContinuousGroups[i].data.length - 2;
var path = d3.path()
path.moveTo(xDiscrete(d.x), y(0));
for (j=0; j<numPts; j++) {
path.lineTo(xDiscrete(d.x) + j*xDiscrete.bandwidth()/(numPts-1), y(d.y))
}
path.lineTo(xDiscrete(d.x) + xDiscrete.bandwidth(), y(0));
return path.toString();
});
thresholdLine
.transition()
.duration(0)
.delay(duration)
.style("opacity", 1)
.attr("y1", y(opts.threshold))
.attr("y2", y(opts.threshold));
g.select(".y.axis")
.transition()
.duration(0)
.delay(duration)
.style("opacity", 1);
g.select(".y-axis-label")
.transition()
.duration(0)
.delay(duration)
.style("opacity", 1);
}
};
// Add buttons
//container for all buttons
var allButtons = svg.append("g")
.attr("id", "allButtons");
//fontawesome button labels
var labels = ["B", "D"];
//colors for different button states
var defaultColor = "#E0E0E0";
var hoverColor = "#808080";
var pressedColor = "#000000";
//groups for each button (which will hold a rect and text)
var buttonGroups = allButtons.selectAll("g.button")
.data(labels)
.enter()
.append("g")
.attr("class", "button")
.style("cursor", "pointer")
.on("click", function(d, i) {
updateButtonColors(d3.select(this), d3.select(this.parentNode));
d3.select("#numberToggle").text(i + 1);
if (d === "D") {
toggle("distribution", transDuration);
} else {
toggle("discrete", transDuration);
}
})
.on("mouseover", function() {
if (d3.select(this).select("rect").attr("fill") != pressedColor) {
d3.select(this)
.select("rect")
.attr("fill", hoverColor);
}
})
.on("mouseout", function() {
if (d3.select(this).select("rect").attr("fill") != pressedColor) {
d3.select(this)
.select("rect")
.attr("fill", defaultColor);
}
});
var bWidth = 40; //button width
var bHeight = 25; //button height
var bSpace = 10; //space between buttons
var x0 = 20; //x offset
var y0 = 10; //y offset
//adding a rect to each toggle button group
//rx and ry give the rect rounded corner
buttonGroups.append("rect")
.attr("class", "buttonRect")
.attr("width", bWidth)
.attr("height", bHeight)
.attr("x", function(d, i) {
return x0 + (bWidth + bSpace) * i;
})
.attr("y", y0)
.attr("rx", 5) //rx and ry give the buttons rounded corners
.attr("ry", 5)
.attr("fill", defaultColor);
//adding text to each toggle button group, centered
//within the toggle button rect
buttonGroups.append("text")
.attr("class", "buttonText")
.attr("x", function(d, i) {
return x0 + (bWidth + bSpace) * i + bWidth / 2;
})
.attr("y", y0 + bHeight / 2)
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.attr("fill", "white")
.text(function(d) {
return d;
});
function updateButtonColors(button, parent) {
parent.selectAll("rect")
.attr("fill", defaultColor);
button.select("rect")
.attr("fill", pressedColor);
}
toggle("distribution", 0);
setTimeout(() => {
toggle("discrete", transDuration);
}, 1000);
},
resize: function(width, height) {
// TODO: code to re-render the widget with a new size
}
};
}
});

Related

Will previous version of d3 work with latter version of d3

The data is not loading up with the newest version of d3 and its not working
Here's the link
var yMargin = 40;
var width = 800;
var height = 400;
var barWidth = width / 275;
var tooltip = d3.select(".visHolder").append("div")
.attr("id", "tooltip")
.style("opacity", 0);
var overlay = d3.select(".visHolder").append("div")
.attr("class", "overlay")
.style("opacity", 0);
var svgContainer = d3.select(".visHolder")
.append("svg")
.attr("width", width + 100).attr("height", height + 60);
d3.json('https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json', function(err, data) {
svgContainer.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -200)
.attr("y", 80)
.text("Gross Domestic Product");
svgContainer.append("text")
.attr("x", width / 2 + 120)
.attr("y", height + 50)
.text('More Information: http://www.bea.gov/national/pdf/nipaguid.pdf')
.attr("class", "info");
var years = data.data.map(function(item) {
var quarter;
var temp = item[0].substring(5, 7);
if (temp === "01") {
quarter = "Q1";
} else if (temp === "04") {
quarter = "Q2";
} else if (temp === "07") {
quarter = "Q3";
} else if (temp === "10") {
quarter = "Q4";
}
return item[0].substring(0, 4) + " " + quarter
});
var yearsDate = data.data.map(function(item) {
return new Date(item[0]);
});
var xMax = new Date(d3.max(yearsDate));
xMax.setMonth(xMax.getMonth() + 3);
var xScale = d3.scaleTime()
.domain([d3.min(yearsDate), xMax])
.range([0, width]);
var xAxis = d3.axisBottom()
.scale(xScale);
var xAxisGroup = svgContainer.append("g")
.call(xAxis)
.attr("id", "x-axis")
.attr("transform", "translate(60, 400)");
var GDP = data.data.map(function(item) {
return item[1]
});
var scaledGDP = [];
var gdpMin = d3.min(GDP);
var gdpMax = d3.max(GDP);
var linearScale = d3.scaleLinear()
.domain([0, gdpMax])
.range([0, height]);
scaledGDP = GDP.map(function(item) {
return linearScale(item);
});
var yAxisScale = d3.scaleLinear()
.domain([0, gdpMax])
.range([height, 0]);
var yAxis = d3.axisLeft(yAxisScale)
var yAxisGroup = svgContainer.append("g")
.call(yAxis)
.attr("id", "y-axis")
.attr("transform", "translate(60,0)");
d3.select("svg").selectAll("rect")
.data(scaledGDP)
.enter()
.append("rect")
.attr("date-date", function(d, i) {
return data.data[i][0]
})
.attr("data-gdp", function(d, i) {
return data.data[i][1]
})
.attr("class", "bar")
.attr("x", function(d, i) {
return xScale(yearsDate[i]);
})
.attr("y", function(d, i) {
return height - d;
})
.attr("width", barWidth)
.attr("height", function(d) {
return d;
})
.style("fill", "#FD7272")
.attr("transform", "translate(60,0)")
.on("mouseover", function(d, i) {
overlay.transition()
.duration(0)
.style("height", d + "px")
.style("width", barWidth + "px")
.style("opacity", .9)
.style("left", (i * barWidth) + 0 + "px")
.style("top", height - d + "px")
.style("transform", "translateX(60px)");
tooltip.transition()
.duration(200)
.style("opacity", .9)
tooltip.html(years[i] + "<br>" + "$" +
GDP[i].toFixed(1).replace(/(\d)(?=(\d{3})+\.)/g, "$1,") + " Billion")
.attr("date-date", data.data[i][0])
.style("left", (i * barWidth) + 30 + "px")
.style("top", height - 100 + "px")
.style("transform", "translateX(60px)");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 0);
overlay.transition()
.duration(200)
.style("opacity", 0);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.1/d3.min.js"></script>
<div class="main">
<div class="container">
<div id="title">United States GDP</div>
<div class="visHolder"></div>
</div>
</div>
In d3 v6., the old d3.json(<src>, <callback>) has been replaced in favour of d3.json(<src>).then(<successcallback>).catch(<errorcallback>);
The old freecodecamp example was written for an older version of d3.
That is why the following works, with minimal changes:
var yMargin = 40;
var width = 800;
var height = 400;
var barWidth = width / 275;
var tooltip = d3.select(".visHolder").append("div")
.attr("id", "tooltip")
.style("opacity", 0);
var overlay = d3.select(".visHolder").append("div")
.attr("class", "overlay")
.style("opacity", 0);
var svgContainer = d3.select(".visHolder")
.append("svg")
.attr("width", width + 100).attr("height", height + 60);
d3.json('https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json').then(function(data) {
svgContainer.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -200)
.attr("y", 80)
.text("Gross Domestic Product");
svgContainer.append("text")
.attr("x", width / 2 + 120)
.attr("y", height + 50)
.text('More Information: http://www.bea.gov/national/pdf/nipaguid.pdf')
.attr("class", "info");
var years = data.data.map(function(item) {
var quarter;
var temp = item[0].substring(5, 7);
if (temp === "01") {
quarter = "Q1";
} else if (temp === "04") {
quarter = "Q2";
} else if (temp === "07") {
quarter = "Q3";
} else if (temp === "10") {
quarter = "Q4";
}
return item[0].substring(0, 4) + " " + quarter
});
var yearsDate = data.data.map(function(item) {
return new Date(item[0]);
});
var xMax = new Date(d3.max(yearsDate));
xMax.setMonth(xMax.getMonth() + 3);
var xScale = d3.scaleTime()
.domain([d3.min(yearsDate), xMax])
.range([0, width]);
var xAxis = d3.axisBottom()
.scale(xScale);
var xAxisGroup = svgContainer.append("g")
.call(xAxis)
.attr("id", "x-axis")
.attr("transform", "translate(60, 400)");
var GDP = data.data.map(function(item) {
return item[1];
});
var scaledGDP = [];
var gdpMin = d3.min(GDP);
var gdpMax = d3.max(GDP);
var linearScale = d3.scaleLinear()
.domain([0, gdpMax])
.range([0, height]);
scaledGDP = GDP.map(function(item) {
return linearScale(item);
});
var yAxisScale = d3.scaleLinear()
.domain([0, gdpMax])
.range([height, 0]);
var yAxis = d3.axisLeft(yAxisScale)
var yAxisGroup = svgContainer.append("g")
.call(yAxis)
.attr("id", "y-axis")
.attr("transform", "translate(60,0)");
d3.select("svg").selectAll("rect")
.data(scaledGDP)
.enter()
.append("rect")
.attr("date-date", function(d, i) {
return data.data[i][0]
})
.attr("data-gdp", function(d, i) {
return data.data[i][1]
})
.attr("class", "bar")
.attr("x", function(d, i) {
return xScale(yearsDate[i]);
})
.attr("y", function(d, i) {
return height - d;
})
.attr("width", barWidth)
.attr("height", function(d) {
return d;
})
.style("fill", "#FD7272")
.attr("transform", "translate(60,0)")
.on('click', function(event, d) {
console.log(d);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.1/d3.min.js"></script>
<div class="main">
<div class="container">
<div id="title">United States GDP</div>
<div class="visHolder"></div>
</div>
</div>
To use click events, as per this migration guide, the new signature passes (event, data, index), not (data, index). The above snippet contains an example, where d is logged when a bar is clicked.

d3 chart won't centre in UIkit framework

I have designed a chart using d3.js and have embedded it in a div within a UIkit framework, however I can't get it to centre within the div.
I have tried putting the svg in a container and adjusting the CSS by putting the svg in a flexbox, removing the margins, aligning it centre, but none of it works. I have tried this response, this one, this one and this one and none of them work. I'm sure it must be something simple - how can I get it to centre? Code below.
Chart
<div id="age_chart" class="uk-container uk-width-medium-1-2 uk-container-center uk-margin-top">
<script>
// chart based on this example: https://bl.ocks.org/63anp3ca/6bafeb64181d87750dbdba78f8678715
// var svg1 = d3.select("svg1"),
var margin = {top: 20, right: 20, bottom: 100, left: 20},
width1 = d3.select("#age_chart").node().getBoundingClientRect().width,
height1 = 400 - margin.top - margin.bottom;
// g = svg1.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// The scale spacing the groups:
var x0 = d3.scaleBand()
.rangeRound([0, width1])
.paddingInner(0.05);
// The scale for spacing each group's bar:
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height1, 0]);
var z = d3.scaleOrdinal()
.range(["#2c7fb8", "#7fcdbb"]);
var svg1 = d3.select('body').append("svg")
.attr("width", width1 + margin.left + margin.right)
.attr("height", height1 + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("data/age_distribution.csv", function(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
}).then(function(data) {
console.log(data);
var keys = data.columns.slice(1);
console.log('keys');
console.log(keys);
x0.domain(data.map(function(d) { return d.age; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { return d[key]; }); })]).nice();
svg1.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("class","bar")
.attr("transform", function(d) { return "translate(" + x0(d.age) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height1 - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
svg1.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height1 + ")")
.call(d3.axisBottom(x0));
svg1.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-size", 11)
.attr("text-anchor", "start")
.text("Percentage of population")
.attr("font-family", "Archivo");
var legend1 = svg1.append("g")
.attr("font-family", "inherit")
.attr("font-size", 11)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend1.append("rect")
.attr("x", width1 * .9)
.attr("width", 15)
.attr("height", 15)
.attr("fill", z)
.attr("stroke", z)
.attr("stroke-width",2)
.on("click",function(d) { update(d) });
legend1.append("text")
.attr("x", width1 * .85)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) { return d; });
var filtered = [];
////
//// Update and transition on click:
////
function update(d) {
//
// Update the array to filter the chart by:
//
// add the clicked key if not included:
if (filtered.indexOf(d) == -1) {
filtered.push(d);
// if all bars are un-checked, reset:
if(filtered.length == keys.length) filtered = [];
}
// otherwise remove it:
else {
filtered.splice(filtered.indexOf(d), 1);
}
//
// Update the scales for each group's items:
//
var newKeys = [];
keys.forEach(function(d) {
if (filtered.indexOf(d) == -1 ) {
newKeys.push(d);
}
})
x1.domain(newKeys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { if (filtered.indexOf(key) == -1) return d[key]; }); })]).nice();
// update the y axis:
svg1.select(".y")
.transition()
.call(d3.axisLeft(y).ticks(null, "s"))
.duration(500);
//
// Filter out the bands that need to be hidden:
//
var bars = svg1.selectAll(".bar").selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
bars.filter(function(d) {
return filtered.indexOf(d.key) > -1;
})
.transition()
.attr("x", function(d) {
return (+d3.select(this).attr("x")) + (+d3.select(this).attr("width"))/2;
})
.attr("height",0)
.attr("width",0)
.attr("y", function(d) { return height1; })
.duration(500);
//
// Adjust the remaining bars:
//
bars.filter(function(d) {
return filtered.indexOf(d.key) == -1;
})
.transition()
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height1 - y(d.value); })
.attr("width", x1.bandwidth())
.attr("fill", function(d) { return z(d.key); })
.duration(500);
// update legend:
legend1.selectAll("rect")
.transition()
.attr("fill",function(d) {
if (filtered.length) {
if (filtered.indexOf(d) == -1) {
return z(d);
}
else {
return "white";
}
}
else {
return z(d);
}
})
.duration(100);
}
});
</script>
</div>
The problem is that the SVG isn't in your div.
The position of a script element doesn't decide where the SVG will get appended on the page. If you put it at the bottom of the page, you'll get the same results - you need to do so to wait for the DOM to be fully loaded.
d3.select('body').append("svg") will append the svg directly on the body element. To append it to the element you've created, since it has id age_chart, use:
d3.select('#age_chart').append("svg")

Stacked Bar Chart to Grouped Bar Chart transition not working

I have a stacked bar chart to Grouped bar chart function with transition that works fine with one chart, but as soon as I add a second it breaks. The first chart will not transition and the second chart works fine. I think this has something to do with the transition being in a function so it only runs for the last chart made.
Any help on this would be great!?
I put together a jsFiddle for this here
My function is as follows:
function createChartDate(inputdata, chartname, inputtop, inputbottom, inputwidth, inputheight, inputleft, inputright, bargap, yaxisShift) {
var stack = d3.layout.stack(),
layers = inputdata,
m = layers[0].length, // number of samples per layer
n = layers.length, // number of layers
data = stack(d3.range(n).map(function(d) {
return layers[d];
}));
var yGroupMax = d3.max(data, function(layer) {
return d3.max(layer, function(d) {
return d.y;
});
}),
yStackMax = d3.max(data, function(layer) {
return d3.max(layer, function(d) {
return d.y0 + d.y;
});
});
var margin = {
top: inputtop,
right: inputright,
bottom: inputbottom,
left: inputleft
},
width = inputwidth - margin.left - margin.right,
height = inputheight - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([0, width], (Number(bargap)/100));
var xTime = d3.time.scale()
.domain([new Date(inputdata[0][0].x), d3.time.day.offset(new Date(inputdata[0][inputdata[0].length - 1].x), 1)])
.rangeRound([0, width - margin.left - margin.right]);
var xAxisTime = d3.svg.axis()
.scale(xTime)
.orient('bottom')
.ticks(d3.time.day, 1)
.tickFormat(d3.time.format('%x'))
.tickSize(0)
.tickPadding(8);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(2)
.tickPadding(6)
.outerTickSize(0);
var svg = d3.select(chartname).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 layer = svg.selectAll(".layer")
.data(data)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
return color(i);
});
var rect = layer.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("x", function(d) {
return xTime(d.x);
})
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0)
var allrect = layer.selectAll('rect')
.style("cursor","pointer")
.append("svg:title")
.text(function(d){return d.y;})
rect.transition()
.delay(function(d, i) {
return i * 10;
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxisTime)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
svg.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(" + (Number(margin.left) - yaxisShift) + ",0)")
.call(yAxis);
svg.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
var allchart = d3.select(chartname).selectAll(".layer").selectAll("rect")
allchart.on('mouseover', function(d){
d3.select(this).style("opacity","0.8")
})
.on('mouseout', function(d){
d3.select(this).style("opacity","1")
});
d3.selectAll("input").on("change", change);
/* var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
d3.select("input[value=\"0\"]").property("checked", true).each(change);
}, 2000); */
function change() {
//clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
if (this.value === "stacked") transitionStacked();
//else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
var allchart = d3.select(chartname).selectAll(".layer").selectAll("rect"),
axistran = d3.selectAll("svg");
allchart.transition()
.ease("linear")
.duration(300)
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d, i, j) {
return xTime(d.x) + x.rangeBand() / n * j;
})
.attr("width", x.rangeBand() / n)
.transition()
.duration(200)
.ease("linear")
.attr("y", function(d) {
return y(d.y);
})
.attr("height", function(d) {
return height - y(d.y);
});
axistran.select("g.yaxis").transition()
.duration(600)
.call(yAxis);
axistran.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
};
function transitionStacked() {
y.domain([0, yStackMax]);
var allchart = d3.select(chartname).selectAll(".layer").selectAll("rect"),
axistran = d3.selectAll("svg");
allchart.transition()
.ease("linear")
.duration(300)
.delay(function(d, i) {
return i * 10;
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
})
.transition()
.duration(200)
.ease("linear")
.attr("x", function(d) {
return xTime(d.x);
})
.attr("width", x.rangeBand());
axistran.select("g.yaxis").transition()
.duration(600)
.call(yAxis);
axistran.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
};
};
As I said in my comment:
Since your createChartDate function wraps multiple variables (and
other functions), your charts aren't independent of each other. Only
the last one transitions because var allchart =
d3.select(chartname)..., chartname holds the value "#chart2"
Lots of ways to fix this but a quick refactor would be to have your createChartDate return the transition functions (in an object), so you can call them later (and you create a closure).
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
</style>
</head>
<body>
<form>
<label>
<input type="radio" name="mode" value="grouped" /> Grouped
</label>
<label>
<input type="radio" name="mode" value="stacked" checked="" /> Stacked
</label>
</form>
<chart id="chart1"></chart>
<chart id="chart2"></chart>
<script>
var layers = [{
"x": "2016-01-01",
"y": 4,
"z": 5
}, {
"x": "2016-01-02",
"y": 5,
"z": 6
}, {
"x": "2016-01-03",
"y": 6,
"z": 3
}, {
"x": "2016-01-04",
"y": 7,
"z": 1
}];
var converted = convertjson(layers, "x", ["y", "z"]);
var trans1 = createChartDate(converted, "#chart1", 40, 60, 960, 550, 30, 30, 30, 30),
trans2 = createChartDate(converted, "#chart2", 40, 60, 960, 550, 30, 30, 30, 30);
function createChartDate(inputdata, chartname, inputtop, inputbottom, inputwidth, inputheight, inputleft, inputright, bargap, yaxisShift) {
var stack = d3.layout.stack(),
layers = inputdata,
m = layers[0].length, // number of samples per layer
n = layers.length, // number of layers
data = stack(d3.range(n).map(function(d) {
return layers[d];
}));
var yGroupMax = d3.max(data, function(layer) {
return d3.max(layer, function(d) {
return d.y;
});
}),
yStackMax = d3.max(data, function(layer) {
return d3.max(layer, function(d) {
return d.y0 + d.y;
});
});
var margin = {
top: inputtop,
right: inputright,
bottom: inputbottom,
left: inputleft
},
width = inputwidth - margin.left - margin.right,
height = inputheight - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([0, width], (Number(bargap) / 100));
var xTime = d3.time.scale()
.domain([new Date(inputdata[0][0].x), d3.time.day.offset(new Date(inputdata[0][inputdata[0].length - 1].x), 1)])
.rangeRound([0, width - margin.left - margin.right]);
var xAxisTime = d3.svg.axis()
.scale(xTime)
.orient('bottom')
.ticks(d3.time.day, 1)
.tickFormat(d3.time.format('%x'))
.tickSize(0)
.tickPadding(8);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(2)
.tickPadding(6)
.outerTickSize(0);
var svg = d3.select(chartname).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 layer = svg.selectAll(".layer")
.data(data)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
return color(i);
});
var rect = layer.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("x", function(d) {
return xTime(d.x);
})
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0)
var allrect = layer.selectAll('rect')
.style("cursor", "pointer")
.append("svg:title")
.text(function(d) {
return d.y;
})
rect.transition()
.delay(function(d, i) {
return i * 10;
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxisTime)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
svg.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(" + (Number(margin.left) - yaxisShift) + ",0)")
.call(yAxis);
svg.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
var allchart = d3.select(chartname).selectAll(".layer").selectAll("rect")
allchart.on('mouseover', function(d) {
d3.select(this).style("opacity", "0.8")
})
.on('mouseout', function(d) {
d3.select(this).style("opacity", "1")
});
/* var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
d3.select("input[value=\"0\"]").property("checked", true).each(change);
}, 2000); */
return {
group: function transitionGrouped() {
y.domain([0, yGroupMax]);
var allchart = d3.select(chartname).selectAll(".layer").selectAll("rect"),
axistran = d3.selectAll("svg");
allchart.transition()
.ease("linear")
.duration(300)
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d, i, j) {
return xTime(d.x) + x.rangeBand() / n * j;
})
.attr("width", x.rangeBand() / n)
.transition()
.duration(200)
.ease("linear")
.attr("y", function(d) {
return y(d.y);
})
.attr("height", function(d) {
return height - y(d.y);
});
axistran.select("g.yaxis").transition()
.duration(600)
.call(yAxis);
axistran.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
},
stack: function transitionStacked() {
y.domain([0, yStackMax]);
var allchart = d3.select(chartname).selectAll(".layer").selectAll("rect"),
axistran = d3.selectAll("svg");
allchart.transition()
.ease("linear")
.duration(300)
.delay(function(d, i) {
return i * 10;
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
})
.transition()
.duration(200)
.ease("linear")
.attr("x", function(d) {
return xTime(d.x);
})
.attr("width", x.rangeBand());
axistran.select("g.yaxis").transition()
.duration(600)
.call(yAxis);
axistran.select("g.yaxis").selectAll(".tick")
.each(function(d) {
if (d === 0) {
this.remove();
}
});
}
}
};
d3.selectAll("input").on("change", change);
function change() {
//clearTimeout(timeout);
if (this.value === "grouped") {
trans1.group();
trans2.group();
}
if (this.value === "stacked") {
trans1.stack();
trans2.stack();
}
}
function convertjson(data, xValue, yArray) {
var arrayconvertedjson = [];
var convertedjson = [];
for (var j = 0; j < yArray.length; j++) {
for (var i = 0; i < data.length; i++) {
convertedjson.push({
"x": new Date(data[i][xValue]),
"y": data[i][yArray[j]]
});
};
arrayconvertedjson.push(convertedjson)
convertedjson = [];
};
return arrayconvertedjson;
};
</script>
</body>
</html>

Setting color of marker-end arrow in D3.js

The code below displays marker-ends on arrows/paths/lines as intended, but the color of the marker-end does not vary by line (i.e., it is always the same orange color, not the color of its respective line). I think the code is defaulting to the color assigned to the first field of my data(?). Any advice would be appreciated.
<script src="http://www.protobi.com/javascripts/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script src="http://www.protobi.com/examples/pca/pca.js"></script>
<script type="text/javascript">
var margin = {top: 20, right: 20, bottom: 20, left: 20};
var width = 1500 - margin.left - margin.right;
var height = 1500 - margin.top - margin.bottom;
var angle = Math.PI * 0;
var color = d3.scale.category10();
var x = d3.scale.linear().range([width, 0]); // switch to match how R biplot shows it
var y = d3.scale.linear().range([height, 0]);
x.domain([-3.5,3.5]).nice()
y.domain([-3.5,3.5]).nice()
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("/brand.csv", function(error, data) {
var matrix = data.map(function(d){
return d3.values(d).slice(1,d.length).map(parseFloat);
});
var pca = new PCA();
matrix = pca.scale(matrix,true,true);
pc = pca.pca(matrix,2)
var A = pc[0]; // this is the U matrix from SVD
var B = pc[1]; // this is the dV matrix from SVD
var brand_names = Object.keys(data[0]); // first row of data file ["ATTRIBUTE", "BRAND A", "BRAND B", "BRAND C", ...]
brand_names.shift(); // drop the first column label, e.g. "ATTRIBUTE"
data.map(function(d,i){
label: d.ATTRIBUTE,
d.pc1 = A[i][0];
d.pc2 = A[i][1];
});
var label_offset = {
"Early/First line": 20,
"Unfamiliar":10,
"Convenient": -5
}
var brands = brand_names
.map(function(key, i) {
return {
brand: key,
pc1: B[i][0]*4,
pc2: B[i][1]*4
}
});
function rotate(x,y, dtheta) {
var r = Math.sqrt(x*x + y*y);
var theta = Math.atan(y/x);
if (x<0) theta += Math.PI;
return {
x: r * Math.cos(theta + dtheta),
y: r * Math.sin(theta + dtheta)
}
}
data.map(function(d) {
var xy = rotate(d.pc1, d.pc2, angle);
d.pc1 = xy.x;
d.pc2 = xy.y;
});
brands.map(function(d) {
var xy = rotate(d.pc1, d.pc2, angle);
d.pc1 = xy.x;
d.pc2 = xy.y;
});
var showAxis = false; // normally we don't want to see the axis in PCA, it's meaningless
if (showAxis) {
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("PC1");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("PC2");
}
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 10.5)
.attr("cx", function(d) { return x(d.pc1); })
.attr("cy", function(d) { return y(d.pc2); })
.style("fill", function(d) { return color(d['species']); })
.on('mouseover', onMouseOverAttribute)
.on('mouseleave', onMouseLeave);
svg.selectAll("text.brand")
.data(brands)
.enter().append("text")
.attr("class", "label-brand")
.attr("x", function(d) { return x(d.pc1) + 10; })
.attr("y", function(d) { return y(d.pc2) + 0; })
.text(function(d) { return d['brand']})
svg.selectAll("marker.brand")
.data(brands)
.enter().append("svg:marker")
.attr('id', 'end-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 6)
.attr('markerWidth', 10)
.attr('markerHeight', 10)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,-5L10,0L0,5')
.style("fill", function(d) { return color(d['brand']); });
svg.selectAll(".line")
.data(brands)
.enter().append("line")
.attr("class", "square")
.attr('x1', function(d) { return x(-d.pc1);})
.attr('y1', function(d) { return y(-d.pc2); })
.attr("x2", function(d) { return x(d.pc1); })
.attr("y2", function(d) { return y(d.pc2); })
.style("stroke", function(d) { return color(d['brand']); })
.style('marker-end', "url(#end-arrow)")
.on('mouseover', onMouseOverBrand)
.on('mouseleave', onMouseLeave);
svg.selectAll("text.attr")
.data(data)
.enter().append("text")
.attr("class", "label-attr")
.attr("x", function(d,i ) { return x(d.pc1)+4 ; })
.attr("y", function(d ,i) { return y(d.pc2) + (label_offset[d.ATTRIBUTE]||0); })
.text(function(d,i) { return d.ATTRIBUTE})
var pctFmt = d3.format('0%')
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([10, 20])
.direction('e')
.html(function(values,title) {
var str =''
str += '<h3>' + (title.length==1 ? 'Brand ' : '' )+ title + '</h3>'
str += "<table>";
for (var i=0; i<values.length; i++) {
if (values[i].key != 'ATTRIBUTE' && values[i].key != 'pc1' && values[i].key != 'pc2') {
str += "<tr>";
str += "<td>" + values[i].key + "</td>";
str += "<td class=pct>" + pctFmt(values[i].value) + "</td>";
str + "</tr>";
}
}
str += "</table>";
return str;
});
svg.call(tip);
function getSpPoint(A,B,C){
var x1=A.x, y1=A.y, x2=B.x, y2=B.y, x3=C.x, y3=C.y;
var px = x2-x1, py = y2-y1, dAB = px*px + py*py;
var u = ((x3 - x1) * px + (y3 - y1) * py) / dAB;
var x = x1 + u * px, y = y1 + u * py;
return {x:x, y:y}; //this is D
}
// draw line from the attribute a perpendicular to each brand b
function onMouseOverAttribute(a,j) {
brands.forEach(function(b, idx) {
var A = { x: 0, y:0 };
var B = { x: b.pc1, y: b.pc2 };
var C = { x: a.pc1, y: a.pc2 };
b.D = getSpPoint(A,B,C);
});
svg.selectAll('.tracer')
.data(brands)
.enter()
.append('line')
.attr('class', 'tracer')
.attr('x1', function(b,i) { return x(a.pc1); return x1; })
.attr('y1', function(b,i) { return y(a.pc2); return y1; })
.attr('x2', function(b,i) { return x(b.D.x); return x2; })
.attr('y2', function(b,i) { return y(b.D.y); return y2; })
.style("stroke", function(d) { return "#aaa"});
delete a.D;
var tipText = d3.entries(a);
tip.show(tipText, a.ATTRIBUTE);
};
// draw line from the brand axis a perpendicular to each attribute b
function onMouseOverBrand(b,j) {
data.forEach(function(a, idx) {
var A = { x: 0, y:0 };
var B = { x: b.pc1, y: b.pc2 };
var C = { x: a.pc1, y: a.pc2 };
a.D = getSpPoint(A,B,C);
});
svg.selectAll('.tracer')
.data(data)
.enter()
.append('line')
.attr('class', 'tracer')
.attr('x1', function(a,i) { return x(a.D.x); })
.attr('y1', function(a,i) { return y(a.D.y); })
.attr('x2', function(a,i) { return x(a.pc1); })
.attr('y2', function(a,i) { return y(a.pc2); })
.style("stroke", function(d) { return "#aaa"});
var tipText = data.map(function(d) {
return {key: d.ATTRIBUTE, value: d[b['brand']] }
})
tip.show(tipText, b.brand);
};
function onMouseLeave(b,j) {
svg.selectAll('.tracer').remove()
tip.hide();
}
});
While you are creating an svg:marker for each line, you give them all the same id. When they are then used on your line elements, since they all have the same id you are only using one of them.
Simple fix, give them unique ids:
svg.selectAll("marker.brand")
.data(brands)
.enter().append("svg:marker")
.attr('id', function(d,i){
return 'end-arrow' + i; //<-- append index postion
})
...
svg.selectAll(".line")
.data(brands)
.enter().append("line")
.attr("class", "square")
.attr('x1', function(d) {
return x(-d.pc1);
})
.attr('y1', function(d) {
return y(-d.pc2);
})
.attr("x2", function(d) {
return x(d.pc1);
})
.attr("y2", function(d) {
return y(d.pc2);
})
.style("stroke", function(d) {
return color(d['brand']);
})
.style('marker-end', function(d,i){
return "url(#end-arrow"+i+")"; //<--use the one with the right id
})
....
Example here.

Display Pie chart, Grouped and Stacked bar chart in one page

I want to display two charts in one page.The Pie chart gets displayed but the Grouped and Stacked bar chart is not displaying . I tried to change the Id name in but no luck :( . Will appreciate if anyone helps me with the code correction .
/* Display Matrix Chart ,Pie chart, Grouped and Stacked bar chart in one page */
<apex:page showHeader="false">
<apex:includeScript value="{!URLFOR($Resource.jquery1)}"/>
<apex:includeScript value="{!URLFOR($Resource.D3)}"/>
<apex:includeScript value="{!URLFOR($Resource.nvD3)}"/>
<div id="body" height="50%" width="100px" ></div> // Id for Pie chart
<div id="body1" height="30%" width="90px"></div> // Id for Stacked and Grouped bar chart
<script>
// Matrix Chart starts here
var drawChart = function(divId,matrixReportId) {
$.ajax('/services/data/v29.0/analytics/reports/'+matrixReportId,
{
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer {!$Api.Session_ID}');
},
success: function(response) {
console.log(response);
var chart = nv.models.multiBarChart();
var chartData = [];
document.getElementById(divId).innerHTML = '';
$.each(response.groupingsDown.groupings, function(di, de) {
var values = [];
chartData.push({"key":de.label, "values": values});
$.each(response.groupingsAcross.groupings, function(ai, ae) {
values.push({"x": ae.label, "y": response.factMap[de.key+"!"+ae.key].aggregates[0].value});
});
});
d3.select('#'+divId).datum(chartData).transition().duration(100).call(chart);
window.setTimeout(function(){
drawChart(divId,matrixReportId);
}, 5000);
}
}
);
};
$(document).ready(function(){
drawChart('chart','00O90000005SSHv');
});
// Pie Chart Starts here
var width =250 ,
height = 450,
radius = Math.min(width, height) / 2;
var data = [{"age":"<5","population":2704659},{"age":"14-17","population":2159981},
{"age":"14-17","population":2159981},{"age":"18-24","population":3853788},
{"age":"25-44","population":14106543},{"age":"45-64","population":8819342},
{"age":">65","population":612463}];
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc().outerRadius(radius - 10).innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
var svg = d3.select("#body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.age); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.age; });
// Grouped and Stacked Bar Chart starts here
var n = 2, // number of layers
m = 10, // number of samples per layer
stack = d3.layout.stack(),
layers = stack(d3.range(n).map(function() { return bumpLayer(m, .1); })),
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([0, width], .08);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0)
.tickPadding(6)
.orient("bottom");
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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0);
rect.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) { return x(d.x) + x.rangeBand() / n * j; })
.attr("width", x.rangeBand() / n)
.transition()
.attr("y", function(d) { return y(d.y); })
.attr("height", function(d) { return height - y(d.y); });
}
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.transition()
.attr("x", function(d) { return x(d.x); })
.attr("width", x.rangeBand());
}
// Inspired by Lee Byron's test data generator.
function bumpLayer(n, o) {
function bump(a) {
var x = 1/(.1 + Math.random()),
y = 2*Math.random()-.5,
z = 10/(.1 + Math.random());
for (var i = 0; i < n; i++) {
var w = (i/n- y) * z;
a[i] += x * Math.exp(-w * w);
}
}
var a=[],i;
for (i = 0; i < n; ++i) a[i] = o + o * Math.random();
for (i = 0; i < 5; ++i) bump(a);
return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; });
}
</script>
<svg id="chart" height="50%" width="500px" ></svg> // Id for Matrix chart
</apex:page>
Thanks in advance
first, as we discussed here (d3 Donut chart does not render), you should position your
<div id="body1" height="30%" width="90px"></div>
above the script.
second, you are missing the # in your second svg-declaration to correctly select the div by its id, it has to be
var svg = d3.select("#body1").append("svg")
you could also think about naming the second svg differently (eg svg2) so you don't override your first svg-variable (in case you want to do something with it later).

Categories

Resources