How to inject data in tooltip - d3.js

I'm using angularjs-nvd3-directives to render my charts. I have an issue on a chart where I have 2 series, one of the serie has tooltip disabled using chart.lines.interactive(false) and the other serie has it enabled.
I want to display in the remaining tooltip the value of the other serie as well as the current serie. A little bit like this: http://nvd3.org/examples/cumulativeLine.html
With angularjs-nvd3-directives you have to specify the tooltipContent function that will render the tooltip. This function takes 5 parameters:
$scope.toolTipContentFunction = function() {
return function(key, x, y, e, graph) {
...
}
}
graph is the chart function that is returned by nvd3. I tried to get the data from it but I cannot find any methods to get it.
How could I do it ?
Maxime

Related

Unable to reset the focus ordinal bar chart

I am trying to reset after choosing some of the individual's bar.
index.html: (line no. 62)
<span>
reset
</span>
This seems not to work. I was able to reset all the graphs pie chart, line chart, etc but not this one.
Those two ordinal graphs are created in index.js like this:
var focus = new dc.barChart('#focus');
var range = new dc.barChart('#range');
https://blockbuilder.org/ninjakx/483fd69328694c6b6125bb43b9f7f8a7
Update:
It looks weird now Coz it's showing a single bar and all the bar have got invisible but I want them to be visible (in gray colour) but not clickable.
This example replaces the built-in filtering functionality of the bar chart with its own implementation of ordinal selection, because the chart has a linear scale.
The example uses a global variable focusFilter to store the current selection. We need to empty this out and we also need to update the dimension filter as the original filterAll would do, pulling that code out of the click handler:
focus.applyFilter = function() { // non-standard method
if(focusFilter.length)
this.dimension().filterFunction(function(k) {
return focusFilter.includes(k);
});
else this.dimension().filter(null);
};
focus.filterAll = function() {
focusFilter = [];
this.applyFilter();
};
This will also allow dc.filterAll() to work, for a "reset all" link.
Fork of your block.
For some reason, I could not get the original
reset
links to work at all in this block, so I replaced them with the equivalent D3 click handlers:
d3.select('#reset-focus').on('click', () => {
focus.filterAll();
dc.redrawAll();
})
d3.select('#reset-all').on('click', () => {
dc.filterAll();
dc.redrawAll();
})
I also updated the focus ordinal bar example. Note that automatic hiding/showing of the reset link doesn't work because the chart still has an irrelevant range filter inside of it.

Unfiltered data on/off

I am using composite charts to see the unfiltered data, but I want to hide sometimes the unfiltered data and make the 'y' axis elastic. Hiding the unfiltered data wasn't hard, just an event listener on chart, but I can't make possible the elasticity on 'y' axis, when the unfiltered data is hidden. Perhaps it's not even possible in a case like this. Any ideas?
chart.select('.unfiltered_data').on('change', function() {
if(!this.checked) {
console.log("Stop showing unfiltered data!")
chart.select('.sub._0')
.attr('visibility', 'hidden')
// chart.elasticY(true)
chart.redraw()
}
else {
console.log("Show unfiltered data!")
chart.select('.sub._0')
.attr('visibility', 'visible')
// chart.elasticY(false)
chart.redraw()
}
})
There is (almost) always a way to do it in dc.js, because dc.js is a leaky abstraction by design!
First I tried to change which child charts are included in each composite chart, but that wasn't the right approach because a composite chart's children can't be changed on a redraw, only on a render. And we want to animate when switching between showing the unfiltered and not showing.
So instead I thought we could
use your visibility idea
turn off elasticY when the unfiltered is hidden, and
use the filtered child chart's domain instead
So I added a checkbox
<label><input type="checkbox" id="unfiltered" name="unfiltered" checked> Show Unfiltered</label>
and a global variable
var show_unfiltered = true;
The handler looks like this:
function ydomain_from_child1(chart) {
chart.y().domain([0, chart.children()[1].yAxisMax()]);
chart.resizing(true);
}
d3.select('#unfiltered').on('change', function() {
show_unfiltered = this.checked;
charts.forEach(chart => {
chart.select('.sub._0').attr('visibility', show_unfiltered ? 'visible' : 'hidden');
chart.elasticY(show_unfiltered);
if(!show_unfiltered) {
ydomain_from_child1(chart);
chart.children()[1].colors(d3.schemeCategory10);
chart.on('preRedraw.hide-unfiltered', ydomain_from_child1);
}
else {
chart.children()[1].colors('red');
chart.on('preRedraw.hide-unfiltered', null);
}
})
dc.redrawAll();
});
Whenever the checkbox is toggled, we turn on or off elasticY based on the setting. When the unfiltered are not shown, we'll simulate elasticY with a preRedraw handler which determines the domain from the second (filtered) child chart.
Additionally, we turn on/off the red color scheme for the filtered chart based on the checkbox.
I have added this to the compare unfiltered example.
I found I had to make one more change: the filtered chart was hidden when there were no filters. So I had to disable this hiding if the unfiltered was unchecked:
var any_filters = !show_unfiltered || charts.some(chart => chart.filters().length);
chart.select('.sub._1')
.attr('visibility', any_filters ? 'visible' : 'hidden')

dc.js - dynamically change valueAccessor of a stacked layer in a lineChart and redraw it

