React Highchart on select event not getting any values in target object - javascript

i am trying to access the properties of a chart point in a Highcharts chart that is being used in a React application, but when i click on the point, the event.target object that is passed to the event handler function is showing all keys as null.
my graph has some filters to sort data by week, last 30 days, custom, and today.
when i click on any point before filtering event.target function shows object fulfilled with every key but when i filtered and my gaprh gets updated with new value which works fine but event.target start showing all keys as null
export const getChartConfig = (
showingStats,
data,
labels,
graphFilter,
filter,
dates,
pointerClickSetter,
) => {
let line1Data = [];
let line2Data = [];
let line3Data = [];
let line4Data = [];
let line5Data = [];
let line6Data = [];
let yAxisLabels = ['', ''];
const totalNeeded = labels.length;
const totalFilled = data.length;
if (totalNeeded === totalFilled) {
Array(totalNeeded)
.fill(0)
.map((item, index) => {
line1Data.push([labels[index], data[index].panelPower]);
line2Data.push([labels[index], data[index].panelVoltage]);
line3Data.push([labels[index], data[index].batteryVoltage]);
line4Data.push([labels[index], data[index].batteryPower]);
line5Data.push([labels[index], data[index].generatedEnergy]);
line6Data.push([labels[index], data[index].consumedEnergy]);
});
} else {
if (filter === 'hourly') {
labels.map((item) => {
const indexOf = [...data].findIndex((eachItem) => {
if (dayjs().date() == dayjs(eachItem.date).utc().format('DD')) {
return dayjs(eachItem.date).utc().format('HH') == item;
}
});
if (indexOf !== -1) {
const format = dayjs(data[indexOf].date).utc().format('HH');
if (format == item) {
line1Data.push([item, data[indexOf].panelPower]);
line2Data.push([item, data[indexOf].panelVoltage]);
line3Data.push([item, data[indexOf].batteryVoltage]);
line4Data.push([item, data[indexOf].batteryPower]);
line5Data.push([item, data[indexOf].generatedEnergy]);
line6Data.push([item, data[indexOf].consumedEnergy]);
} else {
line1Data.push([item, 0]);
line2Data.push([item, 0]);
line3Data.push([item, 0]);
line4Data.push([item, 0]);
line5Data.push([item, 0]);
line6Data.push([item, 0]);
}
} else {
line1Data.push([item, 0]);
line2Data.push([item, 0]);
line3Data.push([item, 0]);
line4Data.push([item, 0]);
line5Data.push([item, 0]);
line6Data.push([item, 0]);
}
});
} else if (filter === 'daily') {
const lastNDays = graphFilter === 'Last 7 Days' ? getLastNDays(7) : labels;
lastNDays.map((item, index) => {
const indexOf = [...data].findIndex((eachItem) => {
return dayjs(eachItem.date).utc().format('DD-MMM-YYYY') === item;
});
if (indexOf !== -1) {
line1Data.push([labels[index], data[indexOf].panelPower]);
line2Data.push([labels[index], data[indexOf].panelVoltage]);
line3Data.push([labels[index], data[indexOf].batteryVoltage]);
line4Data.push([labels[index], data[indexOf].batteryPower]);
line5Data.push([labels[index], data[indexOf].generatedEnergy]);
line6Data.push([labels[index], data[indexOf].consumedEnergy]);
} else {
line1Data.push([labels[index], 0]);
line2Data.push([labels[index], 0]);
line3Data.push([labels[index], 0]);
line4Data.push([labels[index], 0]);
line5Data.push([labels[index], 0]);
line6Data.push([labels[index], 0]);
}
});
} else if (filter === 'weekly') {
const lastNWeeks = getNWeeks(5);
const lastNDays = getLastNDays(30);
let tempDates = [...data].map((eachItem) => dayjs(eachItem.date).utc().format('DD-MMM-YYYY'));
let includedArr = tempDates.map((tempDate) => {
let tempDateObj = dayjs(tempDate);
for (let i = 0; i <= lastNWeeks.length - 1; i++) {
if (dayjs(tempDateObj > lastNWeeks[i]) && tempDateObj < dayjs(lastNWeeks[i + 1])) {
return lastNWeeks[i];
}
}
});
lastNWeeks.map((item, index) => {
const indexOf = includedArr.indexOf(item);
if (indexOf !== -1) {
line1Data.push([labels[index], data[indexOf].panelPower]);
line2Data.push([labels[index], data[indexOf].panelVoltage]);
line3Data.push([labels[index], data[indexOf].batteryVoltage]);
line4Data.push([labels[index], data[indexOf].batteryPower]);
line5Data.push([labels[index], data[indexOf].generatedEnergy]);
line6Data.push([labels[index], data[indexOf].consumedEnergy]);
} else {
line1Data.push([labels[index], 0]);
line2Data.push([labels[index], 0]);
line3Data.push([labels[index], 0]);
line4Data.push([labels[index], 0]);
line5Data.push([labels[index], 0]);
line6Data.push([labels[index], 0]);
}
});
}
}
const allSeries = [];
if (showingStats[0].showing) {
allSeries.push(getSeriesConfig('#5ad8a6', line1Data, 'Solid', 0, false));
}
if (showingStats[1].showing) {
allSeries.push(getSeriesConfig('#5ad8a6', line2Data, 'ShortDot', 1, true));
}
if (showingStats[2].showing) {
allSeries.push(getSeriesConfig('#e8684a', line3Data, 'ShortDot', 0, false));
}
if (showingStats[3].showing) {
allSeries.push(getSeriesConfig('#e8684a', line4Data, 'Solid', 1, true));
}
if (showingStats[4].showing) {
allSeries.push(getSeriesConfig('#0000FF', line5Data, 'Solid', 0, false));
}
if (showingStats[5].showing) {
allSeries.push(getSeriesConfig('#0000FF', line6Data, 'ShortDot', 1, true));
}
showingStats.forEach((stat) => {
if (stat.value.includes('voltage') && stat.showing) {
yAxisLabels[1] = 'Voltage (Volts)';
}
if (stat.value.includes('current') && stat.showing) {
yAxisLabels[0] = 'Current (Amperes)';
}
});
const options = {
chart: {
type: 'line',
zoomType: 'x',
},
reflow: true,
credits: false,
exporting: {
enabled: false,
},
title: {
text: '',
},
// Current (Amperes)
//
yAxis: [
{
// Secondary yAxis
title: {
text: yAxisLabels[0],
},
labels: {
format: '{value}',
},
},
{
// Primary yAxis
labels: {
format: '{value}',
},
title: {
text: yAxisLabels[1],
},
opposite: true,
},
],
xAxis: {
type: 'category',
tickInterval: 1,
categories: [...labels],
accessibility: {
rangeDescription: '',
},
crosshair: {
width: 1,
color: '#2296f3',
},
},
plotOptions: {
spline: {
states: {
hover: {
enabled: true, //shows dots when hover on the line
},
},
marker: {
enabled: false,
},
},
series: {
allowPointSelect: true,
point: {
events: {
select: pointerClickSetter, //<---- here is the problem --- pointerClickSetter is a setter funtion of useState ---
},
},
// data: allSeries,
label: {
connectorAllowed: true,
},
pointStart: 1,
},
},
tooltip: {
backgroundColor: '#ffffff',
borderColor: '#ffffff',
crosshairs: [true, false],
formatter: function () {
const value1 = line1Data[labels.indexOf(this.x)][1].toFixed(2) || 0;
const value2 = line2Data[labels.indexOf(this.x)][1].toFixed(2) || 0;
const value3 = line3Data[labels.indexOf(this.x)][1].toFixed(2) || 0;
const value4 = line4Data[labels.indexOf(this.x)][1].toFixed(2) || 0;
const value5 = line5Data[labels.indexOf(this.x)][1].toFixed(2) || 0;
const value6 = line6Data[labels.indexOf(this.x)][1].toFixed(2) || 0;
return (
'Panel Voltage: <b>' +
value2 +
'V</b>' +
'<br><br>Panel Current: <b>' +
value1 +
'A</b>' +
'<br><br>Battery Voltage: <b>' +
value4 +
'V</b>' +
'<br><br>Battery Current: <b>' +
value3 +
'A</b>' +
'</b>' +
'<br><br>Solar Generation: <b>' +
value5 +
'Wh</b>' +
'</b>' +
'<br><br>Power Consumption: <b>' +
value6 +
'Wh</b>'
);
},
},
series: [...allSeries],
};
return options;
};
<HighchartsReact
highcharts={Highcharts}
options={getChartConfig(
showingStats,
data,
labels,
graphFilter,
graphSubFilter,
currentDates,
chartPointerSetter,
)}
/>

