I have an annotated timeline chart that gets new data over ajax. It's working fine, new points come in and I redraw the graph. My graph has two lines, so there are two labels in the legend on top. For whatever stupid reason, every single time the graph is redrawn, the legend labels swap places! So it will say
Foo 5.2 Bar 3.6
And then I'll refresh (and there will be no new data, so the call to redraw is 100% identical to the previous one) and now it says
Bar 3.6 Foo 5.2
In the respective red and blue, of course. What on earth would possess the applet to do this? Is there any way I can control the order of legend labels? I couldn't find anything about it in the official documentation.
Try using google.visualization.DataTable. For example:
dataTable = new google.visualization.DataTable({cols:graphColumns, rows:graphRows});
chart = new google.visualization.AnnotatedTimeLine($('#chart_div')[0]);
chart.draw(dataTable)
I use it this way and don't have any problems with my legend values.
Related
How can a series be removed from the XYChartScrollbar in amCharts v4?
It was added in much the same way as series are added to the chart:
chart.scrollbarX = new am4charts.XYChartScrollbar();
chart.scrollbarX.series.push(series);
Removing it seems to remove it from the List (chart.scrollbarX.series.length goes from 1 to 0) but it remains on the scrollbar display. I've also tried removeIndex, but no luck there either.
// removes from List, but still displayed
var scrollbarIndex = chart.scrollbarX.series.indexOf(series);
chart.scrollbarX.series.removeIndex(scrollbarIndex);
// same result, removed from List but still displayed
chart.scrollbarX.series.removeValue(series);
I have also tried calling invalidate() and even deepInvalidate() to try to get the scrollbar to refresh but no success.
An example pen is at https://codepen.io/anon/pen/OrQvbw?editors=0011. Clicking on a toggle button adds the series to the chart and scrollbar, but only removes it from the chart.
Removing a series from the scrollbar itself does not seem to remove the series from the scrollbar's chart which itself is a whole other chart with distinct series. Going to look into this more and get back to you.
In the meantime, we can piggy back off of the scrollbar's removeValue method via the series' List's "removed" event, to do the same thing on scrollbarChart. The scrollbarChart's series are all clones of the original chart's series, presuming a series only ever has 1 clone and that that clone is within scrollbarChart, this code seems to do the trick (removed.oldValue is the series we just removed from scrollbarX.series):
chart.scrollbarX.series.events.on("removed", function(removed){
chart.scrollbarX.scrollbarChart.series.removeValue(removed.oldValue.clones.getIndex(0));
});
Here's a fork of your demo with that addition:
https://codepen.io/team/amcharts/pen/1b623a1b4ddf6a198bc80bdf5f5e1514
I have a number of graphs, for simplicity take this example on jsfiddle, where there is a brush-on graph, a bar chart keyed on time and a row chart keyed on some categories. In addition, I use the d3-tip library for the tooltips (in the link above a very simplified version of my tip).
In order to avoid the creation of a bar-row in a rowChart, I used the fake-group as outlined in the FAQ of dc-js (and here as well).
The fake group works well, not displaying the C category on the row chart.
However, if I brush on some months with 0 data, when I reset the filter (just click anywhere but the filtered region on the brushon chart), the d3-tip on the row chart disappears.
Notice the if the group is created without the fake-grouping-function, this problem does not arise.
Any explanation why this happens?
How to avoid this (without loosing the remove_empty_bins)?
Although you can use dc.js and d3.js interchangeably, and dc.js is intentionally a "leaky abstraction", some things will go better if you do them the idiomatic dc.js way.
I have two suggestions:
Apply your tooltips in response to dc.js events so that they will get reapplied when new graphical objects are created (or re-created).
Use chart.selectAll instead of d3.selectAll when modifying the charts.
Okay, #2 actually has no bearing on this question, but it does help scope the selects better so that it's harder for them to miss the chart or accidentally modify stuff elsewhere in the page.
Implementing #1 looks something like this:
month_chart.on('pretransition', function(chart) {
chart.selectAll('rect.bar').call(month_tip)
.on('mouseover', month_tip.show).on('mouseout', month_tip.hide);
});
loc_chart1.on('pretransition', function(chart) {
chart.selectAll('g.row').call(loc_tip)
.on('mouseover', loc_tip.show).on('mouseout', loc_tip.hide);
});
The pretransition event fires right after a render or redraw, so it's usually the best moment to manipulate dc's elements. Much better than just running the code globally. I like to set everything up, then call dc.renderAll(), then allow the renders and redraws to take care of themselves later on.
In particular, when those bars get added back in when remove_empty_bins stops removing them, these events will pick them up and re-tip them.
Fork of your fiddle: https://jsfiddle.net/gordonwoodhull/5feL3gko/4/
Could some one please suggest how to do column-drilldown with D3 JS library,
below example is from Hightchart,
http://www.highcharts.com/demo/column-drilldown
A complete code example for this problem is probably quite extensive, so I'll mostly keep to how you would approach it and assume you know enough of D3 to turn the concept into code.
Lets assume you have the functionality for drawing a general bar chart.
Part of that functionality would priobably be things like
Setting up your svg element and containers
Setting up your scales (one for x and one for y)
Adding axes based on the scales you have created
Adding your bars to the svg container
4.1 Make sure you have your data set available as an array
4.2 Create an enter selection for the available data and append rectelements
4.3 Update attributes like x, y for all your available bar nodes
4.4 Remove any nodes on your exit selection
Voila you have a simple bar chart. Nothing new in that and you can have a look at the code details here -> https://bl.ocks.org/mbostock/3885304
Now in order to do the drill down:
In order to avoid lots of code repetition it probably makes sense to separate the above steps into functions. So for example a setup function that just creates your svg and containers as well as your scales.
Important about the setup function is that you do not need to rerun it on drill through.
Second you will want an update function. This contains steps 3+, which you will need to rerun in order to update your charts on drill through.
One addition here would be adding functionality for updating your scale domain in the beginning (as your data changes on drill through and you want to reflect that in your scales).
Now that you have those two functions all you really need to do is:
Add a click handler to your axis labels or your bars (click on bars might be easier for now). You cans use d3's .on() function for that.
In that event you will want to subset your data by the value of the clicked bar (or get a new data set for the bar value depending on how your data is structured) and then run the update function we created above with the new data.
It could look something like this:
d3.selectAll('.bar-nodes')
.on('click', function(d) {
var updatedData = updateData(d);
updateChart(updatedData);
});
If anything is unclear some more specific questions would be good.
Hope that helps.
I created a grouped-stacked composite chart using dc.js.
I am wondering if I can add a filterHandler to a composite chart.
I want to filter on each stacks.
It seems that, there is no straight forward way to build a composite chart and hook the parent chart to the volumeChart that would filter all the individual child components at once via dc.js, as Gordon suggested. I came up with a solution for one of my dashboards which is not so modular but works fine & also keep in mind the downsides to this approach pointed out below.
For the above dashboard to work, I defined 3 barCharts and 3 lineCharts overlaid on top the barCharts and chaining them all together linearly to volumeChart.
Although this works fine with a fixed y-axis scale, lineChart fails to align with the barCharts when the I set elasticY(true) on all of them, since the lineChart components seem to pic its own yMax and when I pass the yMax value as window.value from barCharts to the lineChart via .on('renderlet'..., again I fail to read these values due the misalignment of individual chart render times. And of course when I overlay the lineCharts I intrinsically loose/block the interaction with the barCharts and use the line moving average tooltips to view bar data.
I have not tried this yet but found a better way to do the same from Scott Miller - https://stackoverflow.com/a/25188909/5743716
Hope it helps!
In D3 I am working on customizing and adding to #Rengel's timeline (link shown in JS Fiddle.
I've successfully created a similar timeline based on tracks, and it also allows users to filter project data based on checkbox values. Each piece of data now has a tooltip, and there are letters from another dataset populating underneath the projects in the same svg container. Now finally I want to add a brush like this one - wrobstory's from the blocks website.
I have only recently started to work on brush events so I am very much a noob, which is why I am unsure how I am going wrong. There is a JS Fiddle I created at: https://jsfiddle.net/rootseire/2vq8028o/2/ which shows everything working before the brush gets called. When I select a section of the timeline, the brush appears, it calculates indexes and extent. But it changes the y state of the year and the year text then transitions down the page.
I have been trying to see why this is happening, but I think I need to step back from the code as it might be just that I am not referencing the correct element. Here is the code for when the mouse pointer drags over the interface:
vis.on('mousedown', function(){
brush_elm = vis.select(".brush").node();
new_click_event = new Event('mousedown');
new_click_event.pageX = d3.event.pageX;
new_click_event.clientX = d3.event.clientX;
new_click_event.pageY = d3.event.pageY;
new_click_event.clientY = d3.event.clientY;
brush_elm.dispatchEvent(new_click_event);
});
But I think it might have something to do with the .points selector. How can I make the brush target the x-axis, the project rectangles and the letters together?
Thanks in advance,
P