Bar Chart: Category as X Axis
-
I am trying to create a single value column chart where I can display the current value of a particular point-xid, and compare it to another point. For example, take amchart's plot of visitors to a country vs. country:
For example, I might have the value<ma-get-point-value point-xid="USA" point="myPoint"></ma-get-point-value>
None of the examples or product solutions videos appear to have this type of chart - they all seem to have time not category as the x axis. I've tried a few things with the serial chart but have been unable to make it work.
All I want to do is a simple column chart comparison of a few live points - but have struggled to find a solution.
Do you have any suggestions?
Thanks, Henry.
-
@henryblu I think it might be possible, I'll have a look at it today.
-
I can't find a way to do it. You could implement it yourself as an AngularJS component if you are up to it.
We might look at adding this to the next UI module release.
-
Actually I realized you can do something like this pretty easily using our
ma-bar-display
directive and AngularJSng-repeat
. Try this, changedesigner.points
to the name of your point array:<div layout="" layout-align="space-around center"> <div ng-repeat="point in designer.points track by point.xid" layout="column" layout-align="start center"> <ma-bar-display style="height: 100px; width: 40px;" ng-style="{color: point.chartColour}" point="point" maximum="2" direction="bottom-to-top"></ma-bar-display> <span>{{point.name}}</span> </div> </div>
-
Thanks Jared, I managed to get it working with this directive:
<ma-point-query query="{name: 'Energy'}" limit="8" points="point_array"></ma-point-query> <div layout="" layout-align="space-around center"> <div ng-repeat="point in point_array track by point.xid" layout="column" layout-align="start center"> <ma-bar-display style="height: 350px; width: 40px;" ng-style="{color: point.chartColour}" point="point" maximum="2" direction="bottom-to-top"></ma-bar-display> <span>{{point.name}}</span> </div> </div>
It does work, but still obviously lacks the serial chart functionality (selecting/unselecting, axis labels/titles, pop-up toggle etc.) From an energy monitoring perspective I think it would make a great addition to the dashboard UI. For example if you wanted to set up a virtual point of the total energy used in each building per person, and display that on a bar chart - comparing the performance of each building.
I hope it comes out in the next release, cheers!
-
Yeah I agree, a way to do it in AmCharts would be nice. I'll look into it for a future release.
-
Hi @Jared-Wiltshire,
This question is relatively similar, so I thought I would post it here. If I have a stacked bar chart:
Is there an easy way to edit the 'stacking method', in order to convert it to a cluster chart? i.e.
In an ideal world, perhaps something like
stacktype: cluster
Any suggestions would be really appreciated!
-
Hi Henry,
There is an easy way!
If you want all columns clustered, you can set
<ma-serial-chart stack-type="none" ...
and then for each series... series-1-graph-options="{clustered:true}" ...
If you want some stacked in one stack and some stacked in another, you can set
stack-type="regular"
then pass inseries-x-graph-options="{clustered: true, newStack:true}"
and a new stack will begin with series x -
Sorry @Jared-Wiltshire, I'm being continually pressed to make the category as x axis chart work. Say I have a chart from AmCharts I want to use: https://www.amcharts.com/demos/column-with-rotated-series/
Here is the HTML file:<!-- Styles --> <style> #chartdiv { width: 100%; height: 500px; } .amcharts-export-menu-top-right { top: 10px; right: 0; } </style> <!-- Resources --> <script src="https://www.amcharts.com/lib/3/amcharts.js"></script> <script src="https://www.amcharts.com/lib/3/serial.js"></script> <script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script> <link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" /> <script src="https://www.amcharts.com/lib/3/themes/light.js"></script> <!-- Chart code --> <script> var chart = AmCharts.makeChart("chartdiv", { "type": "serial", "theme": "light", "marginRight": 70, "dataProvider": [{ "country": "USA", "visits": 3025, "color": "#FF0F00" }, { "country": "China", "visits": 1882, "color": "#FF6600" }, { "country": "Japan", "visits": 1809, "color": "#FF9E01" }, { "country": "Germany", "visits": 1322, "color": "#FCD202" }, { "country": "UK", "visits": 1122, "color": "#F8FF01" }, { "country": "France", "visits": 1114, "color": "#B0DE09" }, { "country": "India", "visits": 984, "color": "#04D215" }, { "country": "Spain", "visits": 711, "color": "#0D8ECF" }, { "country": "Netherlands", "visits": 665, "color": "#0D52D1" }, { "country": "Russia", "visits": 580, "color": "#2A0CD0" }, { "country": "South Korea", "visits": 443, "color": "#8A0CCF" }, { "country": "Canada", "visits": 441, "color": "#CD0D74" }], "valueAxes": [{ "axisAlpha": 0, "position": "left", "title": "Visitors from country" }], "startDuration": 1, "graphs": [{ "balloonText": "<b>[[category]]: [[value]]</b>", "fillColorsField": "color", "fillAlphas": 0.9, "lineAlpha": 0.2, "type": "column", "valueField": "visits" }], "chartCursor": { "categoryBalloonEnabled": false, "cursorAlpha": 0, "zoomable": false }, "categoryField": "country", "categoryAxis": { "gridPosition": "start", "labelRotation": 45 }, "export": { "enabled": true } }); </script> <!-- HTML --> <div id="chartdiv"></div>
Consider that my value for USA was stored in a datapoint USA:
<ma-get-point-value point-xid="USA" point="USA"></ma-get-point-value>
I want to be able to pass this value into the javascript to generate the graph, but interpolating the JS expression {{USA.value}} and placing it in like
"visits": {{USA.value}},
Wont work. What is the best way to pass angular expressions into Javascript?
-
You probably want to create a new component in your userModules.js file. Here's a simple one which just watches some data from an attribute and renders it on a chart with the options specified inside the component's javascript. This is a straight copy of the example you linked to.
define(['angular', 'require', 'amcharts/serial'], function(angular, require, AmCharts) { 'use strict'; var userModule = angular.module('userModule', ['maUiApp']); userModule.component('categoryXAxis', { bindings: { values: '<?' }, controller: ['$scope', '$element', function($scope, $element) { const options = { "type": "serial", "theme": "light", "addClassNames": true, "marginRight": 70, "dataProvider": [], "valueAxes": [{ "axisAlpha": 0, "position": "left", "title": "Visitors from country" }], "startDuration": 1, "graphs": [{ "balloonText": "<b>[[category]]: [[value]]</b>", "fillColorsField": "color", "fillAlphas": 0.9, "lineAlpha": 0.2, "type": "column", "valueField": "visits" }], "chartCursor": { "categoryBalloonEnabled": false, "cursorAlpha": 0, "zoomable": false }, "categoryField": "country", "categoryAxis": { "gridPosition": "start", "labelRotation": 45 } }; const chart = AmCharts.makeChart($element[0], options); $scope.$watch('$ctrl.values', () => { chart.dataProvider = this.values; chart.validateData(); }, true); }] }); return userModule; }); // define
and a quick example of how to pass values through to it showing that the graph is updated dynamically
<div class="ma-designer-root" id="84e87319-1442-41fa-a4ef-a4102f9dbbc0" style="width: 1024px; height: 768px; position: relative;"> <div ng-init="values = [{'country': 'USA','visits': 3025,'color': '#FF0F00'}, {'country': 'China','visits': 1882,'color': '#FF6600'}, {'country': 'Japan','visits': 1809,'color': '#FF9E01'}, {'country': 'Germany','visits': 1322,'color': '#FCD202'}, {'country': 'UK','visits': 1122,'color': '#F8FF01'}, {'country': 'France','visits': 1114,'color': '#B0DE09'}, {'country': 'India','visits': 984,'color': '#04D215'}, {'country': 'Spain','visits': 711,'color': '#0D8ECF'}, {'country': 'Netherlands','visits': 665,'color': '#0D52D1'}, {'country': 'Russia','visits': 580,'color': '#2A0CD0'}, {'country': 'South Korea','visits': 443,'color': '#8A0CCF'}, {'country': 'Canada','visits': 441,'color': '#CD0D74'}]"></div> <category-x-axis class="amchart" id="8ec0e029-4b0b-4f53-aa0d-6c25b4bfa991" style="position: absolute; left: 27px; top: 98px; width: 800px; height: 400px;" values="values"></category-x-axis> <md-input-container id="91a79a8e-460b-44c8-96b5-041ab5e23d56" style="position: absolute; left: 56px; top: 19px;"> <label>USA visits</label> <input ng-model="values[0].visits" type="number"> </md-input-container> </div>
edit. Worth noting that I added the
"addClassNames": true,
option so the chart axis colors are set appropriately inside Mango. Also addclass="amchart"
to the chart. -
Thanks @Jared-Wiltshire!
My last qn - using ng-model, I can see how you have passed the value for an input into a graph's 'values'. How can I do this for a point value? . I tried interpolating a point value {{point.value}} directly inside values = "..." but that doesn't work. I.e<div class="ma-designer-root" id="84e87319-1442-41fa-a4ef-a4102f9dbbc0" style="width: 1000px; height: 768px; position: relative;"> <ma-get-point-value point-xid="EnergyOutA_Panasonic" point="myPoint" ></ma-get-point-value> <div ng-init="values = [{'country': 'USA','visits': {{myPoint.value}},'color': '#FF0F00'}, {'country': 'China','visits': 1882,'color': '#FF6600'}, {'country': 'Japan','visits': 1809,'color': '#FF9E01'}, {'country': 'Germany','visits': 1322,'color': '#FCD202'}, {'country': 'UK','visits': 1122,'color': '#F8FF01'}, {'country': 'France','visits': 1114,'color': '#B0DE09'}, {'country': 'India','visits': 984,'color': '#04D215'}, {'country': 'Spain','visits': 711,'color': '#0D8ECF'}, {'country': 'Netherlands','visits': 665,'color': '#0D52D1'}, {'country': 'Russia','visits': 580,'color': '#2A0CD0'}, {'country': 'South Korea','visits': 443,'color': '#8A0CCF'}, {'country': 'Canada','visits': 441,'color': '#CD0D74'}]"></div> <category-x-axis class="amchart" id="8ec0e029-4b0b-4f53-aa0d-6c25b4bfa991" style="position: absolute; left: 27px; top: 98px; width: 100%; height: 400px;" values="values"></category-x-axis>
Sorry about the lack of fluency in Angular - it is coming along...
-
Some attributes accept strings, others accept expressions which are then evaluated. When attributes accept strings you need to use
{{}}
interpolation, if they are expressions though you don't need the interpolation brackets.ng-init
takes an expression.Likewise we defined the
values
attribute of ourcategoryXAxis
component to take an expression so it doesn't need any interpolation, we just pass the variable name to it and the expression is evaluated to the value of the variable.What you want to probably do is pass an array of mango points through to the
value
attribute (e.g.value="designer.points"
) and change"valueField": "visits"
to"valueField": "value"
(points from a watch list have the.value
property) -
Thanks @Jared-Wiltshire, if
ng-init
doesn't take expressions, why cant I simply use"valueField": "myPoint.value"
?I dont use the watch-lists really... All I want to do is take
<ma-get-point-value point-xid="EnergyOutA_Panasonic" point="myPoint" ></ma-get-point-value>
and substitute it into the array for values. Surely there is an easy way to take a point-value, assign it a variable and just use that inside the array for values? -
@henryblu said in Bar Chart: Category as X Axis:
if ng-init doesn't take expressions, why cant I simply use "valueField": "myPoint.value"?
I'm not sure what exactly you are doing here,
valueField
is in the JavaScript I sent you, its not in the HTML template?If you are using this in the HTML template; you have enclosed the variable name in quotes, so it is evaluated as a string.
ng-init="varA = myPoint.value"
is not the same asng-init="varA = 'myPoint.value'"
@henryblu said in Bar Chart: Category as X Axis:
Surely there is an easy way to take a point-value, assign it a variable and just use that inside the array for values?
There surely is, just use
values="[{country: pointA.name, visits: pointA.value}, {country: pointB.name, visits: pointB.value}, {country: pointC.name, visits: pointC.value}]"
Or more simply, change the field names in the config options as I described above and just pass through an array of points e.g.
values="[pointA, pointB, pointC]"
-
Of course! Thank you very much - Resolved.