That's because the getChartConfig function is called again on state change which triggers unnecessary chart updating through the HighchartsReact component.
You should keep your chart options in a state, memorize them or if your chart is static, disable the allowChartUpdate option.
<HighchartsReact
...
allowChartUpdate={false}
/>
Docs: https://github.com/highcharts/highcharts-react#optimal-way-to-update

Related

Display data for specific release selected in release filter at the top of page in rally dashboard

This is my code and in this the data displayed in chart is hole project data but in rally dashboard there is release filter at the top of your page. and i want my chart to show data of the the release selected by that filter and my sdk version in code is 1.33
<!DOCTYPE HTML\>
<script
src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js">
var WORKSPACE_OID = "__WORKSPACE_OID__";
var PROJECT_OID = "__PROJECT_OID__";
var PROJECT_SCOPING_UP = "__PROJECT_SCOPING_UP__";
var PROJECT_SCOPING_DOWN = "__PROJECT_SCOPING_DOWN__";
var MILS_IN_DAY = 86400000;
var DAYS_BACK = 30;
var filterChart;
var currentProjectDataSource;
var fromDate = new Date(new Date().getTime() - (MILS_IN_DAY * DAYS_BACK));
var allDefects = [];
// var currentRelease;
var onLoadAllIssues = function (result) {
// var defects = result.defects.filter(function (defect) {
// return defect.Release && defect.Release.\_refObjectName === currentRelease.Name;
// });
var labels = [];
var openDefects = [];
var closedDefects = [];
var defects = result.defects;
for (var count = 0; count < defects.length; count++) {
allDefects[allDefects.length] = defects[count];
var defect = defects[count];
labels.push(defect.CreationDate.split('T')[0]);
if (defect.ClosedDate !==null) {
closedDefects.push(defect.ClosedDate.split('T')[0]);
}
}
closedDefects.sort();
const counts = {};
labels.forEach(function (x) { counts[x] = (counts[x] || 0) + 1; });
const closedcounts = {};
closedDefects.forEach(function (x) { closedcounts[x] = (closedcounts[x] || 0) + 1; });
mychart(counts,closedcounts,labels)
};
var createCharts = function () {
var loadAllDefectsQuery = {
type: 'defect',
key: 'defects',
fetch: 'CreationDate,ClosedDate,ObjectID,FormattedID,Name,State,Priority',
order: 'CreationDate',
query: '((CreationDate != "null") OR (CreationDate > "' + dojo.date.stamp.toISOString(fromDate, { zulu: true }) +
'"))'
};
currentProjectDataSource.findAll(loadAllDefectsQuery, onLoadAllIssues);
};
var initPage = function () {
currentProjectDataSource = new rally.sdk.data.RallyDataSource(WORKSPACE_OID, PROJECT_OID, PROJECT_SCOPING_UP,
PROJECT_SCOPING_DOWN);
createCharts();
};
rally.addOnLoad(initPage);
function mychart(counts,closedcounts,labels) {
const pielable = labels;
const piedata = counts;
const closedcountsdata = closedcounts;
const data = {
datasets: [
{
label: 'Number of opened defects',
data: piedata,
},
{
label: 'Number of closed defects',
data: closedcountsdata,
}
]
};
const config = {
type: 'line',
data: data,
options: {
scales: {
x: {
min:"2022-01-01",
max:"2022-12-31",
type: 'time',
time:{
unit:'day',
},
},
y: {
beginAtZero: true,
grace: 5,
ticks: {
stepSize: 1,
},
},
},
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Defect Burndown Chart'
},
tooltip: {
yAlign: 'bottom',
titleMarginBottom: 0,
callbacks: {
title: function (context) {
return( `${context[0].label.slice(0, -13).replace(/,/g, " ")}`)
},
}
}
}
}
};
const myChart = new Chart(
document.getElementById('myChart'),
config
)
filterChart= function filterChart(date){
const year = date.value.substring(0,4);
const month = date.value.substring(5);
const lastday = (y,m)=>{
return new Date(y,m,0).getDate();
}
const startDate = `${date.value}-01`;
const endDate = `${date.value}-${lastday(year,month)}`;
myChart.config.options.scales.x.min=startDate;
myChart.config.options.scales.x.ma`your text`x=endDate;
myChart.update();
}}
</script>

