Related
I am a bit out of my comfort zone, since I normally do analytics and not fancy front-ends. However, I would like to have a real-time demo of some of my work, so it becomes easier to understand and not just numbers in a matrix. I have looked around and found something semi-relevant and come this far:
(It has four series like I want to and it iterates - to some degree)
https://jsfiddle.net/023sre9r/
var series1 = this.series[0],
series2 = this.series[1],
series3 = this.series[2],
series4 = this.series[3];
But I am totally lost on how to remove the random number generators without loosing nice things like the number of data points in a view (seems to depend on the for loop?!). Remove the extra title "Values" right next to my real y-axis title. And of cause how to get a new data point from a XML-file every second.
Ideally I want to have an XML-file containing 4 values, which I update approximately every 200ms in MATLAB. And every second I would like my 4 series chart to update. Is it not relatively easy, if you know what you are doing?! :-)
Thanks in advance!
I simplified your example and added clear code showing how to fetch data from server and append it to your chart using series.addPoint method. Also if you want to use XML, just convert it to JS object / JSON.
const randomData = () => [...Array(12)]
.map((u, i) => [new Date().getTime() + i * 1000, Math.random()])
Highcharts.chart('container', {
chart: {
renderTo: 'container',
type: 'spline',
backgroundColor: null,
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load () {
const chart = this
setInterval(() => {
// Fetch example below (working example: https://github.com/stpoa/live-btc-chart/blob/master/app.js)
// window.fetch('https://api.cryptonator.com/api/ticker/btc-usd').then((response) => {
// return response.json()
// }).then((data) => {
// chart.series[0].addPoint({ x: data.timestamp * 1000, y: Number(data.ticker.price) })
// })
chart.series.forEach((series) => series.addPoint([new Date().getTime(), Math.random()], true, true))
}, 3000)
}
}
},
title: {
text: null
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: [{
title: {
text: 'Temperature [°C]',
margin: 30
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
}, {
}],
tooltip: {
formatter: function() {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + Highcharts.numberFormat(this.y, 4);
}
},
legend: {
enabled: true
},
exporting: {
enabled: false
},
rangeSelector: {
enabled: false
},
navigator: {
enabled: false
},
scrollbar: {
enabled: false
},
series: [{
name: 'Setpoint',
data: randomData()
}, {
name: 'Return',
data: randomData()
}, {
name: 'Supply',
data: randomData()
}, {
name: 'Output',
data: randomData()
}]
})
Live example: https://jsfiddle.net/9gw4ttnt/
Working one with external data source: https://jsfiddle.net/111u7nxs/
I am attempting to take the example produced by Highcharts here http://www.highcharts.com/maps/demo/color-axis and substitute the data loaded by the $.getJson with a local JSON file called 'testdata1.json'.
The code I've modified below produces no errors yet the map does not render. I think it's because the testdata1.json is loaded late, after the javascript is executed. If so, is there a better way I should be doing this -- perhaps waiting for the data to load before executing the JS file? I attempted to do this by placing a
$(document).ready(
in front of the function but it didn't work. Any thoughts are greatly appreciated, I think it's something relatively minor that is just escaping me.
Thank you.
$(function () {
// Map options
var options = {
chart : {
renderTo: '#map',
borderWidth : 1
},
title : {
text : 'US population density (/km²)'
},
legend: {
layout: 'horizontal',
borderWidth: 0,
backgroundColor: 'rgba(255,255,255,0.85)',
floating: true,
verticalAlign: 'top',
y: 25
},
mapNavigation: {
enabled: true
},
colorAxis: {
min: 1,
type: 'logarithmic',
minColor: '#EEEEFF',
maxColor: '#000022',
stops: [
[0, '#EFEFFF'],
[0.67, '#4444FF'],
[1, '#000022']
]
},
series : [{
animation: {
duration: 1000
},
mapData: Highcharts.maps['countries/us/us-all'],
joinBy: ['postal-code', 'code'],
dataLabels: {
enabled: true,
color: 'white',
format: '{point.code}'
},
name: 'Population density',
tooltip: {
pointFormat: '{point.code}: {point.value}/km²'
}
}]
};
$.getJSON('static/data/testdata1.json', function (data) {
// Make codes uppercase to match the map data
$.each(data, function () {
this.code = this.code.toUpperCase();
});
options.series.data= data;
var chart = new Highcharts.Chart(options)
});
});
You have three problems. Here's a fiddle based on their sample that uses your approach, but still uses their data, and works: http://jsfiddle.net/g29k24vw/1/
Here are the important parts:
chart : {
renderTo: 'container',
borderWidth : 1,
type: 'map'
},
And:
$.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename=us-population-density.json&callback=?', function (data) {
// Make codes uppercase to match the map data
$.each(data, function () {
this.code = this.code.toUpperCase();
});
options.series[0].data= data;
var chart = new Highcharts.Chart(options);
});
Note the differences here:
You need to specify the chart type in options if you're going to instantiate the chart object directly instead of using the jQuery helper.
renderTo doesn't want a hash in front of the element name.
options.series[0].data, not options.series.data...series is actually an array of objects.
I have a drilldown map on my page which I would like to optimise.
Right now I am loading every "drilldown" map even if it is not clicked.
Here is an example that shows how the data is load if the state is clicked.I would like to achieve that.
But this is my code and as you can see, I am loading all drilldown jsons even if the map is not clicked. In my example I have only 2 drilldown option, but in my real life problem I have it like 15 so it really slows down a little bit everything.
So this is my code:
// get main map
$.getJSON('json/generate_json_main_map.php', function(data) {
// get region 1 map
$.getJSON('json/generate_json_region_1.php', function(first_region) {
// get region 2 map
$.getJSON('json/generate_json_region_2.php', function(second_region) {
// Initiate the chart
$('#interactive').highcharts('Map', {
title: {
text: ''
},
colorAxis: {
min: 1,
max: 10,
minColor: '#8cbdee',
maxColor: '#1162B3',
type: 'logarithmic'
},
series: [{
data: data,
"type": 'map',
name: st_ponudb,
animation: {
duration: 1000
},
states: {
//highlight barva
hover: {
color: '#dd4814'
}
}
}],
drilldown: {
drillUpButton: {
relativeTo: 'plotBox',
position: {
x: 0,
y: 0
},
theme: {
fill: 'white',
'stroke-width': 0,
stroke: 'white',
r: 0,
states: {
hover: {
fill: 'white'
},
select: {
stroke: 'white',
fill: 'white'
}
}
}
},
series: [{
id: 'a',
name: 'First',
joinBy: ['hc-key', 'code'],
type: 'map',
data: first_region,
point: {
events: {
click: function() {
var key = this.key;
location.href = key;
}
}
}
}, {
id: 'b',
name: 'Second',
joinBy: ['hc-key', 'code'],
type: 'map',
data: second_region,
point: {
events: {
click: function() {
var key = this.key;
location.href = key;
}
}
}
}]
}
});
});
});
});
JSON from generate_json_main_map.php:
[{"drilldown":"a","name":"region 1","value":"1","path":""},{"drilldown":"b","name":"region 2","value":"2","path":""}]
JSON from generate_json_region_1.php:
[{"name":"Place 1","key":"place.php?id=1","value":"1","path":""},{"name":"Place 2","key":"place.php?id=2","value":"2","path":""}]
This is my attempt to make ajax calls load in parallel, but the map is not loading, I get just the coloraxis.
$(function() {
$.when($.getJSON('json/generate_json_main_map.php'), $.getJSON('json/generate_json_region_1.php'), $.getJSON('json/generate_json_region_2.php')).done(function(data,first_region,second_region){
$('#interactive').highcharts('Map', {
title: {
text: ''
},
colorAxis: {
min: 1,
max: 10,
minColor: '#8cbdee',
maxColor: '#1162B3',
type: 'logarithmic'
},
series: [{
data: data,
"type": 'map',
name: st_ponudb,
animation: {
duration: 1000
},
states: {
hover: {
color: '#dd4814'
}
}
}],
drilldown: {
drillUpButton: {
relativeTo: 'plotBox',
position: {
x: 0,
y: 0
},
theme: {
fill: 'white',
'stroke-width': 0,
stroke: 'white',
r: 0,
states: {
hover: {
fill: 'white'
},
select: {
stroke: 'white',
fill: 'white'
}
}
}
},
series: [{
id: 'a',
name: 'First',
joinBy: ['hc-key', 'code'],
type: 'map',
data: first_region,
point: {
events: {
click: function() {
var key = this.key;
location.href = key;
}
}
}
}, {
id: 'b',
name: 'Second',
joinBy: ['hc-key', 'code'],
type: 'map',
data: second_region,
point: {
events: {
click: function() {
var key = this.key;
location.href = key;
}
}
}
}]
}
});
});
});
I can see that the jsons are loaded and there is no JS error shown by firebug.
If you want to load on click, you need to call the state data on click_event (and not at startup).
Just like your JSFiddle example:
chart : {
events: {
drilldown: function (e) {
// Load you data
// show it with chart.addSeriesAsDrilldown(e.point, {...});
}
}
}
Or as #Whymarrh suggests, you can load them all in parallel (instead of one after the other) and once they are all retrieved, compute your map.
See https://lostechies.com/joshuaflanagan/2011/10/20/coordinating-multiple-ajax-requests-with-jquery-when/ for example on how to execute a code after all ajax calls have completed.
When you load your map data as you did, in the following manner:
$.when(
$.getJSON('json/generate_json_main_map.php'),
$.getJSON('json/generate_json_region_1.php'),
$.getJSON('json/generate_json_region_2.php')
).done(...);
The effect is this - when any of the three requests fail, all promises will be rejected and ultimately, your map never gets to be initialised.
A better approach could be to request all data independently, and the outcomes would be handled as follows:
If the request for the main data fails, abort the other requests unconditionally (there would be no need for a drill down if the primary data is non-existent).
If request for main data succeeds, you may go on and initialise the map as data becomes available. The request for drill down data may or may not succeed though (but half bread is better than none?). Assuming everything goes well, then in the event that user initiates a drill down action, you show a loading message and ultimately add the drill down series when it becomes available.
Here's an implementation of the method I offered:
$(function () {
// immediately trigger requests for data
var loadMainData = $.getJSON("json/generate_json_main_map.php");
var loadRegionData = {
"region-1-name": $.getJSON("json/generate_json_region_1.php"),
"region-2-name": $.getJSON("json/generate_json_region_2.php")
};
// region drilldown options
var regionalSeriesOptions = {
"region-1-name": {
id: 'a',
name: 'First',
joinBy: ['hc-key', 'code'],
type: 'map',
point: {
events: {
click: function () {
var key = this.key;
location.href = key;
}
}
}
},
"region-2-name": {
id: 'b',
name: 'Second',
joinBy: ['hc-key', 'code'],
type: 'map',
point: {
events: {
click: function () {
var key = this.key;
location.href = key;
}
}
}
},
// ...
"region-(n-1)-name": {
// series options for region 'n-1'
},
"region-n-name": {
// series options for region 'n'
},
"region-(n+1)-name": {
// series options for region 'n+1'
}
};
// main options
var options = {
title: {
text: ""
},
series: [{
type: "map",
name: st_ponudb,
animation: {
duration: 1000
},
states: {
hover: {
color: "#dd4814"
}
}
}],
events: {
drilldown: function (e) {
var regionName, request, series, chart;
if (e.seriesOptions) {
// drilldown data is already loaded for the currently
// selected region, so simply return
return;
}
regionName = e.point.name;
request = loadRegionData[regionName];
series = regionalSeriesOptions[regionName];
chart = this;
chart.showLoading("Loading data, please wait...");
request.done(function (data) {
// series data has been loaded successfully
series.data = data;
chart.addSeriesAsDrilldown(e.point, series);
});
request.fail(function () {
if (loadMainData.readyState !== 4) {
// do you really want to cancel main request
// due to lack of drilldown data?
// Maybe half bread is better than none??
loadMainData.abort();
}
});
// whether success or fail, hide the loading UX notification
request.always(chart.hideLoading);
}
},
colorAxis: {
min: 1,
max: 10,
minColor: '#8cbdee',
maxColor: '#1162B3',
type: 'logarithmic'
},
drilldown: {
drillUpButton: {
relativeTo: 'plotBox',
position: {
x: 0,
y: 0
},
theme: {
fill: 'white',
'stroke-width': 0,
stroke: 'white',
r: 0,
states: {
hover: {
fill: 'white'
},
select: {
stroke: 'white',
fill: 'white'
}
}
}
},
series: []
}
};
loadMainData.done(function (data) {
options.series[0].data = data;
$("#interactive").highcharts("Map", options);
}).fail(function () {
Object.keys(loadRegionData).forEach(function (name) {
// if primary data can't be fetched,
// then there's no need for auxilliary data
loadRegionData[name].abort();
});
});
});
Since I don't know every detail of your code, it's left for you to find a way to fit it into your solution.
I'm attempting to combine a couple of different chart demos from Highcharts.
My examples are: Data classes and popup and Small US with data labels
I want the map from the first with the popup feature of the second. I need to connect the map to my own google spreadsheet but for now I'm just trying to get the data from the first example to work.
This is what I have so far but can't seem to get any data in the map. I thought I had a joinBy problem, and I may still, but when I set joinBy to null I thought "the map items are joined by their position in the array", yet nothing happened.
https://jsfiddle.net/9eq6mydv/
$(function () {
// Load the data from a Google Spreadsheet
// https://docs.google.com/a/highsoft.com/spreadsheet/pub?hl=en_GB&hl=en_GB&key=0AoIaUO7wH1HwdFJHaFI4eUJDYlVna3k5TlpuXzZubHc&output=html
Highcharts.data({
googleSpreadsheetKey: '0AoIaUO7wH1HwdDFXSlpjN2J4aGg5MkVHWVhsYmtyVWc',
googleSpreadsheetWorksheet: 1,
// custom handler for columns
parsed: function (columns) {
// Make the columns easier to read
var keys = columns[0],
names = columns[1],
percent = columns[10],
// Initiate the chart
options = {
chart : {
renderTo: 'container',
type: 'map',
borderWidth : 1
},
title : {
text : 'US presidential election 2008 result'
},
subtitle: {
text: 'Source: <a href="http://en.wikipedia.org/wiki/United_States_presidential_election,' +
'_2008#Election_results">Wikipedia</a>'
},
mapNavigation: {
enabled: true,
enableButtons: false
},
legend: {
align: 'right',
verticalAlign: 'top',
x: -100,
y: 70,
floating: true,
layout: 'vertical',
valueDecimals: 0,
backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColor) || 'rgba(255, 255, 255, 0.85)'
},
colorAxis: {
dataClasses: [{
from: -100,
to: 0,
color: '#C40401',
name: 'McCain'
}, {
from: 0,
to: 100,
color: '#0200D0',
name: 'Obama'
}]
},
series : [{
data : data,
dataLabels: {
enabled: true,
color: '#FFFFFF',
format: '{point.code}',
style: {
textTransform: 'uppercase'
}
},
mapData: Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small']),
joinBy: keys,
name: 'Democrats margin',
point: {
events: {
click: pointClick
}
},
tooltip: {
ySuffix: ' %'
},
cursor: 'pointer'
}, {
type: 'mapline',
data: Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small'], 'mapline'),
color: 'silver'
}]
};
/**
* Event handler for clicking points. Use jQuery UI to pop up
* a pie chart showing the details for each state.
*/
function pointClick() {
var row = this.options.row,
$div = $('<div></div>')
.dialog({
title: this.name,
width: 400,
height: 300
});
window.chart = new Highcharts.Chart({
chart: {
renderTo: $div[0],
type: 'pie',
width: 370,
height: 240
},
title: {
text: null
},
series: [{
name: 'Votes',
data: [{
name: 'Obama',
color: '#0200D0',
y: parseInt(columns[3][row], 10)
}, {
name: 'McCain',
color: '#C40401',
y: parseInt(columns[4][row], 10)
}],
dataLabels: {
format: '<b>{point.name}</b> {point.percentage:.1f}%'
}
}]
});
}
// Read the columns into the data array
var data = [];
$.each(keys, function (i, key) {
data.push({
key: key,//.toUpperCase(),
value: parseFloat(percent[i]),
name: names,
row: i
});
});
// Initiate the chart
window.chart = new Highcharts.Map(options);
},
error: function () {
$('#container').html('<div class="loading">' +
'<i class="icon-frown icon-large"></i> ' +
'Error loading data from Google Spreadsheets' +
'</div>');
}
});
});
UPDATE:
I wanted to share with everyone my final solution. Although Ondkloss did a magnificent job answering my question the popup feature still didn't work and this is because I forgot to include the jQuery for the .dialog call. Once I included that I had an empty popup with a highchart error 17, this is because the highmaps.js code doesn't include the pie chart class. So I had to add the highcharts.js code and include map.js module afterward. You can see my final jsfiddle here.
Thanks again to Ondkloss for the excellent answer!
The problem here mostly comes down to the use of joinBy. Also to correct it there are some required changes to your data and mapData.
Currently your joinBy is an array of strings, like ["al", "ak", ...]. This is quite simply not an accepted format of the joinBy option. You can read up on the details in the API documentation, but the simplest approach is to have a attribute in common in data and mapData and then supply a string in joinBy which then joins those two arrays by that attribute. For example:
series : [{
data : data,
mapData: mapData,
joinBy: "hc-key",
]
Here the "hc-key" attribute must exist in both data and mapData.
Here's how I'd create the data variable in your code:
var data = [];
$.each(keys, function (i, key) {
if(i != 0)
data.push({
"hc-key": "us-"+key,
code: key.toUpperCase(),
value: parseFloat(percent[i]),
name: names[i],
row: i
});
});
This skips the first key, which is just "Key" (the title of the column). Here we make the "hc-key" fit the format of the "hc-key" in our map data. An example would be "us-al". The rest is just metadata that will be joined in. Note that you were referencing your data in the options prior to filling it with data, so this has to be moved prior to this.
This is how I'd create the mapData variable in your code:
var mapData = Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small']);
// Process mapdata
$.each(mapData, function () {
var path = this.path,
copy = { path: path };
// This point has a square legend to the right
if (path[1] === 9727) {
// Identify the box
Highcharts.seriesTypes.map.prototype.getBox.call(0, [copy]);
// Place the center of the data label in the center of the point legend box
this.middleX = ((path[1] + path[4]) / 2 - copy._minX) / (copy._maxX - copy._minX);
this.middleY = ((path[2] + path[7]) / 2 - copy._minY) / (copy._maxY - copy._minY);
}
// Tag it for joining
this.ucName = this.name.toUpperCase();
});
The first part is your "standard map data". The rest is to correctly center the labels for the popout states, and gotten directly from the example.
And voila, see this JSFiddle demonstration to witness your map in action.
I suggest doing some console.log-ing to see how data and mapData have the hc-key in common and that leads to the joining of the data in the series.
When using JSON with a pyramid chart in Highcharts, I don't get any errors in the console log but no data is displayed.
I have charts.php which outcome is this:
{"success":1,"data":[{"name":"John Spoon","data":300}, {"name":"Dave Jones","data":200},{"name":"Other","data":500}]}
This is what I've tried, which returns no data.
<div id="chart" style=
"height:600px;width:100%;"></div>
<script>
$(function () {
$("#chart").html("Loading Activity Log Graph...");
var options = {
chart: {
type: 'pyramid',
renderTo: 'chart',
marginRight: 100
},
title: {
text: 'Activity',
x: -50
},
plotOptions: {
series: {
dataLabels: {
enabled: true,
format: '<b>{point.name}</b> ({point.y:,.0f})',
color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black',
softConnector: true
}
}
},
legend: {
enabled: false
},
name: 'Activity Count',
series: [{}]
};
$.ajax({
url: "charts.php",
type:'post',
dataType: "json",
success: function(data){
options.series = data.data;
var chart = new Highcharts.Chart(options);
}
});
});
</script>
This is my desired outcome, showing without the use of JSON:
http://jsfiddle.net/0dyy44hz/
What do I need to change for it to show data?
Looking at the pyramid example, the data must be in the form of a single series with each data point as an array of [name, value]. You have two options, change your PHP to output the correct format or modify the PHP output in javascript. Since you didn't post your PHP code, I'll do the later:
var data = {"success":1,"data":[{"name":"John Spoon","data":300}, {"name":"Dave Jones","data":200},{"name":"Other","data":500}]};
options.series = [{
data: $.map(data.data, function(i){ // loop outputted data
return [[i.name, i.data]]; // coerce into an array of [name,value]
})
}];
Here's a working example.