Related
I am currently learning to use Electron for a university project. What I am trying to do is to read data from a .xlsx file, then create a Chart with chart.js and display the data. For that, I am using exceljs and chart.js. For this, I wrote multiple functions (Code below). My problem right now is, that I try to return an Array with some data (It gets created perfectly fine) but it just displays as undefined in the other function.
I know, my code is not good. It's complete spaghetti, honestly. I just need to get this working, it doesn't have to be nice code.
This is the function I use to draw the chart:
function createGraph() {
// Create Canvas if not already created
if (document.getElementById('datacanvas') == null) {
var canvas = document.createElement('canvas');
canvas.setAttribute("id", "datacanvas")
var datadiv = document.getElementById("datadiv");
datadiv.appendChild(canvas);
var ctx = canvas.getContext("2d");
}
else {
var canvas = document.getElementById('datacanvas')
var ctx = canvas.getContext("2d");
}
var labls = ["Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"];
var datasts = createDatasets();
console.log(datasts);
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: labls,
datasets: datasts
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
}
The problem I have is the varible datasts or rather, the data property because the data is undefined. It gets created with these two functions:
function createDatasets() {
console.log("createDatasets");
var dataset = [];
var rdbStrom = document.getElementById('rdbStrom');
var rdbGas = document.getElementById('rdbGas');
var rdbWasser = document.getElementById('rdbWasser');
var rdbGesamt = document.getElementById('rdbGesamt');
if (rdbStrom.checked) {
var set = {
label: 'Stromkosten',
data: getDataArray("strom"),
borderColor: '#FF0000',
borderWidth: 1
};
dataset.push(set);
}
if (rdbGas.checked) {
var set = {
label: 'Gaskosten',
data: getDataArray("gas"),
borderColor: '#00FF00',
borderWidth: 1
};
dataset.push(set);
}
if (rdbWasser.checked) {
var set = {
label: 'Wasserkosten',
data: getDataArray("wasser"),
borderColor: '#0000FF',
borderWidth: 1
};
dataset.push(set);
}
if (rdbGesamt.checked) {
;
var set = {
label: 'Gesamtkosten',
data: getDataArray("gesamt"),
borderColor: '#FFFFFF',
borderWidth: 1
};
dataset.push(set);
}
// Wait
setTimeout(() => {
return dataset;
}, 1000);
}
function getDataArray(type) {
console.log("getDataArray");
var data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var period = [];
var start = new Date(document.getElementById('start').value);
var end = new Date(document.getElementById('end').value);
var tmp = new Date(start);
do {
period.push(tmp.toDateString());
tmp.setDate(tmp.getDate() + 1);
} while (tmp <= end)
if (!fs.existsSync('./Data.xlsx')) {
alert("Error finding File 'Data.xlsx'.");
}
else {
switch (type) {
case "strom": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var d = new Date(r.getCell(1).value);
if (period.includes(d.toDateString())) {
var vbr = r.getCell(3).value;
var prc = r.getCell(4).value;
var gprc = r.getCell(5).value;
var tax = r.getCell(6).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
case "gas": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var date = new Date(r.getCell(1).value);
if (period.includes(date.toDateString())) {
var vbr = r.getCell(8).value;
var prc = r.getCell(9).value;
var gprc = r.getCell(10).value;
var tax = r.getCell(11).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
case "wasser": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var date = new Date(r.getCell(1).value);
if (period.includes(date.toDateString())) {
var vbr = r.getCell(13).value;
var prc = r.getCell(14).value;
var gprc = r.getCell(15).value;
var tax = r.getCell(16).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
default:
break;
}
}
// Wait till process is done reading file
setTimeout(() => {
console.log("Timeout")
for (i = 0; i < data.length; i++) {
console.log("Data[" + i + " ]: " + data[i]);
}
console.log("Return DataArray");
return data;
}, 1000);
}
Again, I know my code is not good, I just need to get this working.
The output in the console is the following:
createDataset
getDataArray
undefined // This is the datasts Variable which I need to wait for
Timeout // This comes from the third function
// Here it displays the data it read from the Excel file from the third function
This is because of asynchronicity issue, you use createDatasets as if it was a synchronous function (such as return 1 + 2) while it relies on asynchronous operations,
ie :
let exampleInt = 0
setTimeout(() => {
// callback
exampleInt = 1;
return dataset;
}, 1000);
// This will be reached before the callback executes, so exampleInt equals 0
You should have a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises to achieve your goal which is, I guess, wait the end of an operation before executing code based on the result of that operation
Does it brighten your mind ?
Now resolution, first function : createDatasets
function createDatasets() {
console.log("createDatasets");
var dataset = [];
var rdbStrom = document.getElementById('rdbStrom');
var rdbGas = document.getElementById('rdbGas');
var rdbWasser = document.getElementById('rdbWasser');
var rdbGesamt = document.getElementById('rdbGesamt');
// storing each label we need
let dataArraysNeeded = [];
let dataArraysNeededAsPromises = [];
let designParams = {
"strom": {
title: "Stromkosten",
color: "#FF0000"
},
"gas": {
title: "Gaskosten",
color: "#00FF00"
},
"wasser": {
title: "Wasserkosten",
color: "#0000FF"
},
"gesamt": {
title: "Gesamtkosten",
color: "#FFFFFF"
}
};
if (rdbStrom.checked) {
dataArraysNeeded.push('strom');
}
if (rdbGas.checked) {
dataArraysNeeded.push('gas');
}
if (rdbWasser.checked) {
dataArraysNeeded.push('wasser');
}
if (rdbGesamt.checked) {
dataArraysNeeded.push('gesamt');
}
// From here we have an array of labels (ex: ["wasser","gesamt"])
// We now want to get the data array for each of these labels, here is how it's done
for (let i = 0; i < dataArraysNeeded.length; i++) {
dataArraysNeededAsPromises.push(getDataArray(dataArraysNeeded[i]));
}
// This will execute all the promises AND WAIT the end of the slowest promise
return Promise.all(dataArraysNeededAsPromises).then((sets) => {
let currentLabel = "";
// sets[0] equals getDataArray("wasser") for example
for (let j = 0; j < sets.length; j++) {
currentLabel = dataArrayLabel[j]; // "wasser"
dataset.push( {
label: designParams[currentLabel]["title"],
data: sets[j],
borderColor: designParams[currentLabel]["color"],
borderWidth: 1
});
}
return dataset; // Array of objects {label, data, borderColor, borderWidth}
});
}
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all for details on how works Promise.all
Your main function createGraph which calls createDatasets (you can see how the result of a function returning a promise is consumed)
function createGraph() {
// Create Canvas if not already created
if (document.getElementById('datacanvas') == null) {
var canvas = document.createElement('canvas');
canvas.setAttribute("id", "datacanvas")
var datadiv = document.getElementById("datadiv");
datadiv.appendChild(canvas);
var ctx = canvas.getContext("2d");
}
else {
var canvas = document.getElementById('datacanvas')
var ctx = canvas.getContext("2d");
}
var labls = ["Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"];
// Here you instanciate your promise of Dataset, which IS NOT synchronous
var datasetPromise = createDatasets();
// So you need to specifiy a callback, executed on promise completion
return datasetPromise.then((yourDatasetReadyToBeUsed) => {
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: labls,
datasets: yourDatasetReadyToBeUsed
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
return 'completed !';
});
}
I'll let you find the last one as it's very similar to those two functions (getDataArray needs to return a Promise too as it reads a file) !
Is it more clear for you ?
I'm trying to get the min and max value of a Dataview with the help of getColumnRange(). Unfortunately the values I get are not the ones I'd expect. Can someone advise me how I can get the correct values? The value I'd expect as max is 331M (the highest bar on the bar chart), but I get 8,5M. I tried to better my understanding of the DataView object with console.log(), however that didn't quite help me (line 65-71)
https://jsfiddle.net/zhp5rny6/3/
var data = new google.visualization.DataTable({
cols: [
{id:"","label":"Products","pattern":"","type":"string"},
{id:"","label":"Manila","pattern":"","type":"number"},
{id:"","label":"Bangkok","pattern":"","type":"number"},
{id:"","label":"New York","pattern":"","type":"number"},
{id:"","label":"Sydney","pattern":"","type":"number"}],
rows:[
{c:[{v:"Glasses"},{v:-1270.7287},{v:-4486.0961},{v:-1019.4662},{v:3763827.8335}]},
{c:[{v:"Joysticks"},{v:145543.985},{v:2108039.5802},{v:266392.5277},{v:6006566.18690001}]},
{c:[{v:"Laptops"},{v:1723781.2686},{v:4435404.56789999},{v:7060065.3164},{v:17514581.1763}]},
{c:[{v:"PS4"},{v:78002243.0557997},{v:47591320.6768999},{v:43832560.9528002},{v:161580254.021496}]},
{c:[{v:"XBOX One"},{v:10507117.0145001},{v:8059953.41120002},{v:14103816.4604002},{v:34676509.4036004}]},
{c:[{v:"Smartphones"},{v:2373067.7893},{v:-53954.2187},{v:30783391.8722},{v:37288122.5537999}]},
{c:[{v:"Chairs"},{v:86583.2876},{v:71082.0195},{v:79813.2918},{v:22959813.0499999}]},
{c:[{v:"CPU"},{v:12479599.4709999},{v:22650137.9890997},{v:17562381.7794999},{v:25589299.8099997}]},
{c:[{v:"RAM"},{v:null},{v:-59.1},{v:282.7517},{v:1461239.6186}]},
{c:[{v:"Table"},{v:-566.5377},{v:-217.3444},{v:-865.114},{v:356870.886599999}]},
{c:[{v:"Water"},{v:870883.521900001},{v:1128581.9856},{v:1177332.008},{v:3922057.50240001}]}]
});
var formatter = new google.visualization.NumberFormat({
pattern: 'short'
});
for (var i = 1; i < data.getNumberOfColumns(); i++) {
formatter.format(data, i);
}
var view = getDataView(data);
function getDataView(dataTable) {
var dataView;
var viewColumns = [];
for (var i = 0; i < dataTable.getNumberOfColumns(); i++) {
addViewColumn(i);
}
function addViewColumn(index) {
viewColumns.push(index);
if(index == (dataTable.getNumberOfColumns() - 1)) {
viewColumns.push({
calc: function (dt, row) {
for(var e = 0; e < dt.getNumberOfRows(); e++) {
var value = 0;
for(var u = 1; u < dt.getNumberOfColumns(); u++) {
value += dt.getValue(row, u);
}
return formatter.formatValue(value).toString();
}
},
type: 'string',
role: 'annotation',
});
}
}
dataView = new google.visualization.DataView(dataTable);
dataView.setColumns(viewColumns);
for(var z = 0; z < dataView.getNumberOfColumns(); z++) {
console.log(JSON.stringify(dataView.getColumnRange(z)));
}
console.log(JSON.stringify(dataView.getViewColumns()));
console.log(JSON.stringify(viewColumns));
return dataView;
}
the reason you get "8.5M" instead of "331M" is because the column type = 'string'
alphabetically, "8.5M" is greater than "331M"
to correct this issue, use a column type of 'number'.
when building the data view annotation column,
and return an object with both the value (v:) and formatted value (f:).
viewColumns.push({
calc: function (dt, row) {
for(var e = 0; e < dt.getNumberOfRows(); e++) {
var value = 0;
for(var u = 1; u < dt.getNumberOfColumns(); u++) {
value += dt.getValue(row, u);
}
return { // <-- return object here
v: value,
f: formatter.formatValue(value)
};
}
},
type: 'number', // <-- change to number
role: 'annotation',
});
see following working snippet...
google.charts.load("44", {packages:['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable({
cols: [
{id:"","label":"Products","pattern":"","type":"string"},
{id:"","label":"Manila","pattern":"","type":"number"},
{id:"","label":"Bangkok","pattern":"","type":"number"},
{id:"","label":"New York","pattern":"","type":"number"},
{id:"","label":"Sydney","pattern":"","type":"number"}],
rows:[
{c:[{v:"Glasses"},{v:-1270.7287},{v:-4486.0961},{v:-1019.4662},{v:3763827.8335}]},
{c:[{v:"Joysticks"},{v:145543.985},{v:2108039.5802},{v:266392.5277},{v:6006566.18690001}]},
{c:[{v:"Laptops"},{v:1723781.2686},{v:4435404.56789999},{v:7060065.3164},{v:17514581.1763}]},
{c:[{v:"PS4"},{v:78002243.0557997},{v:47591320.6768999},{v:43832560.9528002},{v:161580254.021496}]},
{c:[{v:"XBOX One"},{v:10507117.0145001},{v:8059953.41120002},{v:14103816.4604002},{v:34676509.4036004}]},
{c:[{v:"Smartphones"},{v:2373067.7893},{v:-53954.2187},{v:30783391.8722},{v:37288122.5537999}]},
{c:[{v:"Chairs"},{v:86583.2876},{v:71082.0195},{v:79813.2918},{v:22959813.0499999}]},
{c:[{v:"CPU"},{v:12479599.4709999},{v:22650137.9890997},{v:17562381.7794999},{v:25589299.8099997}]},
{c:[{v:"RAM"},{v:null},{v:-59.1},{v:282.7517},{v:1461239.6186}]},
{c:[{v:"Table"},{v:-566.5377},{v:-217.3444},{v:-865.114},{v:356870.886599999}]},
{c:[{v:"Water"},{v:870883.521900001},{v:1128581.9856},{v:1177332.008},{v:3922057.50240001}]}]
});
var formatter = new google.visualization.NumberFormat({
pattern: 'short'
});
for (var i = 1; i < data.getNumberOfColumns(); i++) {
formatter.format(data, i);
}
var view = getDataView(data);
function getDataView(dataTable) {
var dataView;
var viewColumns = [];
for (var i = 0; i < dataTable.getNumberOfColumns(); i++) {
addViewColumn(i);
}
function addViewColumn(index) {
viewColumns.push(index);
if(index == (dataTable.getNumberOfColumns() - 1)) {
viewColumns.push({
calc: function (dt, row) {
for(var e = 0; e < dt.getNumberOfRows(); e++) {
var value = 0;
for(var u = 1; u < dt.getNumberOfColumns(); u++) {
value += dt.getValue(row, u);
}
return {
v: value,
f: formatter.formatValue(value)
};
}
},
type: 'number',
role: 'annotation',
});
}
}
dataView = new google.visualization.DataView(dataTable);
dataView.setColumns(viewColumns);
for(var z = 0; z < dataView.getNumberOfColumns(); z++) {
console.log(JSON.stringify(dataView.getColumnRange(z)));
}
console.log(JSON.stringify(dataView.getViewColumns()));
console.log(JSON.stringify(viewColumns));
return dataView;
}
var options = {
annotations: {
alwaysOutside: true,
textStyle: {
fontSize: 10
}
},
height: 400,
isStacked: true
};
var chart = new google.visualization.ColumnChart(document.getElementById("columnchart_values"));
chart.draw(view, options);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="columnchart_values"></div>
I need to add a feature to Surface plots of Plotly. Feature is to change the range of Z axis data. problem is that it is only possible to limit the z axis value and expanding the values will not work. e.g. if I initialize 'Z Max' with 300 everything is fine but going back to 350 will keep the previous value. Although I have 'z_data' as my reference value that upon call of function by event listener should reserve the initially existing data, this does not happen and its values are updated to previously assigned 'Z Max' value.
Plotly.d3.csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv', function(err, rows){
function unpack(rows, key) {
return rows.map(function(row) { return row[key]; });
}
var z_data=[]
for(let i=0;i<24;i++) {
z_data.push(unpack(rows,i));
}
var allData = [];
for (let i=0; i<z_data.length; i++) {
for (let j=0; j<z_data[i].length; j++) {
allData.push(z_data[i][j])
}
}
var max = Math.max(...allData);
var min = Math.min(...allData);
setPlot(min, max);
function setPlot(zmin, zmax) {
console.log(z_data)
var newZData = z_data;
console.log('min specified: ' + zmin)
console.log('max specified: ' + zmax)
for (let i=0; i<newZData.length; i++) {
for (let j=0; j<newZData[i].length; j++) {
if (newZData[i][j] < zmin) {
newZData[i][j] = zmin
}
if (newZData[i][j] > zmax) {
newZData[i][j] = zmax
}
}
}
var data = [{
z: newZData,
type: 'surface'
}];
var layout = {
title: 'Mt Bruno Elevation',
autosize: false,
width: 500,
height: 500,
margin: {
l: 65,
r: 50,
b: 65,
t: 90,
}
};
Plotly.newPlot('myDiv', data, layout);
}
var zMin = document.querySelector("#zmin");
var zMax = document.querySelector("#zmax");
function updatePlot(){
setPlot(parseFloat(zMin.value), parseFloat(zMax.value));
}
zMin.addEventListener('change', updatePlot, false);
zMax.addEventListener('change', updatePlot, false);
Easier with JSFiddle:
http://jsfiddle.net/mwbbcyja/484/
I have the following Graph :
I want to access the max value of the y axis ( 11000000 ) in this case.
This value is dynamic so it can be different.
I want to save this value in a variable so i can use the max value of the y axis somewhere else.
How do i access the max value in c3js of that axis?
When i console.log(chart.axis.max());
console.log(chart.axis.range()); this values i get the value of y2 but not from y
Object {x: undefined, y: undefined, y2: 100}
Here is my full code :
/**
* #file
* Provides options for recline visualization.
*/
(function ($) {
Drupal.behaviors.ckan_graphmapper_chart_elastic = {
attach: function () {
var data = Drupal.settings.ckan_graphmapper.data;
/**
* Hensbergen
* #returns {*}
* #constructor
*/
function AvHensbergen() {
var parking = data.parkeren;
var parking_av_hensbergen = Array();
for (var j = 0; j < parking.length; j++) {
parking_av_hensbergen.push(parking[j].A_v_Hensbergen.percentage);
}
parking_av_hensbergen.unshift('A. v. Hensbergen');
return parking_av_hensbergen;
}
function AvHensbergenOnDate(res) {
data = res;
var parking = data.parkeren;
var parking_av_hensbergen = Array();
for (var j = 0; j < parking.length; j++) {
parking_av_hensbergen.push(parking[j].A_v_Hensbergen.percentage);
}
parking_av_hensbergen.unshift('A. v. Hensbergen');
return parking_av_hensbergen;
}
/**
* Gemeentehuis
* #returns {*}
* #constructor
*/
function Gemeentehuis() {
var parking = data.parkeren;
var parking_gemeentehuis = Array();
for (var j = 0; j < parking.length; j++) {
parking_gemeentehuis.push(parking[j].Gemeentehuis.percentage);
}
parking_gemeentehuis.unshift('Gemeentehuis');
return parking_gemeentehuis;
}
function GemeentehuisOnDate(res) {
data = res;
var parking = data.parkeren;
var parking_gemeentehuis = Array();
for (var j = 0; j < parking.length; j++) {
parking_gemeentehuis.push(parking[j].Gemeentehuis.percentage);
}
parking_gemeentehuis.unshift('Gemeentehuis');
return parking_gemeentehuis;
}
/**
* Coornhertpad
* #returns {*}
* #constructor
*/
function Coornhertpad() {
var parking = data.parkeren;
var parking_coornhertpad = Array();
for (var j = 0; j < parking.length; j++) {
parking_coornhertpad.push(parking[j].Coornhertpad.percentage);
}
parking_coornhertpad.unshift('Coornhertpad');
return parking_coornhertpad;
}
function CoornhertpadOnDate(res) {
data = res;
var parking = data.parkeren;
var parking_coornhertpad = Array();
for (var j = 0; j < parking.length; j++) {
parking_coornhertpad.push(parking[j].Coornhertpad.percentage);
}
parking_coornhertpad.unshift('Coornhertpad');
return parking_coornhertpad;
}
/**
* Duivenweide
* #returns {*}
* #constructor
*/
function Duivenweide() {
var parking = data.parkeren;
var parking_duivenweide = Array();
for (var j = 0; j < parking.length; j++) {
parking_duivenweide.push(parking[j].Duivenweide.percentage);
}
parking_duivenweide.unshift('Duivenweide');
return parking_duivenweide;
}
function DuivenweideOnDate(res) {
data = res;
var parking = data.parkeren;
var parking_duivenweide = Array();
for (var j = 0; j < parking.length; j++) {
parking_duivenweide.push(parking[j].Duivenweide.percentage);
}
parking_duivenweide.unshift('Duivenweide');
return parking_duivenweide;
}
/**
* Tricotage
* #returns {*}
* #constructor
*/
function Tricotage() {
var parking = data.parkeren;
var parking_tricotage = Array();
for (var j = 0; j < parking.length; j++) {
parking_tricotage.push(parking[j].Tricotage.percentage);
}
parking_tricotage.unshift('Tricotage');
return parking_tricotage;
}
function TricotageOnDate(res) {
data = res;
var parking = data.parkeren;
var parking_tricotage = Array();
for (var j = 0; j < parking.length; j++) {
parking_tricotage.push(parking[j].Tricotage.percentage);
}
parking_tricotage.unshift('Tricotage');
return parking_tricotage;
}
function getPassantenDates() {
var passers = data.passanten;
var passers_dates = Array();
for (var j = 0; j < passers.length; j++) {
passers_dates.push(passers[j].date);
}
passers_dates.unshift('x');
return passers_dates;
}
function getPassantenTotaal() {
var passers = data.passanten;
var passers_totaal = Array();
for (var j = 0; j < passers.length; j++) {
passers_totaal.push(passers[j].meetpunten.totaal);
}
passers_totaal.unshift('passanten');
return passers_totaal;
}
function getPassantenDatesOnDate(res) {
var data = res;
var passers = data.passanten;
var passers_dates = Array();
for (var j = 0; j < passers.length; j++) {
passers_dates.push(passers[j].date);
}
passers_dates.unshift('x');
return passers_dates;
}
function getPassantenTotaalOnDate(res) {
var data = res;
var passers = data.passanten;
var passers_total = Array();
for (var j = 0; j < passers.length; j++) {
passers_total.push(passers[j].meetpunten.totaal);
}
passers_total.unshift('passanten');
return passers_total;
}
function generateStarterGraph() {
var formatX;
var interval = jQuery('#interval_selector').val();
switch (interval) {
case 'day':
formatX = '%a %Y-%m-%d';
break;
case 'month':
formatX = '%m-%Y';
break;
case 'hour':
formatX = '%a %Y-%m-%d %H';
break;
case 'week':
formatX = '%a %Y-%m-%d';
break;
}
chart = c3.generate({
bindto: '#chatterplot_elastic',
data: {
x: 'x',
// #todo should be different based on interval selector.
xFormat: '%Y-%m-%d %H:%M:%S',
columns: [
getPassantenTotaal(),
getPassantenDates(),
AvHensbergen(),
Gemeentehuis(),
Coornhertpad(),
Duivenweide(),
Tricotage()
],
axes: {
'passanten': 'y',
'time_period': "x",
'A. v. Hensbergen': 'y2',
'Gemeentehuis': 'y2',
'Coornhertpad': 'y2',
'Duivenweide': 'y2',
'Tricotage': 'y2'
},
types: {
passanten: 'area-spline',
'A. v. Hensbergen': 'scatter',
'Gemeentehuis': 'scatter',
'Coornhertpad': 'scatter',
'Duivenweide': 'scatter',
'Tricotage': 'scatter'
},
onclick: function (e) {
updateGraphMapper(e);
}
},
axis: {
y2: {
show: true,
min: 0,
max: 100,
padding: {top: 0, bottom: 0},
},
x: {
type: 'timeseries',
localtime: false,
tick: {
format: formatX,
culling: {
max: 5,
}
}
}
}
});
}
/**
* Get the Starter Graph
*/
generateStarterGraph();
console.log(chart.axis.max());
console.log(chart.axis.range());
/**
* When a new date is being set
*/
$(".time-select").bind("change", function () {
var time_period = $('.time-select option:selected').val();
var start_date = new Date($("#slider").dateRangeSlider("values").min).toISOString();
var end_date = new Date($("#slider").dateRangeSlider("values").max).toISOString();
$.ajax({
url: Drupal.settings.basePath + Drupal.settings.veenendaal_parkeren.passanten_url,
type: 'POST',
data: {
interval: time_period,
start: start_date,
end: end_date
},
success: function (res) {
chart.load({
bindto: "#chatterplot_elastic",
x: 'x',
xFormat: '%Y-%m-%d %H:%M:%S',
columns: [
getPassantenTotaalOnDate(res),
getPassantenDatesOnDate(res),
AvHensbergenOnDate(res),
GemeentehuisOnDate(res),
CoornhertpadOnDate(res),
DuivenweideOnDate(res),
TricotageOnDate(res)
]
});
}
});
});
/**
* When a new date is being set
*/
$("#slider").bind("valuesChanged", function (e, data) {
var start_date = new Date(data.values.min).toISOString();
var end_date = new Date(data.values.max).toISOString();
$.ajax({
url: Drupal.settings.basePath + Drupal.settings.veenendaal_parkeren.passanten_url,
type: 'POST',
data: {
start: start_date,
end: end_date,
interval: $('#interval_selector').val()
},
success: function (res) {
updateGraph(
getPassantenTotaalOnDate(res),
getPassantenDatesOnDate(res),
AvHensbergenOnDate(res),
GemeentehuisOnDate(res),
CoornhertpadOnDate(res),
DuivenweideOnDate(res),
TricotageOnDate(res)
);
}
});
function updateGraph(yAxis, xAxis, Hensbergen, Gemeentehuis, Coornhertpad, Duivenweide, Tricotage) {
setTimeout(function () {
chart.load({
bindto: "#chatterplot_elastic",
x: 'x',
xFormat: '%Y-%m-%d %H:%M:%S',
columns: [
yAxis,
xAxis,
Hensbergen,
Gemeentehuis,
Coornhertpad,
Duivenweide,
Tricotage
]
});
}, 200);
}
});
}
};
})(jQuery, Drupal);
Use chart.axis.range() to get a object containing max{x,y,y2} and min{x,y,y2} as shown below, instead of chart.axis.max().
You can calc like this
Math.max.apply(null, chart.data.values('y')
//y is your axes name.
https://jsfiddle.net/9as8r2sq/
If you want to get max from many lines you can do it in cycle
I'm working with the chartJS library and trying to figure out what I need to do to get a single lines data to display in the tooltip.
For example,
I am hovering over the blue line here and see every data point at that mark. What I would like to do is see all three data points for the blue line only.
I've made some progress from chart js tooltip how to control the data that show
getPointsAtEvent: function(e) {
var pointsArray = [], eventPosition = helpers.getRelativePosition(e);
var breakLoop = 0;
helpers.each(this.datasets, function(dataset) {
helpers.each(dataset.points, function(point) {
if (point.inRange(eventPosition.x, eventPosition.y) && point.showTooltip && !point.ignore) {
if(eventPosition.y + 2 >= point.y && eventPosition.y - 2 <= point.y) {
pointsArray.push(point);
breakLoop = 1;
return false;
}
}
});
if(breakLoop) {
return false;
}
}, this);
//console.log(pointsArray);
return pointsArray;
},
Is my chart modification that will return 1 data point on the graph. I'm assuming the next step is to overwrite the showToolTip method.
If this is the only chart you have (i.e. because the following code changes some of the global chart.js elements), you can use the following bit of code
var originalMultiTooltip = Chart.MultiTooltip;
Chart.MultiTooltip = function () {
var argument = arguments[0];
// locate the series using the active point
var activeDatasetLabel = myChart.activeElements[0].datasetLabel;
myChart.datasets.forEach(function (dataset) {
if (dataset.label === activeDatasetLabel) {
// swap out the labels and colors in arguments
argument.labels = dataset.points.map(function (point) { return point.value; });
argument.legendColors = dataset.points.map(function (point) {
return {
fill: point._saved.fillColor || point.fillColor,
stroke: point._saved.strokeColor || point.strokeColor
};
});
argument.title = activeDatasetLabel;
// position it near the active point
argument.y = myChart.activeElements[0].y;
}
})
return new originalMultiTooltip(arguments[0]);
}
// this distance function returns the square of the distance if within detection range, otherwise it returns Infinity
var distance = function (chartX, chartY) {
var hitDetectionRange = this.hitDetectionRadius + this.radius;
var distance = Math.pow(chartX - this.x, 2) + Math.pow(chartY - this.y, 2);
return (distance < Math.pow(hitDetectionRange, 2)) ? distance : Infinity;
}
myChart.getPointsAtEvent = function (e) {
var pointsArray = [],
eventPosition = Chart.helpers.getRelativePosition(e);
var leastDistance = Infinity;
Chart.helpers.each(myChart.datasets, function (dataset) {
Chart.helpers.each(dataset.points, function (point) {
// our active point is the one closest to the hover event
var pointDistance = distance.call(point, eventPosition.x, eventPosition.y)
if (isFinite(pointDistance) && pointDistance < leastDistance) {
leastDistance = pointDistance;
pointsArray = [ point ];
}
});
}, myChart);
return pointsArray;
}
It does 2 things
Replaces the getPointsAtEvent to just pick one point
Wraps the MultiTooltip constructor to swap out the list of values passed with all the values from the active point's series.
Fiddle - http://jsfiddle.net/h93pyavk/
If you extend the line chart, use the code I have above, and the code I pasted below you can get the desired effect to some degree.
showTooltip: function(ChartElements, forceRedraw) { //custom edit
//we will get value from ChartElements (which should be only 1 element long in this case) and use it to match the line row we want to see.
try {
var numMatch = ChartElements[0].value;
}
catch(err) {
var isChanged = (function(Elements) {
var changed = true;
return changed;
}).call(this, ChartElements);
}
// Only redraw the chart if we've actually changed what we're hovering on.
if (typeof this.activeElements === 'undefined') this.activeElements = [];
var isChanged = (function(Elements) {
var changed = false;
if (Elements.length !== this.activeElements.length) {
changed = true;
return changed;
}
helpers.each(Elements, function(element, index) {
if (element !== this.activeElements[index]) {
changed = true;
}
}, this);
return changed;
}).call(this, ChartElements);
if (!isChanged && !forceRedraw) {
return;
} else {
this.activeElements = ChartElements;
}
this.draw();
if (this.options.customTooltips) {
this.options.customTooltips(false);
}
if (ChartElements.length > 0) {
// If we have multiple datasets, show a MultiTooltip for all of the data points at that index
if (this.datasets && this.datasets.length > 1) {
var dataArray,
dataIndex;
for (var i = this.datasets.length - 1; i >= 0; i--) {
dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;
dataIndex = helpers.indexOf(dataArray, ChartElements[0]);
if (dataIndex !== -1) {
break;
}
}
var eleLast = "";
var eleFirst = "";
var tooltipLabels = [],
tooltipColors = [],
medianPosition = (function(index) {
// Get all the points at that particular index
var Elements = [],
dataCollection,
xPositions = [],
yPositions = [],
xMax,
yMax,
xMin,
yMin;
helpers.each(this.datasets, function(dataset) {
dataCollection = dataset.points || dataset.bars || dataset.segments;
//console.log(dataset);
for(i = 0; i < dataset.points.length; i++) {
if(dataset.points[i].value === numMatch) {
for(var k = 0; k < dataset.points.length; k++) {
Elements.push(dataset.points[k]);
}
}
}
});
//save elements last label string
eleLast = Elements[Elements.length-1].label;
eleFirst = Elements[0].label;
//console.log(Elements);
helpers.each(Elements, function(element) {
if(element.value === numMatch) {
xPositions.push(element.x);
yPositions.push(element.y);
}
//Include any colour information about the element
tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));
tooltipColors.push({
fill: element._saved.fillColor || element.fillColor,
stroke: element._saved.strokeColor || element.strokeColor
});
}, this);
yMin = helpers.min(yPositions);
yMax = helpers.max(yPositions);
xMin = helpers.min(xPositions);
xMax = helpers.max(xPositions);
return {
x: (xMin > this.chart.width / 2) ? xMin : xMax,
y: (yMin + yMax) / 2
};
}).call(this, dataIndex);
var newLabel = eleFirst + " to " + eleLast;
new Chart.MultiTooltip({
x: medianPosition.x,
y: medianPosition.y,
xPadding: this.options.tooltipXPadding,
yPadding: this.options.tooltipYPadding,
xOffset: this.options.tooltipXOffset,
fillColor: this.options.tooltipFillColor,
textColor: this.options.tooltipFontColor,
fontFamily: this.options.tooltipFontFamily,
fontStyle: this.options.tooltipFontStyle,
fontSize: this.options.tooltipFontSize,
titleTextColor: this.options.tooltipTitleFontColor,
titleFontFamily: this.options.tooltipTitleFontFamily,
titleFontStyle: this.options.tooltipTitleFontStyle,
titleFontSize: this.options.tooltipTitleFontSize,
cornerRadius: this.options.tooltipCornerRadius,
labels: tooltipLabels,
legendColors: tooltipColors,
legendColorBackground: this.options.multiTooltipKeyBackground,
title: newLabel,
chart: this.chart,
ctx: this.chart.ctx,
custom: this.options.customTooltips
}).draw();
} else {
helpers.each(ChartElements, function(Element) {
var tooltipPosition = Element.tooltipPosition();
new Chart.Tooltip({
x: Math.round(tooltipPosition.x),
y: Math.round(tooltipPosition.y),
xPadding: this.options.tooltipXPadding,
yPadding: this.options.tooltipYPadding,
fillColor: this.options.tooltipFillColor,
textColor: this.options.tooltipFontColor,
fontFamily: this.options.tooltipFontFamily,
fontStyle: this.options.tooltipFontStyle,
fontSize: this.options.tooltipFontSize,
caretHeight: this.options.tooltipCaretSize,
cornerRadius: this.options.tooltipCornerRadius,
text: helpers.template(this.options.tooltipTemplate, Element),
chart: this.chart,
custom: this.options.customTooltips
}).draw();
}, this);
}
}
return this;
},
Obviously this is just a quick and dirty fix, if I get more time to work on it I would like to have each data point show its corresponding value above it.