Icons not Displaying on Deck.gl

I think there's an error with the data format that I using but I can't seen to solve it, at the current format no icons are displaying.
var coord = [];
export default function App({
function coordGet() {
for (let i = 0; i < IconPositions.length; i++) {
coord["ID" + i] = [{
name: "ID" + i,
address: "Address" + i,
exits: "Exits" + i,
position: [parseFloat(IconPositions[i][0]), parseFloat(IconPositions[i][1])]
}];
}
coordGet();
}
const iconVar = { data: coordGet() };
function updateLayers() {
const layers = [
new IconLayer({
id: "icon-layer",
data: iconVar.data,
pickable: true,
iconAtlas: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/icon-atlas.png',
iconMapping: ICON_MAPPING,
getIcon: d => 'marker',
getPosition: d => d.position,
sizeScale: 200,
getSize: d => 7,
getColor: d => [Math.sqrt(d.exits), 140, 0],
})
];
return layers;
}

Error: Highcharts error #13: www.highcharts.com/errors/13/ in Reactjs

I am trying to use High-charts in reactjs and I am new to both these. In the beginning, everything was fine but now I am unable to solve this error.
The following is my code:
Array.prototype.contains = function (v) {
for (var i = 0; i < this.length; i++) {
if (this[i] === v) return true;
}
return false;
};
Array.prototype.unique = function () {
var arr = [];
for (var i = 0; i < this.length; i++) {
if (!arr.contains(this[i])) {
arr.push(this[i]);
}
}
return arr;
}
export default class Chart extends React.Component {
constructor(props) {
super(props);
this.state = {
installs: [],
isLoaded: false,
}
this.state = {
filteredData: [],
isLoadedFilteredData: false,
showChart: true
}
this.state = {
options: {}
}
}
componentDidMount() {
// setInterval(() => {
fetch('xyz)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
installs: json,
});
});
}
getFilteredData = async (operatingSystem, applicationServer, database, cloud, serverRelease) => {
fetch(`xyz`)
.then(res => res.json())
.then(json => {
// console.log(json);
this.setState({
isLoadedFilteredData: true,
filteredData: json,
}, console.log("getFilteredData called ... "));
});
}
handleChange = ({ altered, name, text, value }) => {
const nextFormValue = {
[name]: {
text,
value,
altered
}
};
this.setState({ ...this.state, ...nextFormValue }, () =>
console.log("handleChange()", this.state),
);
};
render() {
const { isLoaded, installs } = this.state;
const { isLoadedFilteredData, filteredData } = this.state;
if (!isLoaded) {
return <p>Loading...</p>;
}
else {
var arrayOS = []
var arrayApp = []
var arrayDB = []
var arrayCloud = []
var arrayServerRelease = []
for (var i = 0; i < installs.length; i++) {
arrayOS.push(this.state.installs[i].object.hardwareStackDetails.operatingSystem)
arrayApp.push(this.state.installs[i].object.hardwareStackDetails.applicationServer)
arrayDB.push(this.state.installs[i].object.hardwareStackDetails.database)
arrayCloud.push(this.state.installs[i].object.cloud)
arrayServerRelease.push(this.state.installs[i].object.serverRelease)
}
var operatingSystemTemp = arrayOS.unique();
var applicationServerTemp = arrayApp.unique();
var dataBaseTemp = arrayDB.unique();
var cloudTemp = arrayCloud.unique();
var serverReleaseTemp = arrayServerRelease.unique();
arrayOS = []
arrayApp = []
arrayDB = []
arrayCloud = []
arrayServerRelease = []
for (var i = 0; i < operatingSystemTemp.length; i++) {
arrayOS.push({
text: operatingSystemTemp[i],
value: operatingSystemTemp[i]
})
}
for (var i = 0; i < applicationServerTemp.length; i++) {
arrayApp.push({
text: applicationServerTemp[i],
value: applicationServerTemp[i]
})
}
for (var i = 0; i < dataBaseTemp.length; i++) {
arrayDB.push({
text: dataBaseTemp[i],
value: dataBaseTemp[i],
})
}
for (var i = 0; i < cloudTemp.length; i++) {
arrayCloud.push({
text: cloudTemp[i],
value: cloudTemp[i]
})
}
for (var i = 0; i < serverReleaseTemp.length; i++) {
arrayServerRelease.push({
text: serverReleaseTemp[i],
value: serverReleaseTemp[i]
})
}
if (isLoadedFilteredData){
if(filteredData.length === 0) {
console.log("filteredData")
mapNew = new Map();
labelMap = {};
xlabels = []
seriesDataValue = []
Highcharts.chart('container', {
title: {
text: ''
},
subtitle: {
// text: 'Source: thesolarfoundation.com'
},
xAxis: {
categories: xlabels
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
series: seriesDataValue,
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
}
);
}
else {
console.log(filteredData)
var mapNew = new Map();
var labelMap = {};
var xlabels = []
var seriesDataValue = []
for (var j = 0; j < filteredData.length; j++) {
if (filteredData[j].object.status === "PASS") {
var date1 = moment(filteredData[j].object.creationTime);
var date2 = moment(filteredData[j].object.updationTime);
var ms = moment(date2, "DD/MM/YYYY HH:mm:ss").diff(moment(date1, "DD/MM/YYYY HH:mm:ss"));
var d = moment.duration(ms);
// var s = d.format("hh:mm:ss");
console.log(ms)
console.log(d.hours())
var timeInhrs = d.hours()
if (mapNew.has(filteredData[j].object.clientBuild)) {
// timeInhrs = ((timeInhrs + mapNewInnerTemp.get(this.state.kuch[j].object.clientBuild)));
console.log(this.state.filteredData[j].object.clientBuild + " " + timeInhrs);
mapNew.set(this.state.filteredData[j].object.clientBuild, timeInhrs);
} else {
console.log(this.state.filteredData[j].object.clientBuild + " " + timeInhrs);
mapNew.set(this.state.filteredData[j].object.clientBuild, timeInhrs);
labelMap[this.state.filteredData[j].object.clientBuild] = 0; ///
//count = count + 1;
}
}
}
console.log(mapNew)
for (var key in labelMap) {
xlabels.push(key);
}
console.log(xlabels)
var dataValue = [] // array
for (const entry of mapNew.entries()) {
console.log(entry)
for (var key in labelMap) {
if (key === entry[0]) {
dataValue.push(entry[1])
}
}
}
console.log(dataValue)
seriesDataValue.push(
{
data: dataValue,
})
console.log(seriesDataValue)
var elementExists = document.getElementById("container");
if(elementExists === null){
let div = document.createElement("div");
div.id="container";
div.id="select"
document.body.appendChild(div);
}
Highcharts.chart('container',
xAxis: {
categories: xlabels
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
series: seriesDataValue,
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
}
);
}
}
return (
<div>
<div id="container"> </div>
</div>
);
}
}
}
The above error gets vanished after the page gets refreshed .
The error results from the fact that you create a chart before a div element with the 'container' id is created.
However, to avoid such problems I recommend you to use officially supported Highcharts wrapper for React: https://www.npmjs.com/package/highcharts-react-official

