dimplejs - Toggling the display of a series selectively - dimple.js

To the Word-Awesomeness example in dimplejs documentation, I have added 2 series with dimple.plot.bar and dimple.plot.line plotFunctions as shown below:
Chart with 2 series:
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://dimplejs.org/dist/dimple.v2.1.0.min.js"></script>
</head>
<body>
<script type="text/javascript">
var svg = dimple.newSvg("body", 800, 600);
var data = [
{ "Word":"Hello", "Awesomeness":2000 },
{ "Word":"World", "Awesomeness":3000 }
];
var chart = new dimple.chart(svg, data);
chart.addCategoryAxis("x", "Word");
chart.addMeasureAxis("y", "Awesomeness");
chart.addSeries(null, dimple.plot.bar);
chart.addSeries(null, dimple.plot.line);
chart.draw();
</script>
</body>
This displays the chart with both line and bar. I would like to hide/show the line and bar based on user selection. I tried removing a series using chart.series.splice (0,1) and redrawing. That did not work. The chart always shows both bar and line series.
However, the documentation of dimple.chart.series states that:
This collection is provided for viewing series which have been added with the addSeries method. However it may be modified directly to remove or move a series.
Example:
// Add three series and remove the middle one using a standard JavaScript array operation
myChart.addSeries("Brand", dimple.plot.bar);
myChart.addSeries("Brand", dimple.plot.bubble);
myChart.addSeries("Brand", dimple.plot.line);
myChart.series.splice(1, 1);
Please let me know how to hide/show a series selectively in dimplejs. Thanks.

The chart.draw code doesn't remove old series from the chart, splicing out the series will just prevent it from being updated the next time you call draw. You'll have to manually remove the elements from the chart :
myChart.addSeries("Brand", dimple.plot.bar);
myChart.addSeries("Brand", dimple.plot.bubble);
myChart.addSeries("Brand", dimple.plot.line);
chart.series[1].shapes.remove();
chart.series.splice(1, 1);
Here are some other options for toggling series :
http://jsbin.com/sacaze/3/edit?css,js,output
Add and remove the series each time. The loop to find the series can vary depending on how you're generating the series, it could just look for a specific combination of dimple.series.plot and dimple.series.categoryFields.
http://jsbin.com/sacaze/4/edit?html,js,output
Use the same logic to find the series, but just hide/show the shapes instead of removing the series. For toggling this is probably the approach I'd use.
To find the series you could also add a custom key to each series object :
var mySeries = chart.addSeries('category', dimple.plot.bar);
mySeries.my_custom_key = 'barSeries';
//somewhere else...
var barSeries = _.find(chart.series, function(series){ return series.my_custom_key === 'barSeries' });
Just make sure it doesn't conflict with a key on dimple.series : https://github.com/PMSI-AlignAlytics/dimple/blob/master/src/objects/series/begin.js

Related

Interactive vega-lite / vega chart with selection

I'm trying to build an interactive vega-lite dashboard where I have got this world map vega editor link
Based on the selection of a country I'm trying to display another graph below(vconcat or outside)
Is it possible to do it outside this chart without using vconcat or I can do it only by vconcat?
Has anyone tried something similar?
The easiest would be to create with vconcat.
That said, there is a way to read the underlying Vega signal of the selection. Then you can use the Vega View API to trigger callback that shows another chart based on the selected data.
You can now use observable notebooks to achieve what you want.
You create you first chart in a cell, link it to a second cell and then export the cells in your web site.
Here's how to start in observable
Here is the central part of the code
letter_selected = Generators.observe(
// selection_caught will (yield) a value promise with the selected letters
function initialize_f(change_) {
// creating an event listener (ie a function to attach to some DOM element)
const signaled = (name, value) => change_(value);
// attaching the event listener and naming it "test_selection"
barChart.addSignalListener("test_selection", signaled);
// check the doc ... https://github.com/observablehq/stdlib
change_(barChart.signal("test_selection"));
function dispose_f() {
return barChart.removeSignalListener("test_selection", signaled);
}
return dispose_f;
}
)

