How to show a value in barChart bar's labels different than valueAccessor? - dc.js

How do you give the bars a value for their height and another value for the bar's labels?

barChart
.valueAccessor(function (d) { return d.value.ValueForBarHieght })
.label(function (d) {
return d.data.value.ValueToShowInBarLabels;
}, false)

Related

D3 - Change stacked bar chart from clicking on legend

I have a plunker here - https://plnkr.co/edit/7Eg34HyhXY6M3UJccAFq?p=preview
Its a simple stacked bar chart
I wanted to be able to click on a color in the legend and update the chart to show just that bar only
Ive found a few examples online but they are a bit too complex - just needed a simple method if thats possible
let legendItems = legend.selectAll('li')
.data(legendKeys)
.enter()
.append('li');
legendItems.append('span')
.attr('class', 'rect')
.style('background-color', (d, i) =>{
return colors[i];
});
legendItems.append('span')
.attr('class', 'label')
.html((d) => {
return d
});
You should bind click event handler this way:
let legendItems = legend.selectAll('li')
.data(legendKeys)
.enter()
.append('li')
.on('click', handleLegendItemClick);
Handler-function should looks like this (pay attention on the comments):
function handleLegendItemClick(d, i) {
// change opacity to show active legend item
legendItems.filter(legendItem => legendItem !== d).style('opacity', 0.5);
legendItems.filter(legendItem => legendItem === d).style('opacity', 1);
// update domain of y scale and update tick on y axis
y.domain([0, stackedSeries[i][0].data[d]]);
yAxis.transition().call(d3.axisLeft(y));
// bind new data
let enter = rects.data([stackedSeries[i]])
.enter()
.append('rect');
// remove old rects
rects.exit();
// draw new rect
rects.merge(enter)
.attr('height', (d) => {
return height - y(d[0].data[d.key]);
})
.attr('y', (d) => {
return 0;
})
.style('fill', (d, i) => {
return colorScale(d);
});
}
Check this fork of your plnkr. (I also add color scale - colorScale for applying a color by legend key name, not by index as you did.)
const colorScale = d3.scaleOrdinal(colors);
colorScale.domain(legendKeys);

Can i give spaces between the bar rectangles without adjusting the height attribute?

In my fiddle having horizontal bar graph; i want to give more space between the bars .my fiddle. I can tamper with the height attribute(line:101) and reduce the bar heights so that the space seems increased But i donot want to change their height. How can i increase the space between bars without changing their height?
Code for the rectangles
rects = groups.selectAll('rect')
.data(function (d) {
return d;
})
.enter()
.append('rect')
.attr('x', function (d) {
return xScale(d.x0);
})
.attr('y', function (d, i) {
return yScale(d.y);
})
.attr('height', function (d) {
return yScale.rangeBand();
})
.attr('width', function (d) {
return xScale(d.x);
})
You're already doing it in your code. When you write:
yScale = d3.scale.ordinal()
.domain(months)
.rangeRoundBands([0, height], .1);
That second argument in rangeRoundBands is the padding between the bars:
ordinal.rangeRoundBands(interval[, padding[, outerPadding]])
So, you just need to tweak that value. Check this fiddle, using 0.5: https://jsfiddle.net/catbu2oz/
But if you're talking about keeping the same height in pixels, there is only one solution: hardcoding the height value of the bars and increasing the range of the scale, as in this fiddle: https://jsfiddle.net/3xakLhfo/

Fill area between two lines

Apologies for such a basic question. Is it possibly to fill the area between two lines?
For example, I have historical high/low temperature data. I'd like to create a shaded area between these two timeseries.
I've tried using the area feature of a lineChart like this:
return [
{
values: tsData.normal,
key: 'Historical Normal',
classed: 'dashed'
},
{
area: true,
values: tsData.hi,
key: 'Historical Max',
color: '#0000ff'
},
{
area: true,
values: tsData.low,
key: 'Historical Min',
color: '#ffffff',
fillOpacity: 1
}
];
Which results in this image:
Note that the gridlines below the Historical Min line are hidden by the filled areas. This solution is a bit hacky and ugly. Is there a more direct way to do this?
I achieved a better solution by drawing an area using d3.
First I created an array (I called areaData) that merges tsData.hi and tsData.low. For each data point I push for eg:
{x: "The x value", y0:"The y value from tsData.hi", y1:"The y value from tsData.low"}
Then I defined the x and y scale based on the chart's scales:
var x = chart.xScale();
var y = chart.yScale();
Then I added an area definition as
var area = d3.svg.area()
.x(function (d) { return x(d.x); })
.y0(function (d) { return y(d.y0); })
.y1(function (d) { return y(d.y1); });
Next I drew the area using:
d3.select('.nv-linesWrap')
.append("path")
.datum(areaData)
.attr("class", "area")
.attr("d", area)
.style("fill", "#AEC7E8")
.style("opacity", .2);
This achieves a better looking solution. Because nvd3 updates the chart when the window is resized. I wrapped the drawing of the area in a function. So that I can call the function when the window is resized while removing the previously drawn area as follows:
var drawArea = function () {
d3.select(".area").remove();
d3.select('.nv-linesWrap')
.append("path")
.datum(areaData)
.attr("class", "forecastArea")
.attr("d", area)
.style("fill", "#AEC7E8")
.style("opacity", .2);
}
drawArea();
nv.utils.windowResize(resize);
function resize() {
chart.update();
drawArea();
}
I also disabled switching the series on and off using the legend as I wasn't handling that case:
chart.legend.updateState(false);
The result:

