Trying to set custom colours for a chart - amcharts

I'm trying to add custom colours to a column chart, so each column has a different colour. I have the following code:
__this._chartColours = ['#2776BD', '#00A1D0','#00C195','#7ED321','#A8C600','#C9B600','#E3A600', '#F7941E', '#FC7149'];
__this._chart = am4core.create(__this._tileChartDiv[0], am4charts.XYChart);
if(result.chartDataMap != null)
{
var colorSet = new am4core.ColorSet();
var counter = 0;
$.each(result.chartDataMap, function(xAxis, yAxis)
{
__this._dataProvider.push({"category": xAxis, "column-1": yAxis});
__this._chart.colors.list.push(am4core.color(__this._chartColours[counter]));
});
__this._chart.data = __this._dataProvider;
__this._chart.padding(40, 40, 40, 40);
var categoryAxis = __this._chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.dataFields.category = "category";
categoryAxis.renderer.minGridDistance = 60;
categoryAxis.title.text = result.xAxisTitle;
var label = categoryAxis.renderer.labels.template;
label.wrap = true;
label.maxWidth = 120;
var valueAxis = __this._chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.title.text = result.yAxisTitle;
var series = __this._chart.series.push(new am4charts.ColumnSeries());
series.dataFields.categoryX = "category";
series.dataFields.valueY = "column-1";
series.tooltipText = "{valueY.value}"
series.columns.template.strokeOpacity = 0;
__this._chart.cursor = new am4charts.XYCursor();
}
It renders the chart fine based on the dataprovider I create but it is not setting the colors. I got the colours code from here: https://www.amcharts.com/docs/v4/concepts/colors/. The XY Chart and derivative charts section
I tried to use a theme, but that didn't work either:
function am4themes_myTheme(target)
{
if (target instanceof am4core.ColorSet)
{
$.each(__this._chartColours, function(index, item)
{
target.list.push(am4core.color(item));
});
}
}
am4core.useTheme(am4themes_myTheme);
It sets all the columns to the first colour. Then I tried adding a color property to the dataProvider for each column but again it sets them all to have the first colour.
I'm pretty much out of ideas.

There are a few issues here.
First, if you want the chart to use only your colors, instead of appending to the default chart's ColorSet, you have to manually override it by assigning an array of Colors to chart.colors.list (instead of pushing values to it).
Next, the column's color (fill) by default is based on its series. So even if you populate the chart's ColorSet, it's only each new series that will get a different color, not each column.
To set an individual column's color it would be something like:
column.fill = am4core.color("#2776BD");
To get each column to have its own color, we can set that upon the column's first instantiation, i.e. on its template's inited event. Further, a column's dataItem will have a property/reference to its index, so we can use that with ColorSet's getIndex method to assign colors in sequence.
So your final code might look something like this:
__this._chart.colors.list = [
am4core.color("#2776BD"),
am4core.color("#00A1D0"),
am4core.color("#00C195"),
am4core.color("#7ED321"),
am4core.color("#A8C600"),
am4core.color("#C9B600"),
am4core.color("#E3A600"),
am4core.color("#F7941E"),
am4core.color("#FC7149")
];
series.columns.template.events.once("inited", function(event){
event.target.fill = chart.colors.getIndex(event.target.dataItem.index);
});
Here's a fork of our Simple Column Chart demo with the above code and your custom colors:
https://codepen.io/team/amcharts/pen/2ef06f392b347412c61bcdcd3439a5c6

Related

How can AMCharts 4 be scrolled horizontally from javascript code?

I am trying to get the chart scrolled horizontally from externally triggered event calls like when a button is clicked elsewhere on the page. I am equally able to get the start and indexes of where i want the chart horizontally scrolled to. The chart's DateAxis is defined as below:
this.categoryAxis = chart.xAxes.push(new am4charts.DateAxis());
this.categoryAxis.dataFields.date = "value_date";
this.categoryAxis.cursorTooltipEnabled = false;
this.categoryAxis.start = 0.7;
this.categoryAxis.keepSelection = true;
And the function that is called by an external event is defined as below:
function(res){
let cpre = res.tree.split(',');
cpre.forEach((el, i) => {
cpre[i] = Number(el);
});
let visibleEl = this.data.findIndex((el, i) => {
return isEqual(cpre, el.graphIndex);
});
console.log(visibleEl);
//Trying to display 6 date points at any given time, not working.
let startIndex = visibleEl - 6 < 0 ? 0 : visibleEl - 6;
this.categoryAxis.zoomToIndexes(visibleEl, startIndex);
}
This line this.categoryAxis.zoomToIndexes(visibleEl, startIndex); currently zooms to the target indexes, but also zooms in the map which is not the desired effect. I only want to have it scroll to the desired position without zooming, thanks.
You can try to use valueAxis.strictMinMax (docs).
valueAxis.strictMinMax = true;
If that doesn't work try to use dateAxis.zoomToDates() instead of zoomToIndexes (docs).