Morris js chart - changing settings dynamically

Is it possible to update a Morris chart dynamically? I know setData() will update the data, but I want to update the settings. Namely, the user being able to select if a bar chart is stacked or not.
I have tried:
bChart.stacked = true;
bChart.setData(response);
... because setData() will redraw. I also tried bChart.redraw();. There was no change.
Any ideas welcome.
You were 90% there. You would need to set bChart.options.stacked to true; then do bChart.redraw();.
Therefore, the code for toggling stacked bars is the following (if you are using jQuery):
jQuery(function($) {
$('#stacked').on('change', function() {
bChart.options.stacked = $(this).is(':checked');
bChart.redraw();
});
});
Providing that the checkbox toggling this option has a #stacked ID.
See this working JSFiddle.

ios-charts How to invalidate/redraw after setting data

See Updates At Bottom (4/30/2015)
I'm implementing a Pie Chart in Swift for iOS using ios-charts, and have chosen to customize the legend. Of note, the chart is displayed within a cell of a UICollectionView. The problem is that on first display, the custom legend content is not being displayed. Instead, I get legend content generated from the data.
If I scroll the view off-screen, and then scroll it back onto the screen, the proper custom legend is displayed. So, I'm guessing that I need to force a redraw/relayout/re-something after setting my custom legend. I haven't figured out how to do that. Does anyone have an idea? Am I completely missing something? Thanks!
Chart on initial display - data-generated (wrong) legend
Chart after scrolling off and back onto the screen - (proper legend)
Here's my code for drawing this chart:
func initChart(pieChart: PieChartView) {
numFormatter.maximumFractionDigits = 0
pieChart.backgroundColor = UIColor.whiteColor()
pieChart.usePercentValuesEnabled = false
pieChart.drawHoleEnabled = true
pieChart.holeTransparent = true
pieChart.descriptionText = ""
pieChart.centerText = "30%\nComplete"
pieChart.data = getMyData()
// Setting custom legend info, called AFTER setting data
pieChart.legend.position = ChartLegend.ChartLegendPosition.LeftOfChartCenter
pieChart.legend.colors = [clrGreenDk, clrGold, clrBlue]
pieChart.legend.labels = ["Complete","Enrolled","Future"]
pieChart.legend.enabled = true
}
func getMyData() -> ChartData {
var xVals = ["Q201","R202","S203","T204","U205", "V206"]
var courses: [ChartDataEntry] = []
courses.append(ChartDataEntry(value: 3, xIndex: 0))
courses.append(ChartDataEntry(value: 3, xIndex: 1))
courses.append(ChartDataEntry(value: 4, xIndex: 2))
courses.append(ChartDataEntry(value: 4, xIndex: 3))
courses.append(ChartDataEntry(value: 3, xIndex: 4))
courses.append(ChartDataEntry(value: 3, xIndex: 5))
let dsColors = [clrGreenDk, clrGreenDk, clrBlue, clrBlue, clrGold, clrGold]
let pcds = PieChartDataSet(yVals: courses, label: "")
pcds.sliceSpace = CGFloat(4)
pcds.colors = dsColors
pcds.valueFont = labelFont!
pcds.valueFormatter = numFormatter
pcds.valueTextColor = UIColor.whiteColor()
return ChartData(xVals: xVals, dataSet: pcds)
}
Update 4/30/2015
Based on discussion with author of MPAndroidChart (on which ios-charts is based), it appears there is not a point in the chart display lifecycle where one can override the legend on "first draw". Basically, the chart is rendered when it is created, no matter what. If you set data on the chart, the chart uses that data to create the legend and then renders. It isn't possible to change the legend between the point of setting data, and the point of chart rendering.
setNeedsDisplay()
Potentially, one can wait for the chart to render, update the legend, and then call chart.setNeedsDisplay() to signal the chart to redraw. Sadly, there's a timing problem with this. If you call this method immediately after rendering the chart, it either doesn't fire or (more likely) it fires too soon and is effectively ignored. In my code, placing this call within viewDidLoad or viewDidAppear had no effect. However...
Building the same chart in Java for Android (using MPAndroidChart) results in the same issue. After messing around for a bit, I noted that if I called the chart.invalidate() after a delay (using Handler.postDelayed()), it would fire properly. It turns out a similar approach works for ios-charts on iOS.
If one uses GCD to delay the call to setNeedsDisplay(), for even a few milliseconds after the rendering, it seems to do the trick. I've added the following code immediately after initializing the chart's view in my ViewController ("cell" is the UICollectionViewCell containing the chart view):
delay(0.05) {
cell.pieChartView.legend.colors = [self.clrGreenDk, self.clrGold, self.clrBlue]
cell.pieChartView.legend.labels = ["Complete","Enrolled","Future"]
// Re-calc legend dimensions for proper position (Added 5/2/2015)
cell.pieChartView.legend.calculateDimensions(cell.pieChartView.labelFont!)
cell.pieChartView.setNeedsDisplay()
}
Using the awesome "delay" method from this SO post: https://stackoverflow.com/a/24318861
Obviously, this is a nasty hack, but it seems to do the trick. I'm not sure I like the idea of using this hack in production, though.
For any Android folk who stumble on this post:
The following code achieves the same effect using MPAndroidChart:
// Inside onCreate()
pie = (PieChart) findViewById(R.id.chart1);
configPieChart(pie);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
String[] legLabels = new String[]{"Complete","Enrolled","Future"};
ArrayList<Integer> legColors = new ArrayList<Integer>();
legColors.add(blue);
legColors.add(gold);
legColors.add(green);
pie.getLegend().setPosition(Legend.LegendPosition.LEFT_OF_CHART_CENTER);
pie.getLegend().setColors(legColors);
pie.getLegend().setLabels(legLabels);
pie.invalidate();
}
}, 20);
I am the author of ios-charts, and we're working on features for customizing the legend data, without those "hacks".
In the latest commits to ios-charts, you can already see extraLabels and extraColors properties that add extra lines to the legend, and a setLegend function that allows you to set a custom data entirely.
This will soon be added to the Android version as well.
Enjoy :-)

