Related
I am creating a high charts graph that I would like to dynamically give the graph color to depending on the title of an object. I currently have an array graphData that has an object title.
I have 5 possible results of titles:
"LOW", "MEDIUM-LOW", "MEDIUM", "MEDIUM-HIGH", AND "HIGH"
I am now attempting to iterate through my array and assign a color depending on what title the index has.
My entire graph receives one color based off the last title of the array. I would like the color to effect each index of the array seperartely.
For example: if "MEDIUM-HIGH" is the last title in the array, my entire graph gets #DD5F0C
Here is my code:
Array:
graphData: [ […]
0: Object { title: "LOW", result: 62582 }
1: Object { title: "MEDIUM-LOW", result: 57758 }
2: Object { title: "LOW", result: 8795 }
3: Object { title: "HIGH", result: 262525 }
4: Object { title: "MEDIUM-HIGH", result: 167168 } ]
let graphColor = ""
for (i = 0; i < graphData.length; i++) {
if (graphData[i].title === "LOW") {
graphColor = "#0D6302"
} else if (graphData[i].title === "MEDIUM-LOW") {
graphColor = "#0B7070"
} else if (graphData[i].title === "MEDIUM") {
graphColor = "#DC9603"
} else if (graphData[i].title === "MEDIUM-HIGH") {
graphColor = "#DD5F0C"
} else if (graphData[i].title === "HIGH") {
graphColor = "#C50710"
}
}
HighCharts code :
Highcharts.chart('container', {
chart: {
type: 'bar'
},
title: {
text: "Bar Graph"
},
xAxis: {
},
yAxis: {
min: 0,
formatter: function() {
return this.value + "%";
},
title: {
text: '% of Total'
}
},
legend: {
reversed: false
},
plotOptions: {
series: {
stacking: 'normal'
}
},
series: [{
name: `graphData[0].title`,
color: graphColor,
data: [graphData[0]],
}, {
name: 'graphData[1].title',
color: graphColor,
data: [graphData[1]],
showInLegend: false,
linkedTo: ":previous"
}, {
name: 'graphData[2].title,
color: graphData[0].title,
data: [graphData[2]]
}, {
name: graphData[3].title,
color: '#DC9603',
data: [graphData[3]]
}, {
name: graphData[4].title,
color: graphColor,
data: [graphData[4]]
}, {
name: graphData[5].title,
color: graphColor,
data: [graphData[5]]
}]
});
I am expecting my "color" to be dynamically generated based off of what graphData.title equals for that specific index.
You are having trouble because you have graphData.length number of entries, but only one graphColor variable to hold the color. Your code samples don't look complete so I'll make some assumptions about how the surrounding code must be. I recommend building up your series data in the for-loop directly so you can just use it in the Highcharts.chart call. The code is easier to read that way and probably more flexible too if you need to have more data rows.
// build the series data array here so it's simple to use in the chart call
const series = new Array(graphData.length);
for (let i = 0; i < graphData.length; i++) {
let graphColor = "#000000"; // a default color just in case
// can use if/else or a switch here
if (graphData[i].title === "LOW") {
graphColor = "#0D6302";
} else if (graphData[i].title === "MEDIUM-LOW") {
graphColor = "#0B7070";
} else if (graphData[i].title === "MEDIUM") {
graphColor = "#DC9603";
} else if (graphData[i].title === "MEDIUM-HIGH") {
graphColor = "#DD5F0C";
} else if (graphData[i].title === "HIGH") {
graphColor = "#C50710";
}
series[i] = {
name: graphData[i].title,
color: graphColor,
data: [graphData[i].result]
};
}
// Adjust the series data as needed
series[1].showInLegend = false;
series[1].linkedTo = ":previous";
Highcharts.chart("container", {
chart: { type: "bar" },
title: { text: "Bar Graph" },
xAxis: {},
yAxis: {
min: 0,
formatter: function() {
return this.value + "%";
},
title: { text: "% of Total" }
},
legend: { reversed: false },
plotOptions: { series: { stacking: "normal" } },
series: series
});
Not sure if I've properly understood what are you trying to do, but try this way:
const colorMap = { "LOW":"#0D6302",
"MEDIUM-LOW": "#0B7070",
"MEDIUM": "#DC9603",
"MEDIUM-HIGH": "#DD5F0C",
"HIGH":"#C50710"
}
...
series: [{
name: `graphData[0].title`,
color: colorMap[graphData[0].title],
data: [graphData[0]],
}, {
In the Highchart way - you can iterate through the series after chart initialization and set the wanted colors by particular series.
Demo: https://jsfiddle.net/BlackLabel/6hm4ebna/
chart: {
type: 'bar',
events: {
load() {
let chart = this;
chart.series.forEach(s => {
console.log(s)
if (s.name === 'test1') {
s.update({
color: 'red'
})
}
else if (s.name === 'test3') {
s.update({
color: 'green'
})
}
})
}
}
},
API: https://api.highcharts.com/highcharts/chart.events.load
If this wouldn't help please reproduce your attempt with the sample data on the online editor which I could work on.
Got a column called ChartData in database with a string value of 4,11,25,36,50. Trying to assign this value to a hidden variable so JS can read the value of this and put this value in the data option using high charts. I have console.log the variable and looks like its appearing as a string rather than an array when being parsed across the server side to the client side.
C# code
string str = reader["ChartData"].ToString();
string[] strList = str.Split(','); //seperate the hobbies by comma
// convert it in json
dataStr = JsonConvert.SerializeObject(strList, Formatting.None);
hiddenvariable.Value = dataStr;
JS code:
function CreateBoxPlot() {
var hv = $('#hiddenvariable').val();
alert(hv); //["40","61","65","74","77"]
var chart;
var titleText = 'Test Chart Title';
var subTitleText = 'Test Chart Subtitle';
var type = 'boxplot';
var data = hv;
console.log(data); //["40","61","65","74","77"]
$(function () {
$('#container').highcharts({
chart: { type: type, inverted: true },
title: { text: titleText },
subtitle: { text: subTitleText },
legend: { enabled: false },
tooltip: {
shared: true,
crosshairs: true
},
plotOptions: {
series: {
pointWidth: 50
}
},
xAxis: {
visible: false
},
yAxis: {
visible: true,
title: {
text: 'Values'
},
plotLines: [{
value: 80,
color: 'red',
width: 2
}]
}
});
chart = $('#container').highcharts();
chart.addSeries({ data: data });
});
}
However when i hardcode data to the below value this works. How do i format this correctly when its parsed over to the JS side:
var data = [[40,61,65,74,77]]
You have to convert the string '["40","61","65","74","77"]' to js array with numbers. To make it work on each browser you can follow this approach:
Parse the string to js array using JSON.parse()
Loop through the created array and convert each element to number:
var json = '["40","61","65","74","77"]',
dataString = JSON.parse(json),
data = [],
i;
for (i = 0; i < dataString.length; i++) {
data[i] = +dataString[i];
}
Code:
$(function() {
var json = '["40","61","65","74","77"]',
dataString = JSON.parse(json),
data = [],
i;
for (i = 0; i < dataString.length; i++) {
data[i] = +dataString[i];
}
$('#container').highcharts({
chart: {
inverted: true
},
legend: {
enabled: false
},
tooltip: {
shared: true,
crosshairs: true
},
plotOptions: {
series: {
pointWidth: 50
}
},
xAxis: {
visible: false
},
yAxis: {
visible: true,
title: {
text: 'Values'
},
plotLines: [{
value: 80,
color: 'red',
width: 2
}]
}
});
chart = $('#container').highcharts();
chart.addSeries({
data: data
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container"></div>
Demo:
https://jsfiddle.net/BlackLabel/ay1xmgoc/
Taking reference from the comments, add this to your code and then try.
var data = hv.map(function (element) {
return +element;
});
What would be the best approach to extend the show() and hide() functions in Highcharts?
I want to add a boolean-like show(effectLinkedSeries) and hide(effectLinkedSeries), so I can control when a linkedSeries gets removed or added along with its "parent". Here is a demo of linkedSeries functionality. A property called "linkedTo" sets up the functionality:
series: [{
name: 'Temperature',
data: averages,
zIndex: 1,
marker: {
fillColor: 'white',
lineWidth: 2,
lineColor: Highcharts.getOptions().colors[0]
}
}, {
name: 'Range',
data: ranges,
type: 'arearange',
lineWidth: 0,
linkedTo: ':previous',
color: Highcharts.getOptions().colors[0],
fillOpacity: 0.3,
zIndex: 0
}]
I could modify the source directly but I'd rather try to extend the library, instead of hacking it.
Figured out I can make use of the Highcharts.Series.prototype to accomplish this
Highcharts.Series.prototype.setVisible = function (vis, redraw, effectLinkedSeries) {
var series = this,
chart = series.chart,
legendItem = series.legendItem,
showOrHide,
ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
oldVisibility = series.visible;
// if called without an argument, toggle visibility
series.visible = vis = series.userOptions.visible = vis === Highcharts.UNDEFINED ? !oldVisibility : vis;
showOrHide = vis ? 'show' : 'hide';
// show or hide elements
Highcharts.each(['group', 'dataLabelsGroup', 'markerGroup', 'tracker'], function (key) {
if (series[key]) {
series[key][showOrHide]();
}
});
// hide tooltip (#1361)
if (chart.hoverSeries === series || (chart.hoverPoint && chart.hoverPoint.series) === series) {
series.onMouseOut();
}
if (legendItem) {
chart.legend.colorizeItem(series, vis);
}
// rescale or adapt to resized chart
series.isDirty = true;
// in a stack, all other series are affected
if (series.options.stacking) {
Highcharts.each(chart.series, function (otherSeries) {
if (otherSeries.options.stacking && otherSeries.visible) {
otherSeries.isDirty = true;
}
});
}
// show or hide linked series
Highcharts.each(series.linkedSeries, function (otherSeries) {
if (effectLinkedSeries === true) {
otherSeries.setVisible(vis, false);
}
});
if (ignoreHiddenSeries) {
chart.isDirtyBox = true;
}
if (redraw !== false) {
chart.redraw();
}
Highcharts.fireEvent(series, showOrHide);
}; // end Highcharts.Series.prototype.setVisible
Highcharts.Series.prototype.show = function (effectLinkedSeries) {
this.setVisible(true,undefined,effectLinkedSeries);
};
Highcharts.Series.prototype.hide = function (effectLinkedSeries) {
this.setVisible(false,undefined,effectLinkedSeries);
};
Im building a chart system that will show me all data entries. I retrieve my data using ajax and I loop trough the data and group the results by colors (red, blue and yellow) and then divide them by months.
I setup base objects (dateCounts_Red, dateCounts_Blue and dateCounts_Yellow) so that by default it starts all months at 0. A counter would then add when it finds a match tot he apropriate color and month.
When I output my dateCounts I get:
{"2015":{"2015-12":1,"2015-10":null,"2015-08":null,"2015-11":null}}
{"2015":{"2015-12":0,"2015-10":null}}
{"2015":{"2015-12":0,"2015-10":null}}
Here is the code I have so far:
var dateCounts_Red = {"2015":{"2015-01":0,"2015-02":0,"2015-03":0,"2015-04":0},"2015":{"2015-05":0},"2015":{"2015-06":0},"2015":{"2015-07":0},"2015":{"2015-08":0},"2015":{"2015-09":0},"2015":{"2015-10":0},"2015":{"2015-11":0},"2015":{"2015-12":0}};
var dateCounts_Blue = {"2015":{"2015-01":0,"2015-02":0,"2015-03":0,"2015-04":0},"2015":{"2015-05":0},"2015":{"2015-06":0},"2015":{"2015-07":0},"2015":{"2015-08":0},"2015":{"2015-09":0},"2015":{"2015-10":0},"2015":{"2015-11":0},"2015":{"2015-12":0}};
var dateCounts_Yellow = {"2015":{"2015-01":0,"2015-02":0,"2015-03":0,"2015-04":0},"2015":{"2015-05":0},"2015":{"2015-06":0},"2015":{"2015-07":0},"2015":{"2015-08":0},"2015":{"2015-09":0},"2015":{"2015-10":0},"2015":{"2015-11":0},"2015":{"2015-12":0}};
data.d.results.forEach(function(element) {
var date = element.created_date.slice(0, 7);
var yr = date.slice(0, 4);
var Color = element.colorvalue;
if(Color == "red") {
dateCounts_Red[yr][date]++;
}
if(Color == "blue"){
dateCounts_Blue[yr][date]++;
}
if(Color == "yellow"){
dateCounts_Yellow[yr][date]++;
}
});
Red_yr_2015_data = [dateCounts_Red['2015']['2015-01'], dateCounts_Red['2015']['2015-02'], dateCounts_Red['2015']['2015-03'], dateCounts_Red['2015']['2015-04'], dateCounts_Red['2015']['2015-05'], dateCounts_Red['2015']['2015-06'], dateCounts_Red['2015']['2015-07'], dateCounts_Red['2015']['2015-08'], dateCounts_Red['2015']['2015-09'], dateCounts_Red['2015']['2015-10'], dateCounts_Red['2015']['2015-11'], dateCounts_Red['2015']['2015-12']];
Blue_yr_2015_data = [dateCounts_Blue['2015']['2015-01'], dateCounts_Blue['2015']['2015-02'], dateCounts_Blue['2015']['2015-03'], dateCounts_Blue['2015']['2015-04'], dateCounts_Blue['2015']['2015-05'], dateCounts_Blue['2015']['2015-06'], dateCounts_Blue['2015']['2015-07'], dateCounts_Blue['2015']['2015-08'], dateCounts_Blue['2015']['2015-09'], dateCounts_Blue['2015']['2015-10'], dateCounts_Blue['2015']['2015-11'], dateCounts_Blue['2015']['2015-12']];
Yellow_yr_2015_data = [dateCounts_Yellow['2015']['2015-01'], dateCounts_Yellow['2015']['2015-02'], dateCounts_Yellow['2015']['2015-03'], dateCounts_Yellow['2015']['2015-04'], dateCounts_Yellow['2015']['2015-05'], dateCounts_Yellow['2015']['2015-06'], dateCounts_Yellow['2015']['2015-07'], dateCounts_Yellow['2015']['2015-08'], dateCounts_Yellow['2015']['2015-09'], dateCounts_Yellow['2015']['2015-10'], dateCounts_Yellow['2015']['2015-11'], dateCounts_Yellow['2015']['2015-12']];
Im currently getting the following error from my Highcharts js:
Uncaught TypeError: Cannot set property 'index' of undefined
THis is preventing the chart system to work correctly the data returned is not being returned with it's expected data.
Here a full example to the issue https://jsfiddle.net/awo5aaqb/21/
Would anyone know what im missing?
Your date count objects have major structural flaw.
When you prettify them they look like:
var dateCounts_Blue = {
"2015": {
"2015-01": 0,
"2015-02": 0,
"2015-03": 0,
"2015-04": 0
},
"2015": {
"2015-05": 0
},
"2015": {
"2015-06": 0
},
"2015": {
"2015-07": 0
},
......
Object keys must be unique so these are clearly being repeated and the compiler will over write duplicates.
Fix the pattern that breaks away from the intended pattern grouping at the beginning
var dateCounts_Red = {
"2015":
{
"2015-01":0,
"2015-02":0,
"2015-03":0,
"2015-04":0,
"2015-05":0,
"2015-06":0,
"2015-07":0,
"2015-08":0,
"2015-09":0,
"2015-10":0,
"2015-11":0,
"2015-12":0
},
};
var dateCounts_Blue = {
"2015":{
"2015-01":0,
"2015-02":0,
"2015-03":0,
"2015-04":0,
"2015-05":0,
"2015-06":0,
"2015-07":0,
"2015-08":0,
"2015-09":0,
"2015-10":0,
"2015-11":0,
"2015-12":0
}
};
var dateCounts_Yellow = {
"2015":{
"2015-01":0,
"2015-02":0,
"2015-03":0,
"2015-04":0,
"2015-05":0,
"2015-06":0,
"2015-07":0,
"2015-08":0,
"2015-09":0,
"2015-10":0,
"2015-11":0,
"2015-12":0}
};
Your data structure is flawed and such comparing values when doing the foreach loop becomes inconsistent because it compares it to multiple values, the above JSON is the fix for your problem.
Not quite codereview.stackexchange.com, but I heavily modified your javascript to make it work a bit better
$.ajax({
url: basePath,
dataType: 'json',
cache: false,
success: function(data) {
var counts = {};
data.d.results.forEach(function(element) {
// If you know it's all the same year, you could totally ignore this
var yr = element.created_date.slice(0, 4);
var month = parseInt(element.created_date.slice(5,7));
var color = element.colorvalue;
if (counts[color] === undefined) {
counts[color] = {};
}
if (counts[color][yr] === undefined) {
counts[color][yr] = {};
}
current_value = counts[color][yr][month];
if (current_value === undefined) {
// Doesnt exist yet, so add it
counts[color][yr][month] = 1;
} else {
// Exists, so increment by 1
counts[color][yr][month] = current_value + 1;
}
});
console.log(JSON.stringify(counts));
console.log(transform_series(counts['red']['2015']));
console.log(transform_series(counts['blue']['2015']));
console.log(transform_series(counts['yellow']['2015']));
var Options = {
chart: {
renderTo: 'myfirstchart',
type: 'column',
margin: 75,
options3d: {
enabled: true,
alpha: 25,
beta: 0,
depth: 70
}
},
title: {
text: "Test Highcharts"
},
subtitle: {
text: 'Test charts'
},
plotOptions: {
column: {
depth: 25
}
},
xAxis: {
categories: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"]
},
yAxis: {
title: {
text: "Number of entries"
}
},
tooltip: {
headerFormat: '<b>{point.key}</b><br>',
pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: {point.y} / {point.stackTotal}'
},
plotOptions: {
column: {
stacking: 'normal',
depth: 40
}
},
series: [{
name: 'Red',
color: 'red',
data: transform_series(counts['red']['2015']),
stack: '2015'
}, {
name: 'Blue',
color: 'blue',
data: transform_series(counts['blue']['2015']),
stack: '2015'
}, {
name: 'Yellow',
color: 'yellow',
data: transform_series(counts['yellow']['2015']),
stack: '2015'
}]
};
return new Highcharts.Chart(Options);
}
});
// this transforms the hash {10: 5, 11:1, 12:1} to get you all 12 months
// and returns an array of values [ 0, 0, 0, 0, 0 ... 5, 1, 1] that
// can be used in high charts
function transform_series(series) {
return Array.apply(null, Array(13)).map(function (_, i) {return (series[i] === undefined) ? 0 : series[i];}).slice(1,13);
}
I'm using Highcharts.visualize to draw the graph from a table containing the data.
You can test my working code here: http://jsfiddle.net/S2XM8/1/
I have two questions:
I want to have a separate styling for my "Additional value". How do I go about it?
Can I add data for the X-axis via the javascript? For example if I need to fill in the gap between 2014-05-27 and 2014-05-25 in the table.
Highcharts.visualize = function (table, options, tableClass) {
// the categories
options.xAxis.categories = [];
$('tbody th', table).each( function () {
options.xAxis.categories.push(this.innerHTML);
});
// the data series
options.series = [];
$('tr', table).each( function (i) {
var tr = this;
$('.graph', tr).each( function (j) {
if (i === 0) { // get the name and init the series
options.series[j] = {
name: this.innerHTML,
data: []
};
} else { // add values
options.series[j].data.push(parseFloat(this.innerHTML));
console.log(this.innerHTML);
}
});
});
options.title = { text: 'Some graph' };
$('#' + tableClass + '-graph').highcharts(options);
};
var tableNumber = document.getElementById('rank-table'),
options = {
chart: {
zoomType: 'x'
},
xAxis: {
tickInterval: 30,
reversed: true,
labels: {
rotation: 45
},
type: 'datetime',
dateTimeLabelFormats: { // don't display the dummy year
month: '%e. %b',
year: '%b'
}
},
yAxis: {
title: {
text: 'Rank'
},
min: 1,
reversed: true
},
legend: {
layout: 'vertical',
align: 'middle',
verticalAlign: 'bottom',
borderWidth: 0
}
};
Highcharts.visualize(tableNumber, options, 'number');
Both things are possible, but require to modify visualize method, see: http://jsfiddle.net/S2XM8/4/
You can set series options in a chart and then merge with data:
series: [{
// nothing special
}, {
type: 'column' // set series type for example
}]
And merging:
options.series[j] = options.series[j] || {};
options.series[j].name = this.innerHTML,
options.series[j].data = [];
Check parsed value before passing as point value:
var value = parseFloat(this.innerHTML);
if(isNaN(value)) { //null value - produces NaN when parsing
options.series[j].data.push(10);
} else {
options.series[j].data.push(value); // push value to the series
}