How to create a stock event in amCharts v4?

Is it possible to indicate events along a series in amCharts v4 similar to the Stock Event in the v3 stock chart?
While I was brought on board specifically for v4 and am not familiar with v3, I'm confident you can simulate some of these features using Bullets.
A bullet is a Container (basically a placeholder parent for whatever visual object or additional Containers that you want), that will appear at every point of data. You can put a label there as well as a line and any other shape, e.g.:
var stockBullet = series.bullets.push(new am4charts.Bullet());
stockBullet.dy = -20;
var circle = stockBullet.createChild(am4core.Circle);
circle.stroke = "#000";
circle.strokeWidth = 1;
circle.radius = 10;
circle.fill = series.fill.brighten(-0.3);
circle.dy = -10;
var line = stockBullet.createChild(am4core.Line);
line.stroke = "#000";
line.strokeWidth = 1;
line.height = 20;
var label = stockBullet.createChild(am4core.Label);
label.fill = am4core.color("#000");
label.strokeWidth = 0;
label.dy = -20;
label.textAlign = "middle";
label.horizontalCenter = "middle"
Since we don't want a bullet to appear at every point of data, only at Stock Events, we can handle that once the bullets are ready on the chart by going through their data, disabling them if need be, otherwise providing text for our label (and maybe tooltipText if need be) (presume there is a property stockEvent in the data):
stockBullet.events.on("inited", function(event) {
if (event.target.dataItem && event.target.dataItem.dataContext && event.target.dataItem.dataContext.stockEvent) {
event.target.children.getIndex(2).text = event.target.dataItem.dataContext.stockEvent.text;
} else {
event.target.disabled = true;
}
});
Getting tooltips of different objects to play well with each other can be tricky depending on your chart, e.g. if it has Chart Cursor enabled there's a cursorTooltipEnabled property to prevent triggering a tooltip over bullets. To simplify things in this case what I did is make an invisible series per unique stock event bullet. For each stock event, use adapters to set its paired series' tooltipText to what's desired, and the base, visible series' tooltipText to "":
series.adapter.add("tooltipText", function(text, target) {
if (target.tooltipDataItem.dataContext.stockEvent) {
return "";
}
return text;
});
// ...
hiddenSeries.adapter.add("tooltipText", function(text, target) {
if (target.tooltipDataItem.dataContext.stockEvent) {
return target.tooltipDataItem.dataContext.stockEvent.description;
}
return "";
});
Here's a demo:
https://codepen.io/team/amcharts/pen/337984f18c6329ce904ef52a0c3eeaaa
Screenshot:

UPDATED: Javascript logic to fix in a small function (SVG, obtaining absolute coords)