Generate dynamic pie chart on click event on a stacked line chart IN DOJO

I am using Dojo to generate alternate chart on click event of previous chart.
Firstly, i am drawing stacked line chart(say for Customers) and on clicking one of the line i need to draw another pie chart for further details of that Customer(say revenue by that customer). All that is happening for a single line chart.
Here Problem is to find id for that particular line, now i am getting array of ids for all plotted lines.
Following graph is drawn for three Customers.
And Following I am presenting my code :
var colorArray =["#2cabe2","#57E964","#736AFF","#B93B8F","#151B8D","#EE9A4D",...];
for(var i =0 ; i<length ; i++)
{
chart.addSeries(response.legend + " "+ i,response.data[i],{color: colorArray[i]});
}
chart.connectToPlot("default", function(evt) {
var shape = evt.shape, type = evt.type;
if (type == "onclick") {
url="/ritl/chart/getProcessPieData.htm?customerId="+dijit.byId('customerId').get('value'); // url to the next (pie) chart in the same jsp.
processPie(url); // calling function for pie Chart
}
});
Data Response is in the form of JSONArray. JSON pattern is as:
{"data":[[{"text":"January","value":1,"customerID":"RITL00013","y":0},{"text":"February","value":2,"customerID":"RITL00013","y":0},{"text":"March","value":3,"customerID":"RITL00013","y":0},{"text":"April","value":4,"customerID":"RITL00013","y":0},{"text":"May","value":5,"customerID":"RITL00013","y":0},{"text":"June","value":6,"customerID":"RITL00013","y":0},{"text":"July","value":7,"customerID":"RITL00013","y":0},{"text":"August","value":8,"customerID":"RITL00013","y":0},{"text":"September","value":9,"customerID":"RITL00013","y":0},{"text":"October","value":10,"customerID":"RITL00013","y":119951.2},{"text":"November","value":11,"customerID":"RITL00013","y":430827.04},{"text":"December","value":12,"customerID":"RITL00013","y":0}],[{"text":"January","value":1,"customerID":"RITL00002","y":0},{"text":"February","value":2,"customerID":"RITL00002","y":0},{"text":"March","value":3,"customerID":"RITL00002","y":0},{"text":"April","value":4,"customerID":"RITL00002","y":0},{"text":"May","value":5,"customerID":"RITL00002","y":0},{"text":"June","value":6,"customerID":"RITL00002","y":0},{"text":"July","value":7,"customerID":"RITL00002","y":0},{"text":"August","value":8,"customerID":"RITL00002","y":0},{"text":"September","value":9,"customerID":"RITL00002","y":0},{"text":"October","value":10,"customerID":"RITL00002","y":41996.52},{"text":"November","value":11,"customerID":"RITL00002","y":566353.8099999999},{"text":"December","value":12,"customerID":"RITL00002","y":0}],[{"text":"January","value":1,"customerID":"RITL00016","y":0},{"text":"February","value":2,"customerID":"RITL00016","y":0},{"text":"March","value":3,"customerID":"RITL00016","y":0},{"text":"April","value":4,"customerID":"RITL00016","y":0},{"text":"May","value":5,"customerID":"RITL00016","y":0},{"text":"June","value":6,"customerID":"RITL00016","y":0},{"text":"July","value":7,"customerID":"RITL00016","y":0},{"text":"August","value":8,"customerID":"RITL00016","y":0},{"text":"September","value":9,"customerID":"RITL00016","y":0},{"text":"October","value":10,"customerID":"RITL00016","y":132784.45},{"text":"November","value":11,"customerID":"RITL00016","y":571506.6},{"text":"December","value":12,"customerID":"RITL00016","y":0}]],"Success":true,"dataLength":3}
How to find out the individual id for the line in the above Chart. Suggest me to get out of this situation. Thanks in advance for active suggestions..
What about something like the following in your connectPlot callback:
event.run.data[event.index].customerID

