I am trying to make my tooltip read my data. But it won't. How do i make it read the data?
I do not understand why I can apply text labels in my chart by writing
.text(function(d) { return d; });
while the tooltip won't read it.
var data = {
labels: [
'Trøndelag', 'Innlandet', 'Oslo','Nordland','Sør-Øst', 'Alle distr.',
'Øst', 'Sør-Vest', 'Møre og R.',
'Troms', 'Vest', 'Finnmark',
],
series: [
{
label: 'Svært stor tillit',
values: [32, 29, 29, 22, 27, 27,31,25,24,26,26,20,24]
},
{
label: 'Ganske stor tillit',
values: [55,54,53,58,53,53,49,53,54,51,48,53,48]
},
{
label: 'Verken stor eller liten tillit',
values: [7,12,13,14,14,16,14,15,16,19,19,15]
},
{
label: 'Ganske liten tillit',
values: [4,4,3,2,3,3,3,3,4,5,4,4,7]
},
{
label: 'Svært liten tillit',
values: [1,1,2,3,3,2,1,3,3,1,2,4,6]
},
{
label: 'Vet ikke',
values: [0,0,1,0,0,0,1,1,0,0,0,0,1]
},
{
label: 'Ubesvart',
values: [0,0,0,0,0,0,0,0,0,0,0,0,0]
}
]
};
var margin = {top: 20, right: 5, bottom: 20, left: 5},
width = parseInt(d3.select('.chart').style('width'), 10),
width = width - margin.left - margin.right,
chartHeight = 1310,
groupHeight = barHeight * data.series.length,
gapBetweenGroups = 0,
spaceForLabels =62,
spaceForLegend = 64,
barHeight=14;
var zippedData = [];
for (var i=0; i<data.labels.length; i++) {
for (var j=0; j<data.series.length; j++) {
zippedData.push(data.series[j].values[i]);
}
}
// Color scale
var color = d3.scale.category20c();
var x = d3.scale.linear()
.domain([0, d3.max(zippedData)])
.range([0, width]);
var y = d3.scale.linear()
.range([chartHeight + gapBetweenGroups, 0]);
d3.select(window).on('resize', resize);
function resize (){
width = parseInt(d3.select('.chart').style('width'),10);
width= width - margin.left - margin.right;
x.range([0,width]);
}
var yAxis = d3.svg.axis()
.scale(y)
.tickFormat('')
.tickSize(0)
.orient("left");
// Specify the chart area and dimensions
var chart = d3.select(".chart")
.attr("width", spaceForLabels + width + spaceForLegend)
.attr("height", chartHeight);
// Create bars
var bar = chart.selectAll("g")
.data(zippedData)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(" + spaceForLabels + "," + (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
})
;
var legendPlass = 150;
var tooltip = d3.select("body")
.append("div")
.attr("class", "d3-tip")
.style("position", "absolute")
.style("opacity", 0);
// Create rectangles of the correct width
bar.append("rect")
.attr("fill", function(d,i) { return color(i % data.series.length); })
.attr("class", "bar")
.attr("width", x)
.attr('y', legendPlass )
.attr("height", barHeight - 1)
;
// Add text label in bar
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", legendPlass + barHeight / 2)
.attr("fill", "red")
.attr("dy", ".35em")
.text(function(d) { return d; });
// Draw labels
bar.append("text")
.attr("class", "label")
.attr("x", function(d) { return - 5; })
.attr("y", legendPlass)
.attr("dy", "1em")
.text(function(d,i) {
if (i % data.series.length === 0)
return data.labels[Math.floor(i/data.series.length)];
else
return ""});
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + spaceForLabels + ", " + -gapBetweenGroups/2 + ")")
.call(yAxis);
//CREATING THE TOOLTIP
chart.selectAll(".bar")
.on("click", function() {
tooltip.style("opacity", 0); })
.on("click", function(d) {
var pos = d3.mouse(this);
tooltip
.transition()
.duration(500)
.style("opacity", 1)
.style("left", d3.event.x + "px")
.style("top", d3.event.y + "px")
.text(function(d) { return d; });
});
// Draw legend
var legendRectSize = 16,
legendSpacing = 4;
var legend = chart.selectAll('.legend')
.data(data.series)
.enter()
.append('g')
.attr('transform', function (d, i) {
var height = legendRectSize + legendSpacing;
var offset = -gapBetweenGroups/2;
var horz = spaceForLegend;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', function (d, i) { return color(i); })
.style('stroke', function (d, i) { return color(i); });
legend.append('text')
.attr('class', 'legend')
.attr('x', legendRectSize + legendSpacing )
.attr('y', legendRectSize - legendSpacing)
.text(function (d) { return d.label; });
You need to append data to it to be able to read. You have this :
var tooltip = d3.select("body")
.append("div")
.attr("class", "d3-tip")
.style("position", "absolute")
.style("opacity", 0);
Needs to be like this :
var tooltip = d3.select("body")
.append("div")
.attr("class", "d3-tip")
.style("position", "absolute")
.style("opacity", 0);
var tooltipWithData = tooltip.data(data).enter();
Then use this later :
tooltipWithData
.transition()
.duration(500)
.style("opacity", 1)
.style("left", d3.event.x + "px")
.style("top", d3.event.y + "px")
.text(function(d) { return d; });
Related
I have implemented one scatter chart using d3.js. I want to convert this chart to line chart, but i am not able to do so. I have tried to follow ( http://embed.plnkr.co/wJDcZmkEzXaLVhuLZmcQ/ ) but it didn't helped me.
This is the code for scatter chart.
var data = [{"buildName":"otfa_R5-10_a1","build":"Build 1","value":"19628"},{"buildName":"otfa_R5-91_a1","build":"Build 2","value":"19628"},{"buildName":"otfa_R5-9_a1","build":"Build 3","value":"19628"}]
var yValues = [], responseData = [];
data.map(function(key) {
var test = [];
test[0] = key.build;
test[1] = key.value;
responseData.push(test);
yValues = key.value;
})
var margin = {
top: 20,
right: 15,
bottom: 60,
left: 60
},
width = 300 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(responseData.map(function(d) {
return d[0];
}))
.rangePoints([0, width], 0.5)
var y = d3.scale.linear()
.domain([5000,20000])
.range([height, 0]);
var chart = d3.select(divId)
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var colors = d3.scale.linear()
.domain([5, 20])
.range(['#4577bc', '#4577bc'])
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)" );
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(responseData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 6)
.style('stroke', function(d, i) {
return colors(i);
})
.style('fill', function(d, i) {
return colors(i);
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#fff8ee");
div.transition()
.duration(200)
.style("opacity", 2.9);
div .html((d[1]))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#4577bc");
div.transition()
.duration(500)
.style("opacity", 0);
});
How we can add a line connecting these points ?
Please help me !!
To add a line to your existing chart, just add it using path generators.
Line generator:
var line = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return y(d[1]); });
Append the line to the svg:
g.append('path').classed('line', true)
.style( { fill: 'none', 'stroke': 'steelblue'} )
.attr('d', line(responseData));
Snippet with the above code included and a few CSS styles to make it look better:
var data = [{"buildName":"otfa_R5-10_a1","build":"Build 1","value":"19628"},{"buildName":"otfa_R5-91_a1","build":"Build 2","value":"10628"},{"buildName":"otfa_R5-9_a1","build":"Build 3","value":"17628"}]
var yValues = [], responseData = [];
data.map(function(key) {
var test = [];
test[0] = key.build;
test[1] = key.value;
responseData.push(test);
yValues = key.value;
})
var margin = {
top: 20,
right: 15,
bottom: 60,
left: 60
},
width = 300 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(responseData.map(function(d) {
return d[0];
}))
.rangePoints([0, width], 0.5)
var y = d3.scale.linear()
.domain([5000,20000])
.range([height, 0]);
var chart = d3.select('body')
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var colors = d3.scale.linear()
.domain([5, 20])
.range(['#4577bc', '#4577bc'])
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)" );
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(responseData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 6)
.style('stroke', function(d, i) {
return colors(i);
})
.style('fill', function(d, i) {
return colors(i);
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#fff8ee");
div.transition()
.duration(200)
.style("opacity", 2.9);
div .html((d[1]))
.style("left", (d3.event.pageX+4) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#4577bc");
div.transition()
.duration(500)
.style("opacity", 0);
});
var line = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return y(d[1]); });
g.append('path').classed('line', true)
.style( { fill: 'none', 'stroke': 'steelblue'} )
.attr('d', line(responseData));
path.domain {
fill: none;
stroke: #000;
}
.axis text {
font-size: 12px;
}
div.tooltip {
position: absolute;
background: #FFF;
padding: 5px;
border: 1px solid #DDD;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.0/d3.min.js"></script>
I am creating a horizontal animated d3 chart. How do you reverse the x axis and position the bars in a more dynamic way.
Are the bars the correct width or is the xaxis scale correct? Using d3 version 4
//horizontal work in progress
http://jsfiddle.net/ueg3bjf7/
//vertical chart code this is based from
http://jsfiddle.net/myf1zhar/
$(document).ready(function() {
var $this = $(".barchart");
var w = $this.data("width");
var h = $this.data("height");
var data = $this.data("data");
var data = [{
"label": "Apples",
"value": 100
},
{
"label": "Pears",
"value": 120
},
{
"label": "Bananas",
"value": 20
}
];
var configurations = $this.data("configurations");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#2b2d39", "#c12f39", "#f8dd2f", "#1b91dc"];
return colores_g[n % colores_g.length];
}
//asess the margin bottom for the chart based on the max char label
var charLabelCount = [];
data.map(function(d) {
var labelStr = d.label.toString();
charLabelCount.push(labelStr.length);
})
var maxChars = charLabelCount.reduce(function(a, b) {
return Math.max(a, b);
});
var bottomMarg = 60;
if (maxChars > 15) {
bottomMarg = 170;
}
//bottom margin calculation
var margin = {
top: 15,
right: 20,
bottom: bottomMarg,
left: 40
},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var yAxis = d3.axisBottom(y);
var xAxis = d3.axisLeft(x);
var svg = d3.select($this[0])
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("viewBox", "0 0 " + w + " " + h)
.attr("preserveAspectRatio", "xMidYMid meet")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "barchartg");
function sortBy(array, key) {
var sorted = array.sort(function(a, b) {
return parseFloat(b[key]) - parseFloat(a[key]);
});
return sorted;
}
var sortedMax = 45;
if (configurations) {
if (configurations[0]["maxValue"]) {
sortedMax = configurations[0]["maxValue"] + 5;
}
} else {
sortedMax = sortBy(data, "value")[0]["value"] + 5;
}
x.domain(data.map(function(d) {
return d.label;
}));
y.domain([0, sortedMax]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0,25)")
.call(xAxis);
svg.selectAll(".x.axis text")
.attr("transform", "rotate(-60) translate(-5,-5)")
.style("text-anchor", "end");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("fill", function(d, i) {
return colores_google(i);
})
.attr("x", function(d) {
return 0;
})
.attr("width", function(d) {
return d.value;
})
.attr("y", function(d, i) {
return 45 + (i * 90);
})
.attr("height", function(d) {
return 50;
});
d3.selectAll("rect").transition()
.duration(500)
.delay(function(d, i) {
return 500 * i;
})
.attr("width", function(d) {
return 0;
})
setTimeout(function() {
d3.selectAll("rect").transition()
.duration(500)
.delay(function(d, i) {
return 600 * (3 - i);
})
.attr("width", function(d) {
return d.value;
})
}, 2000);
});
I will try to answer your questions.
How do you reverse the x axis
You have to change the domain of the axis
y.domain([sortedMax, 0]);
position the bars
You have to translate the axis to the width of your graph
svg.append("g").attr("transform", "translate(0, 300)").attr("class", "y axis")
Are the bars the correct width or is the xaxis scale correct?
You have to use a multiplier to calculate the width of each bar, using the max width of your graph and your max value. I have added the 25 pixels of the translate of the x axis
var mult = (w + 25) / sortedMax;
...
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("fill", function(d, i) {
return colores_google(i);
})
.attr("x", function(d) {
return 0;
})
.attr("width", function(d) {
return d.value * mult;
})
.attr("y", function(d, i) {
return 45 + (i * 90);
})
.attr("height", function(d) {
return 50;
});
...
setTimeout(function() {
d3.selectAll("rect").transition()
.duration(500)
.delay(function(d, i) {
return 600 * (3 - i);
})
.attr("width", function(d) {
return d.value * mult;
})
}, 2000);
You can see the result in this fiddle http://jsfiddle.net/jfLgawue/65/
I was able to create my first htmlwidget that creates this animated plot:
I would like to replace the "B" and "D" buttons with a single icon that uses an svg as the icon. In particular, I want to use this icon.. The icon should be black when selected, light gray when unselected, and a darker gray when hovering over.
To start, I'm not sure where to save the file so my code can see it.
This is the yaml for my htmlwidget package:
# (uncomment to add a dependency)
dependencies:
- name: D3
version: 4
src: htmlwidgets/lib/D3
script: d3.v4.js
stylesheet: style.css
- name: d3tip
version: 0.7.1
src: htmlwidgets/lib/d3-tip
script: d3-tip.min.js
stylesheet: style.css
And this is the js file:
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
}
};
}
});
Once I save the svg in the right folder, I'm also not sure how can I use it to replace the two buttons that I have.
It will probably be easiest and most self contained to grab the svg paths (and in this case a rect) and attach them to the svg with svg.append("defs") - no need to access any image file from the script. Inserting an svg straight from a file makes it trickier, for example, to color, .attr("fill",) won't work in this case.
Open the icon in a text editor, the data we want from the icon is:
<path d="M37.92,42.22c3.78-8,7-14.95,12.08-14.95h0c5,0,8.3,6.93,12.08,14.95,6.12,13,13.73,29.13,33.48,29.13h0v-2h0c-18.48,0-25.79-15.51-31.67-28C59.82,32.74,56.3,25.28,50,25.28h0c-6.3,0-9.82,7.46-13.89,16.09-5.88,12.47-13.19,28-31.67,28h0v2h0C24.18,71.35,31.8,55.2,37.92,42.22Z"/>
<rect y="72.72" width="100" height="2"/>
Then we can append them to the svg as defs, using a parent g, with:
var symbol = svg.append("defs")
.append("g")
.attr("id","bellcurve");
symbol.append("path")
.attr("d", "M37.92,42.22c3.78-8,7-14.95,12.08-14.95h0c5,0,8.3,6.93,12.08,14.95,6.12,13,13.73,29.13,33.48,29.13h0v-2h0c-18.48,0-25.79-15.51-31.67-28C59.82,32.74,56.3,25.28,50,25.28h0c-6.3,0-9.82,7.46-13.89,16.09-5.88,12.47-13.19,28-31.67,28h0v2h0C24.18,71.35,31.8,55.2,37.92,42.22Z" )
symbol.append("rect")
.attr("y", 72.72)
.attr("width",100)
.attr("height",2);
To use the icon, we only need to append it as a child of a g element (this allows us to scale it too, and since it's width is 100 pixels, this allows for easy scaling to any width:
svg.append("g")
.attr("transform","scale(0.4)")
.append("use")
.attr("xlink:href","#bellcurve")
Like any other svg element, we can set the stroke, fill, and stroke-width attributes. If setting the stroke-width to more than 2, you probably won't need to set the fill: the stroke will overlap it.
Here's a quick demonstration using your icon, scaling it and coloring it, and for fun, transitioning it:
var svg = d3.select("body").append("svg")
.attr("width", 400)
.attr("height", 400);
var symbol = svg.append("defs")
.append("g")
.attr("id","bellcurve");
symbol.append("path")
.attr("d", "M37.92,42.22c3.78-8,7-14.95,12.08-14.95h0c5,0,8.3,6.93,12.08,14.95,6.12,13,13.73,29.13,33.48,29.13h0v-2h0c-18.48,0-25.79-15.51-31.67-28C59.82,32.74,56.3,25.28,50,25.28h0c-6.3,0-9.82,7.46-13.89,16.09-5.88,12.47-13.19,28-31.67,28h0v2h0C24.18,71.35,31.8,55.2,37.92,42.22Z" )
symbol.append("rect")
.attr("y", 72.72)
.attr("width",100)
.attr("height",2);
svg.append("g")
.append("use")
.attr("xlink:href","#bellcurve")
.attr("fill","steelblue")
.attr("stroke","steelblue")
svg.append("g")
.attr("transform","translate(100,0)scale(0.5)")
.append("use")
.attr("xlink:href","#bellcurve")
.attr("fill","steelblue")
.attr("stroke","steelblue")
.attr("stroke-width",2)
svg.append("g")
.attr("transform","translate(100,50)scale(0.5)")
.append("use")
.attr("xlink:href","#bellcurve")
.attr("fill","steelblue")
.attr("stroke","steelblue")
.attr("stroke-width",5)
var transition = function() {
d3.select(this)
.transition()
.attr("stroke","orange")
.attr("fill","orange")
.duration(1000)
.transition()
.attr("stroke","steelblue")
.attr("fill","steelblue")
.duration(500)
.on("end",transition)
}
d3.selectAll("g").selectAll("use")
.each(transition);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
With that it should be fairly easy to append the image straight to a button. And when toggling which visualization is showing, you can toggle the button's fill.
Slick looking app by the way.
I have data in a streamgraph stack layout and the desired aesthetic I'm after is to assign an arbitrary series as the center line(s). Series above those lines (as determined by their index in the data) will stack on top and series below that line will stack below.
Here's a jsFidde.
In this example, I'd like the MS and the RC series to be single horizontal lines with the other groups stacked above and below them, respectively. (As opposed to the data index, I could also set the middle series based on some data attribute, in this example, oldest date would make sense.)
I think the solution would require passing my own offset function but I'm having a hard time figuring out how the built-in ones do what they do.
HTML
<div class="chart">
JS
// Adapted from https://gist.github.com/WillTurman/4631136
var data = [
{"key":"DJ","value":0,"date":"1/8/13"},
{"key":"DJ","value":0,"date":"1/9/13"},
{"key":"DJ","value":0,"date":"1/10/13"},
{"key":"DJ","value":1,"date":"1/11/13"},
{"key":"DJ","value":1,"date":"1/12/13"},
{"key":"MS","value":0,"date":"1/8/13"},
{"key":"MS","value":1,"date":"1/9/13"},
{"key":"MS","value":1,"date":"1/10/13"},
{"key":"MS","value":1,"date":"1/11/13"},
{"key":"MS","value":1,"date":"1/12/13"},
{"key":"RC","value":0,"date":"1/8/13"},
{"key":"RC","value":1,"date":"1/9/13"},
{"key":"RC","value":1,"date":"1/10/13"},
{"key":"RC","value":1,"date":"1/11/13"},
{"key":"RC","value":1,"date":"1/12/13"},
{"key":"CG","value":0,"date":"1/8/13"},
{"key":"CG","value":0,"date":"1/9/13"},
{"key":"CG","value":0,"date":"1/10/13"},
{"key":"CG","value":0,"date":"1/11/13"},
{"key":"CG","value":1,"date":"1/12/13"},
{"key":"RI","value":0,"date":"1/8/13"},
{"key":"RI","value":0,"date":"1/9/13"},
{"key":"RI","value":0,"date":"1/10/13"},
{"key":"RI","value":0,"date":"1/11/13"},
{"key":"RI","value":1,"date":"1/12/13"}
]
chart(data, "pink");
var datearray = [];
var colorrange = [];
function chart(data, color) {
if (color == "blue") {
colorrange = ["#045A8D", "#2B8CBE", "#74A9CF", "#A6BDDB", "#D0D1E6", "#F1EEF6"];
}
else if (color == "pink") {
colorrange = ["#980043", "#DD1C77", "#DF65B0", "#C994C7", "#D4B9DA", "#F1EEF6"];
}
else if (color == "orange") {
colorrange = ["#B30000", "#E34A33", "#FC8D59", "#FDBB84", "#FDD49E", "#FEF0D9"];
}
strokecolor = colorrange[0];
var format = d3.time.format("%m/%d/%y");
var margin = {top: 20, right: 40, bottom: 30, left: 30};
var width = document.body.clientWidth - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
var tooltip = d3.select("body")
.append("div")
.attr("class", "remove")
.style("position", "absolute")
.style("z-index", "20")
.style("visibility", "hidden")
.style("top", "30px")
.style("left", "55px");
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height-10, 0]);
var z = d3.scale.ordinal()
.range(colorrange);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.weeks);
var yAxis = d3.svg.axis()
.scale(y);
var yAxisr = d3.svg.axis()
.scale(y);
var stack = d3.layout.stack()
.offset("silhouette")
.values(function(d) { return d.values; })
.x(function(d) { return d.date; })
.y(function(d) { return d.value; });
var nest = d3.nest()
.key(function(d) { return d.key; });
var area = d3.svg.area()
.interpolate("linear")
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y0 + d.y); });
var svg = d3.select(".chart").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 + ")");
data.forEach(function(d) {
d.date = format.parse(d.date);
d.value = +d.value;
});
var layers = stack(nest.entries(data));
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d, i) { return z(i); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxis.orient("right"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis.orient("left"));
svg.selectAll(".layer")
.attr("opacity", 1)
.on("mouseover", function(d, i) {
svg.selectAll(".layer").transition()
.duration(250)
.attr("opacity", function(d, j) {
return j != i ? 0.6 : 1;
})})
.on("mousemove", function(d, i) {
mousex = d3.mouse(this);
mousex = mousex[0];
var invertedx = x.invert(mousex);
invertedx = invertedx.getMonth() + invertedx.getDate();
var selected = (d.values);
for (var k = 0; k < selected.length; k++) {
datearray[k] = selected[k].date
datearray[k] = datearray[k].getMonth() + datearray[k].getDate();
}
mousedate = datearray.indexOf(invertedx);
pro = d.values[mousedate].value;
d3.select(this)
.classed("hover", true)
.attr("stroke", strokecolor)
.attr("stroke-width", "0.5px"),
tooltip.html( "<p>" + d.key + "<br>" + pro + "</p>" ).style("visibility", "visible");
})
.on("mouseout", function(d, i) {
svg.selectAll(".layer")
.transition()
.duration(250)
.attr("opacity", "1");
d3.select(this)
.classed("hover", false)
.attr("stroke-width", "0px"), tooltip.html( "<p>" + d.key + "<br>" + pro + "</p>" ).style("visibility", "hidden");
})
var vertical = d3.select(".chart")
.append("div")
.attr("class", "remove")
.style("position", "absolute")
.style("z-index", "19")
.style("width", "1px")
.style("height", "380px")
.style("top", "10px")
.style("bottom", "30px")
.style("left", "0px")
.style("background", "#fff");
d3.select(".chart")
.on("mousemove", function(){
mousex = d3.mouse(this);
mousex = mousex[0] + 5;
vertical.style("left", mousex + "px" )})
.on("mouseover", function(){
mousex = d3.mouse(this);
mousex = mousex[0] + 5;
vertical.style("left", mousex + "px")});
}
I need help adding a second series of data to my bar chart. Currently I'm populating the bars from the glob key in my dataset. This will be the first series. The second series I would like to be is local.
How do I go about adding that?
Play with my JSFiddle here.
var w = 300;
var h = 200;
var colors = ["#377EB8", "#4DAF4A"];
var dataset = [
{"keyword": "payday loans", "glob": 1500000, "local": 673000, "cpc": "14.11"},
{"keyword": "title loans", "glob": 165000, "local": 165000, "cpc": "12.53" },
{"keyword": "personal loans", "glob": 550000, "local": 301000, "cpc": "6.14"}
];
var data = [[1500000, 165000, 550000],
[673000, 165000, 301000]];
var tdata = d3.transpose(dataset.glob, dataset.local);
var series = 2; // Global & Local
var x0Scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var x1Scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {return d.glob;})])
.range([0, h]);
var glob = function(d) {
return d.glob;
};
var cpc = function(d) {
return d.cpc;
};
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w)
.attr("height", h);
// Graph Bars
svg.selectAll("rect")
.data(dataset, cpc, glob)
.enter()
.append("rect")
.attr("x", function(d, i){
return x0Scale(i);
})
.attr("y", function(d) {
return h - yScale(d.glob);
})
.attr("width", x0Scale.rangeBand())
.attr("height", function(d) {
return yScale(d.glob);
})
.attr("fill", colors[1])
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + x0Scale.rangeBand() / 3;
var yPosition = parseFloat(d3.select(this).attr("y")) + yScale;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#cpcVal")
.text(d.cpc);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
});
//Create labels
svg.selectAll("text")
.data(dataset, glob)
.enter()
.append("text")
.text(function(d) {
return commaFormat(d.glob);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return x0Scale(i) + x0Scale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d.glob) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
It's easiest to create an svg group (<g>) element for each set of data, and then add the individual parts to each group. For example, roughly:
http://jsfiddle.net/WXMwv/1/
var sets = svg.selectAll(".set")
.data(dataset)
.enter().append("g")
.attr("class","set")
.attr("transform",function(d,i){
return "translate(" + x0Scale(i) + ",0)"
})
.on("mouseover", function(d,i) {
//Create x value from data index
var xPosition = parseFloat(x0Scale(i) + x0Scale.rangeBand() / 6);
var yPosition = 0;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#cpcVal")
.text(d.cpc);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
});
sets.append("rect")
.attr("class","glob")
.attr("width", x0Scale.rangeBand()/2)
.attr("y", function(d) {
return yScale(d.glob);
})
.attr("height", function(d){
return h - yScale(d.glob);
})
.attr("fill", colors[1])
sets.append("rect")
.attr("class","local")
.attr("width", x0Scale.rangeBand()/2)
.attr("y", function(d) {
return yScale(d.local);
})
.attr("x", x0Scale.rangeBand()/2)
.attr("height", function(d){
return h - yScale(d.local);
})
.attr("fill", colors[0])
The text elements are left as an exercise for the reader :)