in nvd3 multibarchart, some stacks lose their colors or otherwise become invisible - d3.js

How do I ensure that bars in multibarchart in nvd3 always starts from same level? What I am seeing is that some series start kinda "floating" in the air (actually one of the stacks loses its color)
Please see http://jsfiddle.net/TZ2kH/1/ and click on "stacked" option (initial settings - that is another question)
My data series is very short, just 3 rows - 2 in one sub-series and 1 in another sub-series.
data_multiBarChart = [{
'values': [ {
'y': 7,
'x': 9
}],
'key': 'Count',
'yAxis': '1'
}, {
'values': [{
'y': 12,
'x': 0
}, {
'y': 8,
'x': 1
}],
'key': 'Duration',
'yAxis': '1'
}];
Thanks.
--EDIT--
FWIW warnings seen in firebug console:
Unexpected value NaN parsing height attribute.
this.setAttribute(name, f(t));
I have also seen with similarly sparse data:
Unexpected value NaN parsing height attribute.
...3.interpolateRgb=function(e,t){e=d3.rgb(e),t=d3.rgb(t);var n=e.r,r=e.g,i=e.b,s=t...

Its because of the data you are passing into the chart. The count of sub-series in you chart must be equal, if you do not have a value you must at least set the y value to 0.
A multiple bar graph contains comparisons of two or more categories or bars. When the chart is drawn, the X-Axis must be in a sequence, take a look at how the X-Axis is lined up in this fiddle
Try using the data as shown below :
data_multiBarChart = [{
'values': [{
'y': 0,
'x': 1
}, {
'y': 8,
'x': 2
}],
'key': 'Count',
'yAxis': '1'
}, {
'values': [{
'y': 12,
'x': 1
}, {
'y': 8,
'x': 2
}],
'key': 'Duration',
'yAxis': '1'
}];
Hope I made sense, and helps solve your problem.

Related

How to animate something at the same time in GASP?

I want to animate these two things at the same time, and not one by one.
t1.fromTo(searchForm, { scaleX: 0 }, { duration: 1, autoAlpha: 1, scaleX: 1 });
t1.fromTo(loupe, {x: '-=0'}, {duration: 1, x: '+=265'})
I want to move them together; how can I do that?
Just use the position parameter to position your tween(s) wherever you want in the timeline.
t1.fromTo(searchForm, { scaleX: 0 }, { duration: 1, autoAlpha: 1, scaleX: 1 });
t1.fromTo(loupe, {x: '-=0'}, {duration: 1, x: '+=265'}, 0);
There's more info about the position parameter at https://greensock.com/position-parameter.
Also, there's no reason to use a .fromTo() in the second case because x: "-=0" does absolutely nothing, so you can just to a normal .to() tween and omit that whole object.
Happy tweening!

d3.js v4: How to access parent group's datum index?

The description of the selection.data function includes an example with multiple groups (link) where a two-dimensional array is turned into an HTML table.
In d3.js v3, for lower dimensions, the accessor functions included a third argument which was the index of the parent group's datum:
td.text(function(d,i,j) {
return "Row: " + j;
});
In v4, this j argument has been replaced by the selection's NodeList. How do I access the parent group's datum index now?
Well, sometimes an answer doesn't provide a solution, because the solution may not exist. This seems to be the case.
According to Bostock:
I’ve merged the new bilevel selection implementation into master and also simplified how parents are tracked by using a parallel parents array.
A nice property of this new approach is that selection.data can
evaluate the values function in exactly the same manner as other
selection functions: the values function gets passed {d, i, nodes}
where this is the parent node, d is the parent datum, i is the parent
(group) index, and nodes is the array of parent nodes (one per group).
Also, the parents array can be reused by subselections that do not
regroup the selection, such as selection.select, since the parents
array is immutable.
This change restricts functionality—in the sense that you cannot
access the parent node from within a selection function, nor the
parent data, nor the group index — but I believe this is ultimately A
Good Thing because it encourages simpler code.
(emphasis mine)
Here's the link: https://github.com/d3/d3-selection/issues/47
So, it's not possible to get the index of the parent's group using selection (the parent's group index can be retrieved using selection.data, as this snippet bellow shows).
var testData = [
[
{x: 1, y: 40},
{x: 2, y: 43},
{x: 3, y: 12},
{x: 6, y: 23}
], [
{x: 1, y: 12},
{x: 4, y: 18},
{x: 5, y: 73},
{x: 6, y: 27}
], [
{x: 1, y: 60},
{x: 2, y: 49},
{x: 3, y: 16},
{x: 6, y: 20}
]
];
var svg = d3.select("body")
.append("svg")
.attr("width", 300)
.attr("height", 300);
var g = svg.selectAll(".groups")
.data(testData)
.enter()
.append("g");
var rects = g.selectAll("rect")
.data(function(d, i , j) { console.log("Data: " + JSON.stringify(d), "\nIndex: " + JSON.stringify(i), "\nNode: " + JSON.stringify(j)); return d})
.enter()
.append("rect");
<script src="https://d3js.org/d3.v4.min.js"></script>
My workaround is somewhat similar to Dinesh Rajan's, assuming the parent index is needed for attribute someAttr of g.nestedElt:
v3:
svg.selectAll(".someClass")
.data(nestedData)
.enter()
.append("g")
.attr("class", "someClass")
.selectAll(".nestedElt")
.data(Object)
.enter()
.append("g")
.attr("class", "nestedElt")
.attr("someAttr", function(d, i, j) {
});
v4:
svg.selectAll(".someClass")
.data(nestedData)
.enter()
.append("g")
.attr("class", "someClass")
.attr("data-index", function(d, i) { return i; }) // make parent index available from DOM
.selectAll(".nestedElt")
.data(Object)
.enter()
.append("g")
.attr("class", "nestedElt")
.attr("someAttr", function(d, i) {
var j = +this.parentNode.getAttribute("data-index");
});
I ended up defining an external variable "j" and then increment it whenever "i" is 0
example V3 snippet below.
rowcols.enter().append("rect")
.attr("x", function (d, i, j) { return CalcXPos(d, j); })
.attr("fill", function (d, i, j) { return GetColor(d, j); })
and in V4, code converted as below.
var j = -1;
rowcols.enter().append("rect")
.attr("x", function (d, i) { if (i == 0) { j++ }; return CalcXPos(d, j); })
.attr("fill", function (d, i) { return GetColor(d, j); })
If j is the nodeList...
j[i] is the current node (eg. the td element),
j[i].parentNode is the level-1 parent (eg. the row element),
j[i].parentNode.parentNode is the level-2 parent (eg. the table element),
j[i].parentNode.parentNode.childNodes is the array of level-1 parents (eg. array of row elements) including the original parent.
So the question is, what is the index of the parent (the row) with respect to it's parent (the table)?
We can find this using Array.prototype.indexOf like so...
k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes,j[i].parentNode);
You can see in the snippet below that the row is printed in each td cell when k is returned.
var testData = [
[
{x: 1, y: 1},
{x: 1, y: 2},
{x: 1, y: 3},
{x: 1, y: 4}
], [
{x: 2, y: 1},
{x: 2, y: 2},
{x: 2, y: 3},
{x: 2, y: 4}
], [
{x: 3, y: 4},
{x: 3, y: 4},
{x: 3, y: 4},
{x: 3, y: 4}
]
];
var tableData =
d3.select('body').selectAll('table')
.data([testData]);
var tables =
tableData.enter()
.append('table');
var rowData =
tables.selectAll('table')
.data(function(d,i,j){
return d;
});
var rows =
rowData.enter()
.append('tr');
var eleData =
rows.selectAll('tr')
.data(function(d,i,j){
return d;
});
var ele =
eleData.enter()
.append('td')
.text(function(d,i,j){
var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes,j[i].parentNode);
return k;
});
<script src="https://d3js.org/d3.v4.min.js"></script>
Reservations
This approach is using DOM order as a proxy for data index. In many cases, I think this is a viable band-aid solution if this is no longer possible in D3 (as reported in this answer).
Some extra effort in manipulating the DOM selection to match data might be needed. As an example, filtering j[i].parentNode.parentNode.childNodes for <tr> elements only in order to determine the row -- generally speaking the childNodes array may not match the selection and could contain extra elements/junk.
While this is not a cure-all, I think it should work or could be made to work in most cases, presuming there is some logical connection between DOM and data that can be leveraged which allows you to use DOM child index as a proxy for data index.
Here's an example of how to use the selection.each() method. I don't think it's messy, but it did slow down the render on a large matrix. Note the following code assumes an existing table selection and a call to update().
update(matrix) {
var self = this;
var tr = table.selectAll("tr").data(matrix);
tr.exit().remove();
tr.enter().append("tr");
tr.each(addCells);
function addCells(data, rowIndex) {
var td = d3.select(this).selectAll("td")
.data(function (d) {
return d;
});
td.exit().remove();
td.enter().append("td");
td.attr("class", function (d) {
return d === 0 ? "dead" : "alive";
});
td.on("click", function(d,i){
matrix[rowIndex][i] = d === 1 ? 0 : 1; // rowIndex now available for use in callback.
});
}
setTimeout(function() {
update(getNewMatrix(matrix))
}, 1000);
},
Assume you want to do a nested selectiom, and your
data is some array where each element in turn
contains an array, let's say "values". Then you
have probably some code like this:
var aInnerSelection = oSelection.selectAll(".someClass") //
.data(d.values) //
...
You can replace the array with the values by a new array, where
you cache the indices within the group.
var aInnerSelection = oSelection.selectAll(".someClass") //
.data(function (d, i) {
var aData = d.values.map(function mapValuesToIndexedValues(elem, index) {
return {
outerIndex: i,
innerIndex: index,
datum: elem
};
})
return aData;
}, function (d, i) {
return d.innerIndex;
}) //
...
Assume your outer array looks like this:
[{name "X", values: ["A", "B"]}, {name "y", values: ["C", "D"]}
With the first approach, the nested selection brings you from here
d i
------------------------------------------------------------------
root dummy X {name "X", values: ["A", "B"]} 0
dummy Y {name "Y", values: ["C", "D"]} 1
to here.
d i
------------------------------------------------------------------
root X A "A" 0
B "B" 1
Y C "C" 2
D "D" 3
With the augmented array, you end up here instead:
d i
------------------------------------------------------------------
root X A {datum: "A", outerIndex: 0, innerIndex: 0} 0
B {datum: "B", outerIndex: 0, innerIndex: 1} 1
Y C {datum: "C", outerIndex: 1, innerIndex: 0} 2
D {datum: "D", outerIndex: 1, innerIndex: 1} 3
So you have within the nested selections, in any function(d,i), all
information you need.
Here's a snippet I crafter after re-remembering this usage of .each for nesting, I thought it may be useful to others who end up here. This examples creates two layers of circles, and the parent group index is used to determine the color of the circles - white for the circles in the first layer, and black for the circles in the top layer (only two layers in this case).
const nested = nest().key(layerValue).entries(data);
let layerGroups = g.selectAll('g.layer').data(nested);
layerGroups = layerGroups.enter().append('g').attr('class', 'layer')
.merge(layerGroups);
layerGroups.each(function(layerEntry, j) {
const circles = select(this)
.selectAll('circle').data(layerEntry.values);
circles.enter().append('circle')
.merge(circles)
.attr('cx', d => xScale(xValue(d)))
.attr('cy', d => yScale(yValue(d)))
.attr('r', d => radiusScale(radiusValue(d)))
.attr('fill', j === 0 ? 'white' : 'black'); // <---- Access parent index.
});
My solution was to embed this information in the data provided to d3js
data = [[1,2,3],[4,5,6],[7,8,9]]
flattened_data = data.reduce((acc, v, i) => {
v.forEach((d, j) => {
data_item = { i, j, d };
acc.push(data_item);
});
return acc;
}, []);
Then you can access i, j and d from the data arg of the function
td.text(function(d) {
// Can access i, j and original data here
return "Row: " + d.j;
});

handsontable - maxRows - remove rows option disabled

I would like to create a table with the option maxRows. But when I use this option and the table have reached the max row count the option "remove row" within the context menu is disabled. Does anybody know why this option is getting disabled when the table have reached the max count of rows?
Example code:
var myData = [
["", "Kia", "Nissan", "Toyota", "Honda"],
["2008", 10, 11, 12, 13],
["2009", 20, 11, 14, 13],
["2010", 30, 15, 12, 13]
],
container = document.querySelector('#exampleGrid');
var hot = new Handsontable(container, {
data: myData,
startRows: 5,
startCols: 5,
minSpareCols: 1,
//always keep at least 1 spare row at the right
minSpareRows: 1,
//always keep at least 1 spare row at the bottom,
maxRows:8,
rowHeaders: true,
colHeaders: true,
contextMenu: true
});

Query Mongoid for all circle areas that include a given point

Let say I have two classes:
class Cirle
include Mongoid::Document
field :lat, type: Float
field :lon, type: Float
field :radius, type: Integer
end
class Point
include Mongoid::Document
field :lat, type: Float
field :lon, type: Float
end
How can I find all Circles that include a given Point?
I'm not familiar with Mongoid, but perhaps the following will help. Suppose:
circles = [
{ x: 1, y: 2, radius: 3 },
{ x: 3, y: 1, radius: 2 },
{ x: 2, y: 2, radius: 4 },
]
and
point = { x: 4.5, y: 1 }
then the circles containing point are obtained with the help of Math::hypot:
circles.select { |c|
Math.hypot((c[:x]-point[:x]).abs, (c[:y]-point[:y]).abs) <= c[:radius] }
#=> [{ x: 3, y: 1, radius: 2 }, { x: 2, y: 2, radius: 4 }]
Edit: to improve efficiency as #Drenmi suggests:
x, y = point.values_at(:x, :y)
circles.select do |c|
d0, d1, r = (c[:x]-x).abs, (c[:y]-y).abs, c[:radius]
d0*d0 + d1*d1 <= r*r
end

Can't get nvd3 x value labels to display right

I'm trying to specify the x axis labels as strings. I can get the strings to show up, but I can't get them to spread out/align properly. All of the numbers are displaying correctly, I just can't seem to get the labels to spread out correctly or the domain to show up. I'm really looking to get the labels working, but the domain seemed like it could be an alternate way possibly.
nv.addGraph(function() {
var chart = nv.models.lineChart();
var fitScreen = false;
var width = 600;
var height = 300;
var zoom = 1;
chart.useInteractiveGuideline(true);
chart.xAxis
.domain(["Sunday","Monday","Tuesday","Wednesday","Thursday"])
.tickValues(['Label 1','Label 2','Label 3','Label 4','Label 5'])
.ticks(5);
//.tickFormat(d3.format('d'));
chart.yAxis
.tickFormat(d3.format(',.2f'));
d3.select('#chart1 svg')
.attr('perserveAspectRatio', 'xMinYMid')
.attr('width', width)
.attr('height', height)
.datum(veggies);
setChartViewBox();
resizeChart();
return chart;
});
Data
veggies = [
{
key: "Peas",
values: [
{x: 0, y: 2},
{x: 1, y: 5},
{x: 2, y: 4}
]
},
{
key: "Carrots",
values: [
{x: 0, y: 2},
{x: 1, y: 5},
{x: 2, y: 4}
]
}
];
The "domain" of a d3 scale or axis is the input data. Which in your data set look like numbers, not names of the week. See http://alignedleft.com/tutorials/d3/scales
You want to something like xAxis.domain([0,1,2,3,4,5]);.
So how do you get labels to go with your numbers? Without thinking about it to closely, I suggested using the "range" (output) part of the scale. But range for the axis defines the numerical spacing for the categories on your graph, not the labels.
What you want is a custom formatting function that converts the numbers from the data into labels. You set that with the "tickFormat" option:
xAxis.tickFormat( function(index) {
var labels = ["Label0", "Label1", "Label2", "Label3", "Label4", "Label5"];
return labels[index];
});
Normally, tick formatting functions are used to format numbers into decimals or percents, but the syntax allows you to use any function that takes the data value as a parameter (I've named it index here) and returns the string that you want to be displayed. The line return labels[index]; finds the index-numbered element in the labels array, where the first label is index 0.
If your data values aren't consecutive integers starting with zero, you can use the object/associative array format, with named elements, instead of just an array. For example, if your data had the values "M", "T", "W", "R", etc., and you wanted it to display full day names, you would use:
xAxis.tickFormat(function(name) {
var labels = {"M":"Monday", "T":"Tuesday", "W":"Wednesday",
"R": "Thursday", "F":"Friday"};
return labels[name];
});
The values you pass to tickValues() have to be values in the input domain,* i.e, in the same format as the values in the JSON data. Also, once you set tickValues explicitly, don't over-ride that setting by setting a number of ticks. But you should only need tickValues if you only want some of the days to have labels (for example, if there is not enough space on the chart for all the names).
*Note correction.

Resources