How to clear the vue warn when I use vue-slider-component?

The issue detail:
1. I implement the feature with the vue-slider-component module, but that has a lot of warnings when I move the dots on the slider.
2. I know that the reason is that I used v-for to point to an object that will change, but I do not know how to fix this issue.
the following link is my test site:
https://jsfiddle.net/ncwv84x9/
enter image description here
My codes:
code1 (Html)
<div id="app">
<div class="box" v-for="(item,index) in columnvalue">
<label>{{item.text}}</label>
<input v-model="value[index]" />
</div>
<hr />
<br />
<vue-slider v-model="value" :order="false" :tooltip="'always'" :process="false" :marks="marks" :width="600">
<template slot="tooltip" slot-scope="{index}">
<div>{{getText(index)}}</div>
</template>
</vue-slider>
</div>
JavaScript + Vue:
new Vue({
el: '#app',
components: {
VueSlider: window['vue-slider-component']
},
data: function() {
return {
// collect the all values
columnvalue: [],
// stored disease value
pet_name: [{
text: 'dog',
index: 0
},
{
text: 'cat',
index: 1
}
],
// stored drug value
feeder_name: [{
text: 'Sam',
index: 0
}],
// from age filter
age: [
65, 100
],
test: "",
value: [],
process: dotsPos => [
[dotsPos[0], dotsPos[1], {
backgroundColor: 'pink'
}],
[dotsPos[1], dotsPos[2], {
backgroundColor: 'blue'
}],
[dotsPos[2], dotsPos[3], {
backgroundColor: 'black'
}],
],
after: {},
relations: [],
marks: {
'0': {
label: 'start',
margin: '0 0 0 10px'
},
'100': {
label: 'end',
labelStyle: {
left: '100%',
margin: '0 0 0 10px',
top: '50%',
transform: 'translateY(-50%)'
}
}
}
}
},
created: function() {
//vue instance 被 constructor 建立後,在這裡完成 data binding
let tmpArray = this.pet_name.concat(this.feeder_name);
let tmpValueArray = [];
for (i = 0; i < tmpArray.length; i++) {
tmpArray[i].index = i;
tmpValueArray.push(0);
}
this.columnvalue = tmpArray;
this.value = tmpValueArray;
},
methods: {
getText(index) {
const ani = this.columnvalue.find((v) => v.index == index).text;
this.after = {
...this.after,
[ani]: this.value[index]
}
return ani;
},
getNodeRelation() {
const indexs = this.after;
let result = [];
let result2 = [];
let placement = [];
for (obj in indexs) {
placement.push([obj, indexs[obj]]);
}
placement.sort(function(a, b) {
/* console.log(a[1]) */
return a[1] - b[1];
})
for (i = 0; i < placement.length; i++) {
if (i + 1 >= placement.length) {
break;
}
let distance = placement[i + 1][1] - placement[i][1];
let predicate = "";
if (distance > 0) {
predicate = "after";
} else if (distance == 0 && placement[i + 1][1] == 0) {
predicate = "hasUse";
} else {
predicate = "same";
}
let source = {
label: placement[i][0],
index: i
};
let target = {
label: placement[i + 1][0],
index: i
};
// store the 4-tuple reprsentations about slider
result2.push({
source: source,
target: target,
type: predicate,
days: distance
});
}
/* this.relations = "{\"relation\":" + JSON.stringify(result2)+"}" */
;
this.relations = JSON.stringify(result2);
},
getAllFilters() {
let vm = this;
let beginHas = true;
if (vm.relations.length == 0) {
vm.getNodeRelation();
beginHas = false;
}
let result = {
age: vm.age,
disease_name: vm.disease_name,
drug_name: vm.drug_name,
relation: vm.relations
};
if (!beginHas) {
vm.relations = [];
}
this.test = JSON.stringify(result);
}
},
})
I get a infinite loop error which disappears when I remove this section in getText()
this.after = {
...this.after,
[ani]: this.value[index]
}
This is because there is some reactivity triggered and re-renders the dom, which calls that function, which renders the dom and so on...

