I'm working with plotly.js to make a simple world map with some lines on it. The data is simply a csv of latitudes and longitudes, count is nothing important.
Plotly.d3.csv('edgel_latlon.csv', function(err, rows){
function unpack(rows, key) {
return rows.map(function(row) { return row[key]; });}
function getMaxOfArray(numArray) {
return Math.max.apply(null, numArray);
}
var data = [];
var count = unpack(rows, 'To');
var startLongitude = unpack(rows, 'lon1');
var endLongitude = unpack(rows, 'lon2');
var startLat = unpack(rows, 'lat1');
var endLat = unpack(rows, 'lat2');
for ( var i = 0 ; i < count.length; i++ ) {
var opacityValue = count[i]/getMaxOfArray(count);
var result = {
type: 'scattergeo',
locationmode: 'world',
lon: [ startLongitude[i] , endLongitude[i] ],
lat: [ startLat[i] , endLat[i] ],
mode: 'lines',
line: {
width: .5,
color: 'red'
},
opacity: 1
};
data.push(result);
};
var layout = {
title: 'Title',
showlegend: false,
geo:{
resolution:1000,
scope: 'world',
projection: {
type: 'mercator'
},
showland: true,
showcountries: true,
landcolor: 'rgb(243,243,243)',
countrycolor: 'rgb(0,0,0)'
}
};
Plotly.plot(myDiv, data, layout, {showLink: false});
});
When I hover over a line on the map I would like the line to change from the color red to blue, but have no idea how to do this with plotly. Either you can't use the standard D3 methods to access the line or I'm missing something obvious. Thanks!
Related
I'm trying to add a new point (to series data) on spline chart after clicking in the place where I'm clicked on the line. But point click event doesn't return xAxis, yAxis (only in pixels). I decide to calculate the difference between point pixels position and click, but point adds not on the click place. What I'm doing wrong? How to handle this?
My JS
var setDragStatus = function (status) {
document.getElementById('dragstatus').innerHTML = status;
};
Highcharts.chart('container', {
title: {
text: 'Spline Drag&Drop'
},
plotOptions: {
series: {
turboThreshold: 4,
minPointLength: 5,
dragDrop: {
draggableY: true,
dragMaxY: 1,
},
point: {
events: {
click: function (e) {
let pointPlotX = e.point.plotX
let pointPlotY = e.point.plotY
let pointX = e.point.x
let pointY = e.point.y
let clickX = e.chartX
let clickY = e.chartY
let pointDiffX = clickX / pointPlotX
let pointDiffY = clickY / pointPlotY
let newPointX = pointX * pointDiffX
let newPointY = pointDiffY * pointY
this.series.addPoint([newPointX, newPointY])
}
}
},
}
},
xAxis: {
reversed: false,
showFirstLabel: false,
showLastLabel: true
},
series: [
{
name: 'spline top',
data: [0, 0.3, 0.6, 1],
type: 'spline'
}
]
}
);
Result - https://jsfiddle.net/antiaf/1hfuyjbr/
To calculate x and y values you can use toValue Axis method:
plotOptions: {
series: {
...,
point: {
events: {
click: function(e) {
let series = this.series,
yAxis = series.yAxis,
xAxis = series.xAxis,
newPointX = xAxis.toValue(e.chartX),
newPointY = yAxis.toValue(e.chartY);
this.series.addPoint([newPointX, newPointY])
}
}
}
}
}
Live demo: https://jsfiddle.net/BlackLabel/hg81o4ej/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Axis#toValue
I am trying to draw a graph with the help of high chart and also using load event I am trying to add values after each 1 second to the graph.
In this graph I also want to show axis as Mb,Kb,,Gb data. So I am writing a function to return the byte values as Mb,Kb,Gb (for both series and tooltip)
This is my code :
// highchart options :
var series1, series2 ;
var chart = {
type: 'bar',
events: {
load: function () {
// set up the updating of the chart each second
series1 = this.series[0];
series2 = this.series[1];
setInterval(function () {
add_function();
}, 1000);//call function each 1 second
}
}
};
var tooltip = {
enabled: true,
formatter: function() { return fbytes(this.y,2);}
};
var plotOptions = {
bar: {
},
series: {
dataLabels: {
enabled: true,
formatter: function() { return fbytes(this.y,2);},
inside: true,
style: {fontWeight: 'number'}
},
pointPadding: 0,
pointWidth:38
},
column : {
grouping: true
}
};
series= [
{
name: 'old',
color: '#f9a80e',
data: [,]
},
{
name: 'new',
color: '#89897f',
data: [,]
}
];
and the load event function is :
Array.max = function (array) {
return Math.max.apply(Math, array);
};
Array.min = function (array) {
return Math.min.apply(Math, array);
};
add_function()
{
var arr[];
//getting array values here
var min_value = Array.min(arr);
var max_value = Array.max(arr);
var chart2 = $('#container').highcharts();
chart2.yAxis[0].update({max:max_value, min: 0}, true);
series1.setData([arr[0],arr[2]], true, true);
series2.setData([arr[1],arr[3]], true, true);
}
and the conversion function :
function fbytes(bytes, precision) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
var posttxt = 0;
if (bytes == 0) return '0 Bytes';
if (bytes < 1024) {
return Number(bytes) + " " + sizes[posttxt];
}
while( bytes >= 1024 ) {
posttxt++;
bytes = bytes / 1024;
}
return Math.round(bytes.toPrecision(precision)) + " " + sizes[posttxt];
}
my logic : i got some array values randomly and i am displaying this data on the graph .
problem facing : I didn't get this.y value inside series . When i print this value inside
series: {
dataLabels: {
enabled: true,
formatter: function() { return fbytes(this.y,2);},
inside: true,
style: {fontWeight: 'number'}
},
I am getting this.y = undefined . What is happening ?
Any mistake in the code ? Any suggestions ?
I created demo using your code and modified add_function() a little bit. Did you mean something like this?
function add_function(series1, series2) {
var chart2 = $('#container').highcharts(),
increment = 1024,
min_value,
max_value,
newVal1 = [],
newVal2 = [];
if (!series1.data.length && !series2.data.length) {
var arr = [512, 128, 1024, 0];
min_value = Array.min(arr);
max_value = Array.max(arr);
newVal1 = [arr[0], arr[2]];
newVal2 = [arr[1], arr[3]];
} else {
series1.yData.forEach(function(sEl, sInx) {
newVal1.push(sEl + increment);
});
series2.yData.forEach(function(sEl, sInx) {
newVal2.push(sEl + increment);
});
max_value = Array.max(newVal1.concat(newVal2));
}
chart2.yAxis[0].update({
max: max_value,
min: 0
}, true);
series1.setData(newVal1, true, true);
series2.setData(newVal2, true, true);
}
Example:
http://jsfiddle.net/js3g311q/
I am having trouble creating a highcharts box-plot graph, I have all the data in the correct format i.e. min, lower quartile, median, upper quartile and max.
I can display the categories but I cannot get it to display the data.
This is my code:
function BoxPlot() {
//ViewBag Variables
var Till = #Html.Raw(Json.Encode(#ViewBag.Tills));
var Per20 = #Html.Raw(Json.Encode(#ViewBag.P20));
var Per30 = #Html.Raw(Json.Encode(#ViewBag.P30));
var Per40 = #Html.Raw(Json.Encode(#ViewBag.P40));
var Per50 = #Html.Raw(Json.Encode(#ViewBag.P50));
var Per60 = #Html.Raw(Json.Encode(#ViewBag.P60));
var Per70 = #Html.Raw(Json.Encode(#ViewBag.P70));
var Per80 = #Html.Raw(Json.Encode(#ViewBag.P80));
var Per90 = #Html.Raw(Json.Encode(#ViewBag.P90));
//Combine the till no with its data
var final = [];
for(var i=0; i < Till.length; i++) {
final.push({
name: Till[i],
p20: Per20[i],
p30: Per30[i],
p40: Per40[i],
p50: Per50[i],
p60: Per60[i],
p70: Per70[i],
p80: Per80[i],
p90: Per90[i],
});
}
console.log(final)
//get the data into the correct format for box plot
var formated = [];
for(var i=0; i < final.length; i++) {
formated.push({
a: final[i].p20,
b: ((final[i].p30 + final[i].p40) / 2),
c: ((final[i].p50 + final[i].p60) / 2),
d: ((final[i].p70 + final[i].p80) / 2),
e: final[i].p90,
});
}
console.log(formated)
//graph the data
$('#container').highcharts({
chart: {
type: 'boxplot'
},
title: {
text: 'Highcharts Box Plot'
},
legend: {
enabled: true
},
xAxis: {
categories: Till,
title: {
text: 'Till No.'
}
},
yAxis: {
title: {
text: 'Value'
}
},
series: [{
name: 'Values',
data: formated,
tooltip: {
headerFormat: '<em>Till No. {point.key}</em><br/>'
}
}]
});
}
This is an example of the formatted array and the data it contains:
This is how the graph currently looks, you can see the categories array is working but it is not showing the data:
I was able to solve this by changing how I gathered the data, Im not sure if the box plot is case sensitive but by changing the variable names the data displayed
This is the whole code I am using:
function BoxPlot() {
//ViewBag Variables
var Till = #Html.Raw(Json.Encode(#ViewBag.Tills));
var Per20 = #Html.Raw(Json.Encode(#ViewBag.P20));
var Per30 = #Html.Raw(Json.Encode(#ViewBag.P30));
var Per40 = #Html.Raw(Json.Encode(#ViewBag.P40));
var Per50 = #Html.Raw(Json.Encode(#ViewBag.P50));
var Per60 = #Html.Raw(Json.Encode(#ViewBag.P60));
var Per70 = #Html.Raw(Json.Encode(#ViewBag.P70));
var Per80 = #Html.Raw(Json.Encode(#ViewBag.P80));
var Per90 = #Html.Raw(Json.Encode(#ViewBag.P90));
var heading = #Html.Raw(Json.Encode(#ViewBag.QueryTitle));
//Combine the till no with its data
var final = [];
for(var i=0; i < Till.length; i++) {
final.push({
name: Till[i],
p20: Per20[i],
p30: Per30[i],
p40: Per40[i],
p50: Per50[i],
p60: Per60[i],
p70: Per70[i],
p80: Per80[i],
p90: Per90[i],
});
}
console.log(final)
//get the data into the correct format for box plot
var formated = [];
for(var i=0; i < final.length; i++) {
formated.push({
low: final[i].p20,
q1: ((final[i].p30 + final[i].p40) / 2),
median: ((final[i].p50 + final[i].p60) / 2),
q3: ((final[i].p70 + final[i].p80) / 2),
high: final[i].p90,
});
}
console.log(formated)
var boxData = [];
//boxData.push(formated);
//console.log(boxData);
//graph the data
$('#container').highcharts({
chart: {
type: 'boxplot'
},
title: {
text: heading
},
legend: {
enabled: true
},
xAxis: {
categories: Till,
title: {
text: 'Till No.'
}
},
yAxis: {
title: {
text: 'Distribution'
}
},
series: [{
name: 'Tills',
data:
formated
}]
});
}
I have the following HTTP request that i use to fill out my chart:
$scope.series = ['Moduler tager', 'Gns.score'];
$scope.activity_data = [];
$scope.activity_ticks = [];
var tmp_data = [];
$scope.bar = [];
$scope.line = [];
$http.get(api.getUrl('findSelfActivityByDivisions', null))
.success(function (response) {
response.forEach(function(y){
var i = 0;
var log_date = y.date.substr(0, y.date.indexOf('T'));
var date = new Date(log_date);
var logg_date = moment(date).fromNow();
var indexOf = tmp_data.indexOf(logg_date);
var found = false;
var index = 0;
if(tmp_data.length > 0){
tmp_data.forEach(function(current_data){
if(current_data[0] == logg_date){
found = true;
}
if(!found){
index++;
}
})
}
if(found){
var tmp = tmp_data[index];
tmp[1] = tmp[1] + y.num_modules;
tmp[2] = tmp[2] + y.num_score_modules;
tmp[3] = tmp[3] + y.sum_score;
tmp_data[index] = tmp;
}
else
{
var tmp = [logg_date, y.num_modules, y.num_score_modules, y.sum_score];
tmp_data.push(tmp);
}
})
var line = [];
var bar = [];
tmp_data.forEach(function(data){
$scope.activity_ticks.push(data[0])
line.push(data[1]);
var avg_score = data[3] / data[2];
if(isNaN(avg_score)){
avg_score = 0;
}
bar.push(avg_score);
});
$scope.line = line;
$scope.bar = bar;
});
Now then i have the following chart config:
$scope.chartConfig = {
options: {
chart: {
type: 'areaspline'
}
},
series: [{
data: $scope.bar,
type: 'column'
},{
data: $scope.line,
type: 'line'
}],
xAxis: {
categories: $scope.activity_ticks
},
title: {
text: 'Hello'
},
loading: false
}
Sadly none of the graphs are showing (im guessing it has something to do with the date comming after the load)
Can anyone help me out?
Your $scope.chartConfig is likely firing before the success callback of your $http.get(api.getUrl('findSelfActivityByDivisions', null)) completes. I am assuming $scope.chartConfig is located in a controller. Try placing a $watchGroup on the values then apply your chart rendering logic once those values resolve. An example may include
Note that $watchGroup is found within Angular as of 1.3
$scope.$watchGroup(['line', 'bar'], function(newValues, oldValues) {
// newValues[0] --> $scope.line
// newValues[1] --> $scope.bar
if(newValues !== oldValues) {
$scope.chartConfig = {
options: {
chart: {
type: 'areaspline'
}
},
series: [{
data: $scope.bar,
type: 'column'
},{
data: $scope.line,
type: 'line'
}],
xAxis: {
categories: $scope.activity_ticks
},
title: {
text: 'Hello'
},
loading: false
}
}
});
I'm trying to use the exporting option to add a button which is then used to switch between a line chart with the real point and another with the cumulative sum of them.
I'm using the following code:
$(function () {
$('#container').highcharts({
chart: {
type: 'line'
},
xAxis: {
tickPixelInterval: 200,
categories: jsonResponse["Date"]
},
series: {
data: jsonResponse["values"]
},
exporting: {
buttons: {
'myButton': {
_id: 'myButton',
symbol: 'diamond',
text: 'Cumulative',
x: -62,
symbolFill: '#B5C9DF',
hoverSymbolFill: '#779ABF',
onclick: function() {
if(!cumulative){
this.series[0].setData = cumcum(jsonResponse["values"]);
alert(this.series[1].setData);
cumulative = true;
} else {
this.series[0].setData = jsonResponse["values"];
cumulative = false;
alert(this.series[1].setData);
}
},
_titleKey: "myButtonTitle"
}
}
}
});
});
function cumcum(data){
var res = new Array();
res[0] = data[0];
for(var i=1; i<data.length; i++) {
res[i] = res[i-1] + data[i];
}
return res;
}
From the alert I can see that the data are correctly calculated but the plot stays the same.
I also tried series[0].yData and series[0].processedYData
setData is a function, you have to call it like:
this.series[0].setData(cumcum(jsonResponse["values"])
See API http://api.highcharts.com/highstock#Series for more information.