Leaflet markers do not open popup on click

I just started using Leaflet and Marker Clusterer to organize the markers.
Problem #1: When an unclustered marker is clicked, no popup appears.
Problem #2: When a cluster is clicked several times, all the markers within that cluster appears, and when one of this marker is clicked, its popup appears! However, after closing the popup by clicking on the map, clicking on any of these clustered markers do not open any popups!
If I only have 3 unclustered markers, the popup works fine. However, as more markers are added, once a cluster forms, clicking on the marker within any cluster will not cause the popup to open!
Initializing markerclusterer
markers = new L.MarkerClusterGroup();
map.addLayer(markers);
All markers added to markercluster markers
A loop calls on the render function to create the marker and add it to the markerclusterer's array markers. (ignore the backbone.js code)
ListingMarkerView = Backbone.View.extend({
template: _.template( $('#tpl_ListingMarkerView').html() ),
render: function() {
// Create marker
var content = this.template( this.model.toJSON() );
var marker = new L.marker(
[this.model.get('lat'), this.model.get('lng')],
{content: content});
marker.bindPopup(content);
// Add to markerclusterer
markers.addLayer(marker);
}
});
Without markerclusterer
If I add the marker directly to map instead of the markerclusterer array markers, the popups work fine, so I guess the problem has something to do with markerclusterer.
Did I do something wrong that resulted in such behavior of the popups? All help appreciated, thanks!
From what little I know of the cluster marker group, you should do this:
var markerGroup = new L.MarkerClusterGroup();
markerGroup.on('click', function(ev) {
// Current marker is ev.layer
// Do stuff
});
To add an event handler to the cluster layer instead, do this:
markerGroup.on('clusterclick', function(ev) {
// Current cluster is ev.layer
// Child markers for this cluster are a.layer.getAllChildMarkers()
// Do stuff
});
Oh, and read the github README carefully, it's all in there...
Make sure you have the right versions of everything in your Leaflet + Clusterer stack (Js and Css). Compare against the examples in the Clusterer Github repo.