How to add value in the tooltip in graph Highcharts?

There is a chart that shows the statistics , production has been sold in any quantity in any city.
enter image description here
I need to add another value , for what amount it was sold . On the second day I'm sitting , I can not figure out how to do it .
here is the link
function ev(str) {
eval(str)
}
function regression(x, y, typ) {
var type = (typ == null) ? 'linear' : typ;
var N = x.length;
var slope;
var intercept;
var SX = 0;
var SY = 0;
var SXX = 0;
var SXY = 0;
var SYY = 0;
var Y = [];
var X = [];
if (type == 'linear') {
X = x;
Y = y;
} else if (type == 'exp' || type == 'exponential') {
for (var i = 0; i < y.length; i++) {
// ignore points <= 0, log undefined.
if (y[i] <= 0) {
N = N - 1;
} else {
X.push(x[i]);
Y.push(Math.log(y[i]));
}
}
}
for (var i = 0; i < N; i++) {
SX = SX + X[i];
SY = SY + Y[i];
SXY = SXY + X[i] * Y[i];
SXX = SXX + X[i] * X[i];
SYY = SYY + Y[i] * Y[i];
}
slope = (N * SXY - SX * SY) / (N * SXX - SX * SX);
intercept = (SY - slope * SX) / N;
return [slope, intercept];
}
function linearRegression(X, Y) {
var ret;
ret = regression(X, Y, 'linear');
return [ret[0], ret[1]];
}
function expRegression(X, Y) {
var ret;
var x = X;
var y = Y;
ret = regression(x, y, 'exp');
var base = Math.exp(ret[0]);
var coeff = Math.exp(ret[1]);
return [base, coeff];
}
function fitData(data, typ) {
var type = (typ == null) ? 'linear' : typ;
var ret;
var res;
var x = [];
var y = [];
var ypred = [];
for (i = 0; i < data.length; i++) {
if (data[i] != null && Object.prototype.toString.call(data[i]) === '[object Array]') {
if (data[i] != null && data[i][0] != null && data[i][1] != null) {
x.push(data[i][0]);
y.push(data[i][1]);
}
} else if (data[i] != null && typeof data[i] === 'number') { //If type of X axis is category
x.push(i);
y.push(data[i]);
} else if (data[i] != null && Object.prototype.toString.call(data[i]) === '[object Object]') {
if (data[i] != null && data[i].x != null && data[i].y != null) {
x.push(data[i].x);
y.push(data[i].y);
}
}
}
if (type == 'linear') {
ret = linearRegression(x, y);
for (var i = 0; i < x.length; i++) {
res = ret[0] * x[i] + ret[1];
ypred.push([x[i], res]);
}
return {
data: ypred,
slope: ret[0],
intercept: ret[1],
y: function(x) {
return (this.slope * x) + this.intercept;
},
x: function(y) {
return (y - this.intercept) / this.slope;
}
};
} else if (type == 'exp' || type == 'exponential') {
ret = expRegression(x, y);
for (var i = 0; i < x.length; i++) {
res = ret[1] * Math.pow(ret[0], x[i]);
ypred.push([x[i], res]);
}
ypred.sort();
return {
data: ypred,
base: ret[0],
coeff: ret[1]
};
}
}
function showGraph(zero) {
zero = typeof zero === "undefined" ? true : zero
sourceData.sort(function(a, b) {
return a[0] - b[0]
})
series = {}
seriesSum = []
dateStart = new Date(sourceData[0][0].toString().replace(/(\d{4})(\d{2})(\d{2})/g, "$1-$2-$3"))
dateEnd = new Date(sourceData[sourceData.length - 1][0].toString().replace(/(\d{4})(\d{2})(\d{2})/g, "$1-$2-$3"))
sourceData.map(function(entry) {
var date = new Date(entry[0].toString().replace(/(\d{4})(\d{2})(\d{2})/g, "$1-$2-$3"))
var microtime = date.getTime()
var dealer = entry[2]
var sum = entry[3]
//int
if (typeof entry[1] === "undefined") {
entry[1] = 0
}
//create group
if (typeof series[dealer] === "undefined") {
series[dealer] = []
}
//find similar day
if (series[dealer].length > 0 && series[dealer][series[dealer].length - 1][0] === microtime) {
series[dealer][series[dealer].length - 1][1] += entry[1]
} else {
series[dealer].push([microtime, entry[1]])
}
//mixed sum
if (seriesSum.length > 0 && seriesSum[seriesSum.length - 1][0] === microtime) {
seriesSum[seriesSum.length - 1][1] += entry[1]
} else {
seriesSum.push([microtime, entry[1]])
}
})
seriesByName = {}
seriesArray = []
_.map(series, function(days, dealer) {
var newDays = []
if (zero) {
for (var dateTemp = new Date(dateStart.getTime()), i = 0; dateTemp < dateEnd; dateTemp.setDate(dateTemp.getDate() + 1)) {
var microtime = dateTemp.getTime()
try {
if (days[i][0] == microtime) {
newDays.push(days[i])
i++
} else {
newDays.push([microtime, 0])
}
} catch (error) {}
}
} else {
newDays = days
}
seriesByName[dealer] = newDays
seriesArray.push({
name: dealer,
data: newDays,
id: dealer,
dataGrouping: {
approximation: "sum",
enabled: true,
forced: true,
units: [
['day', [1]]
],
dateTimeLabelFormats: {
week: ['За 7 дней начиная от "%A, %b %e, %Y"', '%A, %b %e', '-%A, %b %e, %Y']
}
}
})
})
graphContainer = $('#graph').highcharts('StockChart', {
rangeSelector: {
buttons: [, {
type: 'month',
count: 1,
text: '1м'
}, {
type: 'month',
count: 2,
text: '2м'
}, {
type: 'month',
count: 6,
text: '6м'
}, {
type: 'year',
count: 1,
text: '1г'
}, {
type: 'all',
text: 'Весь'
}],
selected: 5
},
tooltip: {
//pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
animation: false,
valueDecimals: 0
},
yAxis: [{
/*labels: {
formatter: function () {
return (this.value > 0 ? ' + ' : '') + this.value + '%';
}
},*/
}, {
labels: {
enabled: false
}
}],
plotOptions: {
series: {
//compare: 'percent'
}
},
series: seriesArray,
legend: {
align: 'center',
verticalAlign: 'bottom',
layout: 'horizontal',
enabled: true
}
})
graph = graphContainer.highcharts()
grouped = false
$('#groupWeek').on('click', function() {
for (var i = 0; i < graph.series.length; i++) {
var serie = graph.series[i]
if (grouped) {
serie.update({
dataGrouping: {
units: [
['day', [1]]
]
}
})
} else {
serie.update({
dataGrouping: {
units: [
['week', [1]]
]
}
})
}
}
grouped = grouped ? false : true
if (grouped) {
$(this).text('Убрать группировку')
} else {
$(this).text('Группировать по неделям')
}
})
percent = false
$('#changePercent').on('click', function() {
for (var i = 0; i < graph.series.length; i++) {
var serie = graph.series[i]
if (percent) {
serie.update({
compare: 'value'
})
} else {
serie.update({
compare: 'percent'
})
}
}
percent = percent ? false : true
if (percent) {
$(this).text('Представить в значениях')
} else {
$(this).text('Представить в процентах')
}
})
trend = false
trendArrayId = []
$('#showTrendline').on('click', function() {
if (grouped) $('#groupWeek').trigger('click')
if (trend) {
while (trendArrayId.length) {
graph.get(trendArrayId.pop()).remove()
}
} else {
var countSeries = graph.series.length
for (var iteratorSeries = 0; iteratorSeries < countSeries; iteratorSeries++) {
var serie = graph.series[iteratorSeries]
if (serie.name !== 'Navigator') {
var data = fitData(seriesByName[serie.name]).data
trendArrayId.push('trend_' + serie.name)
graph.addSeries({
data: data,
color: serie.color,
visible: serie.visible,
linkedTo: serie.name,
name: 'Тенденция (' + serie.name + ')',
id: 'trend_' + serie.name,
dataGrouping: {
approximation: "sum",
enabled: true,
forced: true,
units: [
['day', [1]]
]
}
})
}
}
}
trend = trend ? false : true
if (trend) {
$(this).text('Убрать линии тенденции')
} else {
$(this).text('Вывести линии тенденции')
}
})
}
var sourceData = [
[20140829, 63, "Москва и МО", 100],
[20140930, 1, "Краснодар", 100],
[20140819, 1, "Краснодар", 100],
[20141120, 1, "Краснодар", 100],
[20141010, 1, "Краснодар", 100],
[20141003, 2, "Краснодар", 100],
[20140825, 81, "Москва и МО", 100],
[20140822, 77, "Москва и МО", 100],
[20140918, 90, "Москва и МО", 100],
[20140930, 128, "Москва и МО", 100],
[20141031, 85, "Москва и МО", 100],
[20140827, 105, "Москва и МО", 100],
[20141027, 141, "Москва и МО", 100],
];
if (!oldBrowser) {
$(showGraph(false));
}
One option would be to add a hidden series with your extra value to the chart series: { visible: false ... } and display the value only in the tooltip.
You can override the tooltip formatter using the following property:
http://api.highcharts.com/highcharts#tooltip.formatter
Ensuring you have tooltip: { shared: true ... } setting, you can access all series values for a given x value.
This gives you the flexibility to use a hidden series OR another option would be to have a reference dictionary that you can use to look up your extra value (using the x value as the key) and render it in the tooltip.

Categories