I have the following bar chart that I loop through an array to add in new datasets as follows:
for (var i = 0; i < this.Chart.length; i++) {
barChartData.datasets.push({
label: this.Chart[i].name,
backgroundColor: this.chart[i].color
data: [{
x: //set details
y: //set details
}],
});
}
this.data = {
labels: moment.months(),
datasets: barChartData.datasets
};
this.config = {
type: 'bar',
data: this.data,
options: {
scales: {
x: {
stacked: true,
grid: {
display: false
}
},
y: {
stacked: true,
grid: {
display: false
}
}
},
datasets: {
bar: {
barThickness: 30
}
}
},
responsive: true,
maintainAspectRatio: true,
};
Everything works as expected the only issue is with this line:
label: this.Chart[i].name,
where I loop through the name of the label, I have duplicates because one label name can have multiple data associated to it for different months, which is correct behavior.
This is how it looks:
Is there a way on the chart that I could remove the duplicate label just for display purposes so it looks as:
You could remove duplicates from your list using this function
function remove_duplicates_safe(arr) {
var seen = {};
var ret_arr = [];
for (var i = 0; i < arr.length; i++) {
if (!(arr[i] in seen)) {
ret_arr.push(arr[i]);
seen[arr[i]] = true;
}
}
return ret_arr;
}
You can then do something like this or make any logic you need
var listNoDuplicates = remove_duplicates_safe(this.Chart);
replace this line
for (var i = 0; i < this.Chart.length; i++) {
with this line
for (var i = 0; i < listNoDuplicates.length; i++) {
Related
In my code below, I am able to draw a Treemap and also display the tag in each tree cell. But the text is overflowing the tile if it's a long word
I need to ensure the word stays in the tile even If it means putting .... after certain characters. How can I achieve them? Please have a look at the version of chart.js and Treemap I am using before providing the solution. Thanks a lot :)
var topTags = [
{tag:'android',num:42657},{tag:'reactjs',num:38844},{tag:'php',num:34381},{tag:'sql',num:29996},
];
var canvas = document.getElementById("treemap");
var ctx = canvas.getContext("2d");
var chart = window.chart = new Chart(ctx, {
type: "treemap",
data: {
datasets: [{
tree: topTags,
key: "num",
groups: ['tag'],
spacing: 0.5,
borderWidth: 1.5,
fontColor: "black",
borderColor: "grey"
}]
},
options: {
maintainAspectRatio: false,
legend: { display: false },
tooltips: { enabled: false }
}
});
CHART.JS AND TREEMAP VERSION :
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-chart-treemap#0.2.3"></script>
I solved it by splitting the text into multiple lines, starting a new line whenever the max width would be exceeded.
const chart = new Chart(context, {
type: 'treemap',
data: {
datasets: [
{
/* ... */
labels: {
display: true,
formatter(ctx: TreemapScriptableContext) {
if (ctx.type !== 'data') {
return;
}
return splitLabelToFit(ctx.raw["_data"].label, ctx.raw.w*0.9, ctx);
}
}
}
],
},
});
function splitLabelToFit(label: string, maxWidth: number, ctx: TreemapScriptableContext) {
const words = label.split(' ');
const lines = [];
let currentLine = '';
for (let i = 0; i < words.length; i++) {
const word = words[i];
const newLine = currentLine + ' ' + word;
const width = ctx.chart.ctx.measureText(newLine).width;
if (width < maxWidth) {
currentLine = newLine;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
return lines;
}
I am working on chart.js and I have data coming from JSON via ajax. See the example below:
[{"timestamp":"06:00:00.000000","true_count":2},{"timestamp":"07:00:00.000000","true_count":5},{"timestamp":"08:00:00.000000","true_count":7},{"timestamp":"09:00:00.000000","true_count":8},{"timestamp":"10:00:00.000000","true_count":12},{"timestamp":"11:00:00.000000","true_count":15},{"timestamp":"12:00:00.000000","true_count":20},{"timestamp":"13:00:00.000000","true_count":17},{"timestamp":"14:00:00.000000","true_count":14},{"timestamp":"16:00:00.000000","true_count":11},{"timestamp":"17:00:00.000000","true_count":19},{"timestamp":"18:00:00.000000","true_count":22},{"timestamp":"19:00:00.000000","true_count":16},{"timestamp":"20:00:00.000000","true_count":14},{"timestamp":"22:00:00.000000","true_count":7}]
The JS code i am using for my chart is below:
// create initial empty chart
var ctx_live = document.getElementById("chLine");
var myChart = new Chart(ctx_live, {
type: 'bar',
data: {
labels: [],
datasets: [{
data: [],
borderWidth: 1,
borderColor:'#00c0ef',
label: 'liveCount',
}]
},
options: {
responsive: true,
title: {
display: true,
text: "Count Per Hour",
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
}
}
});
// logic to get new data
var getData = function() {
var _data =[];
var _labels = [];
$.ajax({
url: 'chart_data',
type: "get",
success: function(data) {
full_data = JSON.parse(data);
full_data.forEach(function(key,index) {
_data.push(key.true_count);
_labels.push(key.hour);
});
myChart.data.labels = _labels;
myChart.data.datasets[0].data = _data;
myChart.update();
}
});
};
// get new data every 3 seconds
setInterval(getData, 3000);
Now, this is working fine and shows the true_count over time which is a one-hour basis. Now, the chart is showing only hours with count but what I would like to do is to set the static hours from 12 AM to 11 PM, and for hours for which I don't have data the true_count will be zero, and for those that I have data for, the true count will be assigned to that hour and show on the chart.
Any ideas on how do I do that?
Here is an example:
// create initial empty chart
var ctx_live = document.getElementById("chLine");
var myChart = new Chart(ctx_live, {
type: 'bar',
data: {
labels: [],
datasets: [{
data: [],
borderWidth: 1,
borderColor: '#00c0ef',
label: 'liveCount',
}]
},
options: {
responsive: true,
title: {
display: true,
text: "Count Per Hour",
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
}
}
});
// Some constants to be changed later:
const HOUR_TO_START = 0;
const HOUR_TO_END = 23;
// helper:
const intToAmPm = (i) =>
i==0 ? '12 AM' :
i==12 ? '12 PM' :
i < 12 ? i + ' AM' :
(i-12) + ' PM';
// logic to get new data
var getData = function() {
var _data = [];
var _labels = [];
$ajax({
url: 'chart_data',
type: "get",
success: function(data) {
full_data = JSON.parse(data);
let preparedData = {};
full_data.forEach(function(key, index) {
let hour = parseInt(String(key.timestamp).substring(0, 2));
preparedData[hour] = key.true_count;
});
for (let i = HOUR_TO_START; i <= HOUR_TO_END; i++) {
_data.push(preparedData[i] === undefined ? 0 : preparedData[i]);
_labels.push(intToAmPm(i));
}
myChart.data.labels = _labels;
myChart.data.datasets[0].data = _data;
myChart.update();
}
});
};
// get new data every 3 seconds
//setInterval(getData, 3000);
getData();
// THIS IS FOR TESTING. IMITATE BACKEND
function $ajax(param) {
param.success('[{"timestamp":"06:00:00.000000","true_count":2},{"timestamp":"07:00:00.000000","true_count":5},{"timestamp":"08:00:00.000000","true_count":7},{"timestamp":"09:00:00.000000","true_count":8},{"timestamp":"10:00:00.000000","true_count":12},{"timestamp":"11:00:00.000000","true_count":15},{"timestamp":"12:00:00.000000","true_count":20},{"timestamp":"13:00:00.000000","true_count":17},{"timestamp":"14:00:00.000000","true_count":14},{"timestamp":"16:00:00.000000","true_count":11},{"timestamp":"17:00:00.000000","true_count":19},{"timestamp":"18:00:00.000000","true_count":22},{"timestamp":"19:00:00.000000","true_count":16},{"timestamp":"20:00:00.000000","true_count":14},{"timestamp":"22:00:00.000000","true_count":7}]');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chLine"></canvas>
I am having 3 different charts on my web page for which I am using Chartjs. The problem occurs when I hover over one of the charts it starts showing old data. I am creating chart on a HTML button click. I checked few answers on stackoverflow (for eg. destroy()) but that is not working for me. Below is the function for chart. please guide me regarding this.
<script>
function dailyPrd1() {
var pl_type1 = "";
var pl_sl1 = "";
var date1="";
pl_type1 = plant_type1.options[plant_type1.selectedIndex].innerHTML;
//alert(pl_type1);
pl_sl1 = plant_select1.options[plant_select1.selectedIndex].innerHTML;
//alert(pl_sl1);
date1 = document.getElementById('date2').value;
//alert(date1);
var pl2 = "";
pl2 = pl_type1 + '-' + pl_sl1;
var obj2 = "";
var hrs1 = [];
var prod1 = [];
var colr1 = [];
var req2 = new XMLHttpRequest();
var config_string2 = '<%=ConfigurationManager.AppSettings["serverip11"].ToString() %>' + pl_sl1 + "/" + pl_type1 + "/" + date1;
req2.open("GET", config_string2, true);
req2.send();
req2.overrideMimeType("application/json");
req2.onload = function () {
obj2 = JSON.parse(this.response);
obj2 = JSON.parse(obj2);
var len12 = 0;
len12 = obj2.day_list.length;
for (i = 0; i < len12; i++) {
hrs1.push(obj2.day_list[i].day);
}
var speedCanvas2 = document.getElementById("myChart3");
Chart.defaults.global.defaultFontFamily = "Lato";
Chart.defaults.global.defaultFontSize = 16;
var chartOptions2 = {
responsive: true,
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Days'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Value in cu.m'
}
}]
},
legend: {
display: true,
position: 'top',
labels: {
boxWidth: 80,
fontColor: 'black'
}
}
};
var speedData2 = {
labels: hrs1,
// datasets: [dataFirst, dataSecond]
};
var lineChart2 = new Chart(speedCanvas2, {
type: 'bar',
data: speedData2,
options: chartOptions2
});
var iti1 = 0;
iti1 = obj2.prod_qty.length;
var aaa = 'Pl 1'
for (j = 0; j < iti1; j++) {
prod1.push(obj2.prod_qty[j].tot_prod);
}
addData(lineChart2, pl2, getRandomColor(), prod1);
}
}
</script>
After you change your data you should update your chart with chartVariable.update().
I made a JSBin which explains you how to use it.
The important function for you is the last in the code, addDataButton() which gets triggered by a button click. In this function I add new data and update my chart after that.
Instead of chartVariable and chart you should use lineChart2 in your case.
Complete code:
let numberOfDataCounter = 0 // Current data counter
const numberOfDataAtBeginning = 4 // data number to start with
const weekdays = ["Su", "Mo", "Du", "We", "Th", "Fr", "Sa"]
function randomNumber(){
let randomNumber = Math.floor(Math.random()*100)
return randomNumber
}
let chartData = {
label: [],
data: []
}
function addData (){
chartData.label.push(weekdays[numberOfDataCounter % 7])
chartData.data.push(randomNumber())
numberOfDataCounter++
}
// Fill chart with data at beginning
while (numberOfDataAtBeginning>numberOfDataCounter) {
addData()
}
let data = {
labels: chartData.label,
datasets: [{
label: "Label",
data: chartData.data
}]
}
let chart = new Chart(document.getElementById("chart"), {
type: 'line',
data: data,
options: {
scales: {
yAxes: [{
ticks: {
min: 0,
max: 100
}
}]
}
}
});
function addDataButton(){
addData()
chart.update()
}
I am trying to pass in my_series to series in highcharts to graph stacked bars. From my php, I get hash, which is like this:
{"Oat":{"10":"AA","11":"H"},"Brown Rice":{"10":"AA","11":"BB"}}
I actually dont know what is wrong with my code. I think it should work, but when I run it, I do not see anything on the screen. I checked that sample_name has "Oat" and "Brown Rice", which is what I want.
What I eventually want is a color-coded stacked bar graph that has samples on the yAxis and position on the xAxis. I think I almost got to what I want, it's just a problem with making an actual graph.
Thank you!
$(function() {
var allele_str = [];
var sample_name = [];
var hash = {};
var my_series = {
data: []
};
var position = [];
var options = {
chart: {
renderTo: 'container',
defaultSeriesType: 'bar',
zoomType: 'xy',
inverted: true
},
title: {
text: 'Gene'
},
xAxis: {
categories: [],
},
legend: {
enabled: false
},
yAxis: {
title: {
text: 'Position'
},
labels: {
enabled: false
}
}
plotOptions: {
series: {
stacking: 'normal'
}
}
series: []
};
$.getJSON('6K_load.php', function(data) {
sample_name = Object.keys(data);
options.xAxis.categories.push(sample_name);
for (var x in sample_name) { // sample Oat, BR
for (var k in data[sample_name[x]]) { // pos
series.name = k;
var z = data[sample_name[x]][k];
hash[z] = 1;
allele_str.y = hash[z];
if (z === 'AA') {
allele_str.color = 'grey';
}
if (z === 'BB') {
allele_str.color = 'blue';
}
if (z === '--') {
allele_str.color = 'white';
}
if (z === 'H') {
allele_str.color = 'red';
}
my_series.data.push(allele_str);
}
options.series.push(my_series);
}
var chart = new Highcharts.Chart(options);
});
});
The problem is that you not parse your data to number value, you need to use parseFloat() in this line
my_series.data.push(parseFloat(allele_str));
I am new to Highcharts, Sharepoint and JS. What I need to do is make each bar link to a SharePoint view
This code gets the data
IWSChartBuilder.EngagementsSegmentChart = function () {
var load = function () {
var year = new Date().getFullYear();
//Variable to hold counts
var countArray = [];
$.when(
//Consulting Engagements List
IWSChartBuilder.RESTQuery.execute("valid REST query")
).done(
function (engagements1) {
var dataArray = [];
var countArray = [];
//Get data from Consulting Engagements list
var results = engagements1.d.results;
for (var i = 0; i < results.length; i++) {
for (var i = 0; i < results.length; i++) {
dataArray.push(results[i].Segment);
}
}
var baseUrl = "valid url";
countArray = IWSChartBuilder.Utilities.buildCategoryCountsWithLink(countArray, dataArray, baseUrl);
//Put data into format for stacked bar chart
var seriesData = [];
var xCategories = [];
var links = [];
for (var i = 0; i < countArray.length; i++) {
xCategories.push(countArray[i].name);
seriesData.push(countArray[i].y);
links.push(countArray[i].url);
}
//Build Chart
IWSChartBuilder.Utilities.loadColumnChartWithLink(links, xCategories, seriesData, "#engagementSegmentChart", "Engagements by Segment", "Total Projects");
}
).fail(
function (engagements1) {
$("#engagementSegmentChart").html("<strong>An error has occurred.</strong>");
}
);
};
return {
load: load
}
}();
//code to display chart
loadColumnChartWithLink = function (xCategories, seriesData, divId, chartTitle, yAxisTitle) {
//Build Column Chart
$(divId).highcharts({
chart: {
type: 'column'
},
credits: {
enabled: false
},
title: {
text: chartTitle
},
xAxis: {
categories: xCategories,
allowDecimals: false,
labels: {
rotation: -45,
align: 'right'
}
},
yAxis: {
min: 0,
allowDecimals: false,
title: {
text: yAxisTitle
}
},
legend: {
enabled: false
},
plotOptions: {
bar: {
dataLabels: {
enabled: false
}
},
series: {
cursor: 'pointer',
point: {
events: {
click: function() {
location.href = this.options.url;
}
}
}
}
},
series: [{
name: yAxisTitle,
data: seriesData
}]
});
},
Any help is greatly appreciated
Mark
You need to adapt your data to create objects as points, like in the example:
{y:10,url:'http://google.com'}
and then catch click event on serie's point.
http://jsfiddle.net/2tL5T/