NEW:
So here is the code at codepen:
http://codepen.io/cmer41k/pen/pRJNww/
Currently function UpdateCoords(draggable) - is commented out in the code.
What I wanted is to update on mouseup event the coordinates of the path (circle as path here) to the absolute ones and remove transform attribute.
But I am failing to do that;(( sorry only learning
OLD:
In my code I have an svg element (path) that gets dragged around the root svg obj (svg) via transform="translate(x,y)" property.
I wanted to update such path element's attribute "d" (the string that describes all coords) to use absolute coordinates and get rid of transformed\translate thing.
Basically:
was: d="M10,10 30,10 20,30" + transform="translate(20,0);
to be: d="M30,10 50,10 40,30" + transform="translate(0,0)" (or if we can delete the transform - even better)
So I did the code that does the thing for me, but there is a bug that prevents proper result.
I am sure I am doing something wrong in here:
var v = Object.keys(path.controlPoints).length
// controlPoints here is just a place in path object where I store the coords for the path.
var matrix = path.transform.baseVal.consolidate();
//I validated that the above line does give me proper transform matrix with proper x,y translated values. Now below I am trying to loop through and update all control points (coordinates) of the path
for (i=0; i<v; i++) {
var position = svg.createSVGPoint();
position.x = path.controlPoints["p"+i].x;
position.y = path.controlPoints["p"+i].y;
// so for each of path's control points I create intermediate svgpoint that can leverage matrix data (or so I think) to "convert" old coords into the new ones.
position = position.matrixTransform(matrix);
path.controlPoints["p"+i].x = position.x;
path.controlPoints["p"+i].y = position.y;
}
// I am sure I am doing something wrong here, maybe its because I am not "cleaning"/resetting this position thing in this loop or smth?
Sorry I am not a programmer, just learning stuff and the question is - in this code snipped provided the goal that I described - is something wrong with how I handle "position"?
Alright, the code snipped is now functioning properly!
So after I figured how to obtain properly the matrix I still had a weird displacement for any subsequent draggables.
I became clear that those displacements happen even before my function.
I debugged it a bit and realized that I was not clearing the ._x and ._y params that I use for dragging.
Now code works!
http://codepen.io/cmer41k/pen/XpbpQJ
var svgNS = "http://www.w3.org/2000/svg";
var draggable = null;
var canvas = {};
var inventory = {};
var elementToUpdate = {};
//debug
var focusedObj = {};
var focusedObj2 = {};
// to be deleted
window.onload = function() {
canvas = document.getElementById("canvas");
inventory = document.getElementById("inventory");
AddListeners();
}
function AddListeners() {
document.getElementById("svg").addEventListener("mousedown", Drag);
document.getElementById("svg").addEventListener("mousemove", Drag);
document.getElementById("svg").addEventListener("mouseup", Drag);
}
// Drag function //
function Drag(e) {
var t = e.target, id = t.id, et = e.type; m = MousePos(e); //MousePos to ensure we obtain proper mouse coordinates
if (!draggable && (et == "mousedown")) {
if (t.className.baseVal=="inventory") { //if its inventory class item, this should get cloned into draggable
copy = t.cloneNode(true);
copy.onmousedown = copy.onmouseup = copy.onmousemove = Drag;
copy.removeAttribute("id");
copy._x = 0;
copy._y = 0;
canvas.appendChild(copy);
draggable = copy;
dPoint = m;
}
else if (t.className.baseVal=="draggable") { //if its just draggable class - it can be dragged around
draggable = t;
dPoint = m;
}
}
// drag the spawned/copied draggable element now
if (draggable && (et == "mousemove")) {
draggable._x += m.x - dPoint.x;
draggable._y += m.y - dPoint.y;
dPoint = m;
draggable.setAttribute("transform", "translate(" +draggable._x+","+draggable._y+")");
}
// stop drag
if (draggable && (et == "mouseup")) {
draggable.className.baseVal="draggable";
UpdateCoords(draggable);
console.log(draggable);
draggable._x = 0;
draggable._y = 0;
draggable = null;
}
}

Xamarin Forms Teechart - show only graph and labels

I have a steema teechart named tChart1 consisting of candles. I want to show only the candles and the labels of the bottom axis. Everything else should be transparent. I have been able to hide some of the things, but the background is still white and there are vertical lines (I assume the grid) still showing.
tChart1.Title.Visible = false;
tChart1.Walls.Visible = false;
tChart1.Axes.Left.Visible = false;
tChart1.Legend.Visible = false;
You need to hide or make transparent the following elements:
tChart1.Axes.Left.Grid.Visible = false;
tChart1.Axes.Bottom.Grid.Visible = false;
tChart1.Panel.Transparent = true;
tChart1.Panel.Gradient.Visible = false;
tChart1.Walls.Visible = false;
tChart1.Legend.Transparent = true;
Bottom axis labels font color should be changed like this:
tChart1.Axes.Bottom.Labels.Font.Color = Color.Blue;
Custom labels can be set like this:
private void AddCustomLabels()
{
tChart1.Axes.Left.Labels.Items.Clear();
(tChart1.Axes.Left.Labels.Items.Add(123,"Hello")).Font.Size=16;
(tChart1.Axes.Left.Labels.Items.Add(466,"Good\n\rbye")).Transparent=false;
tChart1.Axes.Left.Labels.Items.Add(300);
AxisLabelItem a = tChart1.Axes.Left.Labels.Items.Add(-100);
a.Transparent=false;
a.Color=Color.Blue;
a.Transparency=50;
}
Candle series pen:
candle1.Pen.Color = Color.Lime;

how to create multiple slickgrids with a single function

I have a problem which I cannot seem to solve. I need to create a function which loops over an array of datasets and creates an independent slickgrids for each dataset. The catch is that the functions need to be bound to each grid independently. For example:
// this part works fine
for(var i=0; i<domain.length; i++){
dataView = new Slick.Data.DataView();
grid = new Slick.Grid('#' + domain[i].name, dataView, domain[i].columns, domain[i].options);
var data = domain[i].data;
// this works well and I am able to create several slickgrid tables
... etc ...
The problem is that every grid is now called "grid". Therefore, when I bind a function like this:
// controls the higlighting of the active row
grid.highlightActiveRow = function () {
var currentCell;
currentCell = this.getActiveCell();
I get a result which affects all grids (or in some cases only one grid).
How do I create multiple, independent grids with associated functions??? The problems seems to be that I have created one object "grid" and then assign all functions using the syntax grid.xxx - but I dont know how to create a unique object on each itteration.
Any help would be most appreciated.
PS: slickgrid is just amazing!!
Thanks
//****** UPDATE *********
#Jokob, #user700284
Thank you both for your help. Here is where I have manged to get to:
var dataView;
function buildTable() {
for(i = 0; i<domains.length; i++){
dataView = new Slick.Data.DataView();
var d = domains[i];
grid = new Slick.Grid('#' + d.name, dataView, d.columns, grids.options);
var data = d.data;
grid.init();
dataView.beginUpdate();
dataView.setItems(data);
// dataView.setFilter(filter); -- will be reinstated once i have this working
dataView.endUpdate();
arrOfGrids.push(grid);
};
};
Jakob - for now i am sticking to "for(i)" until I can wrap my head around your comment - which seems very sensible.
But, using the above, the grid data are not populating. I am not getting any js errors and the column headers are populating but not the data. The reference to d.data is definitely correct as I can see the data using the Chrome js debugger.
Any ideas? Many thanks for your help so far
Instead of assign all new grids to grid (in which case you overwrite the old one everytime you create a new one), push them to an array:
var arrayOfGrids = [];
for(var i=0; i<domain.length; i++) {
dataView = new Slick.Data.DataView();
arrayOfGrids.push(new Slick.Grid('#' + domain[i].name, dataView, domain[i].columns, domain[i].options));
// ....
Then, when you want to something with your grids, like adding the highlight-function, you loop over the array and do it for each element:
for ( var i=0; i<arrayOfGrids.length; i++ ) {
arrayOfGrids[i].highlightActiveRow = function () {
var currentCell;
currentCell = this.getActiveCell();
// ... etc...
BONUS
While we're at it, I would recommend that you use the forEach method that's available on the array-object when iterating over the arrays, rather than the for-loop. The unlike the loop, forEach creates a proper scope for your variables and it gets rid of the useless i-iteration variable:
var arrayOfGrids = [];
domain.forEach(function(d) {
dataView = new Slick.Data.DataView();
arrayOfGrids.push(new Slick.Grid('#' + d.name, dataView, d.columns, d.options));
// ....
And then the same for the other loop of course :)
You could try adding each of the grid instances to an array.You will be able to handle each of the grids differently if you want, by means of <arrray>[<array-index>]
var gridArr = [];
// this part works fine
for(var i=0; i<domain.length; i++){
dataView = new Slick.Data.DataView();
var grid = new Slick.Grid('#' + domain[i].name, dataView, domain[i].columns, domain[i].options);
var data = domain[i].data;
// this works well and I am able to create several slickgrid tables
... etc ...
gridArr.push(grid)
Then if you say gridArr[0] you can access the 1st grid,gridArr[1] second grid and so on.
Just in case anybody else is following this question - here is the working solution:
Many many thanks to #Jokob and #user700284
// default filter function
function filter(item) {
return true; // this is just a placeholder for now
}
var dataView;
function buildTables() {
for(i = 0; i<domains.length; i++){
dataView = new Slick.Data.DataView();
var d = domains[i];
grid = new Slick.Grid('#' + d.name, dataView, d.columns, options);
var data = d.data;
grid.init();
dataView.beginUpdate();
dataView.setItems(data);
dataView.setFilter(filter);
dataView.endUpdate();
grid.invalidate();
grid.render();
arrOfGrids.push(grid);
};
};

Resources