Can't fill circle background with color threshold

I try to display circles on a map for a particular dataset. The dataset provides where to center the circle. The dataset contains an identifier (attribute name), a year (attribute year) and a value (attribute value)
I would like to display two things at this level:
The radius of the circle according to the value
The background color (fill) of the circle according to the year and using threshold
A tooltip when the circle is clicked.
Here is the code I use:
var circle = d3.geo.circle();
var color = d3.scale.threshold()
.domain([ 1800, 1850, 1900, 1950, 2000, 2050 ])
.range("#f2f0f7", "#dadaeb", "#bcbddc", "#9e9ac8", "#756bb1", "#54278f");
var elements = layerElement.selectAll('circle')
.data(data)
.enter()
.append('path')
.attr('id', function(d) {
return d.name;
});
elements
.datum(function(d) {
return circle
.origin(origin({d: d}))
.angle(radius({d: d}))();
})
.attr('class', 'point')
.attr('d', path)
.style('fill', function(d) {
return color(d.year);
})
.style('opacity', '0.6');
elements.on('click', function(d) {
(...)
});
I set an identifier to each circle. I see them within the in-memory SVG DOM:
<path id="Hoba"
class="point"
d="M488.55415440889976,286.8579825670507L488.45185788883936,284.8328597859807L488.56757478032006,282.785303550314L488.90003726486356,280.73774731464727L489.445602813917,278.71262453357724L490.1982940971579,276.7321228760774L491.14986447137636,274.8179411327541L492.2898883324236,272.99105147935524L493.6058753403125,271.2714697012249L495.0834072659968,269.6780358961697L496.7062959605052,268.2282080584055L498.45676071568937,266.9378708051187L500.3156230733832,265.82116134127676L502.2625169486039,264.890314569452...L508.2862800372698,302.266499816963L506.206659850514,302.3738076922315L504.15052774957604,302.26649981696306L502.14041114802717,301.9457518786948L500.19833330399786,301.41507805895924L498.3455720287532,300.680292531176L496.6024265625401,299.74944575935126L494.9879951718629,298.63273629550935L493.5199659048794,297.34239904222255L492.2144227974436,295.8925712044583L491.08566965304294,294.2991373994032L490.1460733273343,292.5795556212728L489.4059282342853,290.752665967874L488.8733435584207,288.8384842245506Z"
style="fill: yellow; opacity: 0.75;">
My problems are:
Within the function attr, the first parameter corresponds to the selected shape but there is no identifier in it. Here is the content I get:
{"type":"Polygon","coordinates":[[[5.279833759995999,-21.628058088269754],(...)[5.525725679844768,-22.85403683844725],[5.279833759996005,-21.628058088269807]]]}
So I can't get the corresponding value to apply a background color
I have the same problem when clicking the element
My question is how to get the identifier for the selected element (circle shape in my case).
Thanks very much for your help!
Thierry
Your call to datum is generating a path from each data item but throwing away the rest of it, including the properties. If you want to keep the properties, nest the object that it generates:
.datum(function(d) {
return {
circle: circle
.origin(origin({d: d}))
.angle(radius({d: d}))(),
d: d
};
})
And then the rest of your calls will follow: like
.attr('d', function(d) { return path(d.circle); })
And
.style('fill', function(d) {
return color(d.d.year);
})

Access x value on axis d3

I think this is a simple question but I'm not getting it. I have the following code for the x-axis of my bar chart, and I'm trying to access the x-value when the corresponding bar is clicked. I've tried selecting xAxis, x, and .domain, but I'm getting null values.
var x = d3.scale.ordinal()
.domain(['191','192','255','902'])
.rangeRoundBands([margin,w-margin], .1)
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickSize(3, 0)
svg.selectAll(".series")
.data(ratiodata)
.enter()
.append("g")
.classed("series",true)
.style("fill","url(#gradient)")
.selectAll("rect").data(Object)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x",function(d,i) { return x(x.domain()[i])})
.attr("y",function(d) { return y(d.y0)})
.attr("height",function(d) { return y(0)-y(d.size)})
.attr("width",x.rangeBand())
.on("click", function(d,i) {
//Clicking on the bar currently displays elements from another dataset.//
//Ratiodata is only used for displaying the bars//
});
You can get the value in the same way in that you're setting it to start with:
.on("click", function(d, i) {
console.log(x.domain()[i]);
});

Resources