I am trying to realize a dashboard to display basic data.
I am actually completely stuck on an issue. Strangely enough, I couldn't find anything even similar to it online, so I don't have many leads on how to move forward.
I have mainly two charts:
a lineChart called "stackChart" that
displays consumption as a base layer with its valueAccessor function
dispalys production as a stacked layer with its value Accessor function
a barChart called "volumeChart" that is simply the rangeChart for the lineChart
I use radio buttons to select whether to aggregate the grouped data by sum or by average (using the same approach as this example) and then I just use:
stackChart.valueAccessor(/*function with new value (avg or sum)*/);
dc.redrawAll();
to refresh the base layer (consumption).
What I don't manage to do is to refresh the "stacked layer" by updating its valueAccessor! I can't find any way to access its valueAccessor (or, worst case, just completely remove the stacked layer and then add a new refreshed stacked layer using just ".stack(...)").
Here is the respective part of my code where the chart is built:
// Charts customization #js
stackChart
.renderArea(true)
.height(350)
.transitionDuration(1500)
.dimension(dateDim)
.group(powByTime, "Consumption")
// BASE LAYER valueAccessor HERE
.valueAccessor(function(d) { return d.value.conSum; })
.x(d3.time.scale().domain([minDate, maxDate]))
.xUnits(d3.time.days)
.elasticY(true)
.renderHorizontalGridLines(true)
.legend(dc.legend().x(80).y(0).itemHeight(13).gap(5))
.brushOn(false)
// STACKED LAYER HERE
.stack(powByTime, "Production", function(d) { return d.value.prodSum; })
.rangeChart(volumeChart)
.controlsUseVisibility(true)
;
And here is where I look for changes in the radio buttons and re-draw the layers:
// Listen for changes
d3.selectAll('#select-operation input')
.on('click', function() {
var aggrMode = this.value; // fetch "avg" or "sum" from buttons
// UPDATE BASE LAYER HERE:
stackChart.valueAccessor(function(d) { var sel = accessors[aggrMode]['consPow']; return d.value[sel]; });
// ???HOW TO UPDATE STACKED LAYER valueAccessor function???
//stackChart.stack.valueAccessor(function(d) { var sel = accessors[aggrMode]['prodPow']; return d.value[sel]; });
dc.redrawAll();
});
If you need more details on what I am trying to do and full code you can check here.
As a reference, here is what it looks like:
I don't really know dc.js, but it may be possible that you can't change an accessor once it's been set. Try writing a single function for your accessor that will return either the sum or the average, depending on the state of some variable that you can set.
#Ryan's solution will probably work fine (and may be a better design), but here's the lowdown on the dc.js API with respect to stacking, in case you need it.
As described in this issue the group and stack API is pretty weird. It grew organically, in a backward-compatible way, so both the stacks and the value accessors on top of the stacks sort of branch out in a beautiful fractal of... well, no it's pretty messy.
But the issue also suggests the solution for your problem. Since chart.group() resets the set of stacks, just go ahead and build them all from scratch in your event handler:
stackChart.group(powByTime, "Consumption") // this resets the stacks
.valueAccessor(function(d) { var sel = accessors[aggrMode]['consPow']; return d.value[sel]; })
.stack(powByTime, "Production", function(d) { var sel = accessors[aggrMode]['prodPow']; return d.value[sel]; });
Internally it's just emptying an array of layers/stacks and then populating it with some references.
This is quite efficient since dc.js doesn't store your data except where it is bound to the DOM elements. So it is the same amount of work to redraw using the old group and value accessor as it is to redraw using new ones.

Is it possible to search in bubble chart with dc.js

Can we search in bubble chart using dc.js
We have a dropdown box like this:
<select name="dbox" id="dbox" multiple="true">
<option value="3200">3200</option>
....
</select>
Here I tried jquery function:
function multiple_filter(dim,selectionArray){
if (selectionArray!==null) {
dim.filter(function(d) {
return selectionArray.indexOf(d) >= 0; });
} else {
return dim.filter(null);
}
dc.redrawAll();
}
When we want to filter dimension here applying function:
$("select#dbox").on("change",function(){
var val=$(this).val();
multiple_filter(dboxDimension,val);
});
This works but main filtering dimension doesnt filter.
So How can we change function to draw main filtering dimension ? Thanks
I'd suggest you to create a new "graph" textFilterWidget, it can even allow you to search on data that aren't displayed on the bubble chart (eg longer name...)
(you need to use a different dimension (well, you can have a different one that has the same function/data) than the one from the bubble, otherwise weird stuff will happen)

highcharts mark points after graph is drawn

I am using highcharts to graph multilple series (several lines with multiple points each on one chart). The user selects one or more points on multiple lines. Data about the selected points is shown in a gridview on my asp page. After some server side logic I would like to redraw the page and put an image, marker, flag or some other way of showing the user the redrawn graph with those points "marked".
I have been playing with jquery to add an image (small circle) to the div where the chart is rendered but not having much luck with the X/Y position of the image within the div.
Any advice or examples on how I might do this? Not married to image in DIV other suggestions appreciated.
I figured it out. I made a function that is called when the point is clicked passing the whole point object. An if statement toggles the marker of the ponit and using the acumulate = true it shows all the points on my curve that have been selected. Likewise if it is already selected it toggles the marker off. Much easier than what I was trying.
Here is my function to toggle point and make them all seleted
function ChartClicked(oPointObject) {
if (oPointObject.selected) {
oPointObject.select(false, true);
}
else {
oPointObject.select(true, true);
}
}
Here is a snipet of my graph. It is in the plotOptions I call the click event
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function () {
ChartClicked(this);
}
}
}
}
},
Hope this helps someone else.

Resources