Related
I'm using html, css and javascript for the project. The server is set up locally including node, express and highcharts.
In my html i have a div container, that i'm referencing in my highcharts function. The data i'm using in the highchart come from a csv file. I wrote a simple getData function, that parses the data into an array. And at the top of the highchartsfunction, i'm using await getData, so the chart can only be rendered after getting the data for it. All functions are async. On that one page i'm having seven charts, all constructed using the syntax following below, each with its own getData function and div container.
Two problems:
Problem No.1:
The charts are not getting displayed until i refresh the page and often times i even have to refresh it several times, till every chart is getting shown at a time (Like 20 times). The functions are all executed, i checked that in the console.
Problem No.2:
I would like to write this dynamically, as i want to have multiple pages on my website, each using its own csv file, but basically the same code.
*Edit:
From time to time i receive: "Error: Highcharts error #13: www.highcharts.com/errors/13/" in the console, but as i keep refreshing it changes the line/js it is referencing to. It always is the part of the highcharts function, in which i give it its container Highcharts.chart('hammerchart', {. I checked this error and what it sais is basically, that the script can't find a container with the #id. Which i don't understand as im using "DOMContentLoaded". I tried the "load" event instead, which solved the error #13, but ended up in no chart getting displayed at all without any error message.
*Edit2:
Until now i only used firefox to view my website. I checked Chrome aswell and here i don't have any error message, but it also never shows any of the charts aswell at all.
If someone could help me out here, i would be really happy, as i tried to fix this for days now with no success. I'm also open for suggestions, regarding the handling of my data. Thank you veeeery much in advance!!
Code:
CSV Data sample
2015,2.5,7.2,1.6,6.5,2.2
2016,3.5,7.6,2.6,9.3,1.2
2017,2.5,7.2,1.6,6.5,2.2
2018,3.5,7.6,2.6,9.3,1.2
2019,2.5,7.2,1.6,6.5,2.2
HTML
<div id="hammerchart">
</div>
CSS
#hammerchart{
width:49%;
height: 500px;
margin:0.5%;
margin-top:50px;
flex: 1 1 600px;
}
Javascript - first declaration of the arrays, then my parsing function, splitting the csv by linebreaks and commas. Then i'm using parseFloat(), so that i don't have strings in it. In the highcharts function i'm using await getData and DOMContentloaded, to make sure both html and my data are ready.
const years = [];
const columntwos = [];
const columnthrees = [];
const columnfours = [];
const columnfives = [];
const columnsixs = [];
async function getDataone() {
const response = await fetch('/javascripts/hammerchart.CSV');
const data = await response.text();
const table = data.split('\n');
table.forEach(row => {
const columns = row.split(',');
const year = columns[0];
years.push(year);
const columntwo = columns[1];
columntwos.push(parseFloat(columntwo));
const columnthree = columns[2];
columnthrees.push(parseFloat(columnthree));
const columnfour = columns[3];
columnfours.push(parseFloat(columnfour));
const columnfive = columns[4];
columnfives.push(parseFloat(columnfive));
const columnsix = columns[5];
columnsixs.push(parseFloat(columnsix));
});
};
async function hammerchart() {
await getDataone();
document.addEventListener('DOMContentLoaded', () => {
Highcharts.chart('hammerchart', {
chart: {
zoomType: 'xy'
},
title: {
text: 'Data'
},
subtitle: {
text: ''
},
credits: {
text: 'highcharts',
},
xAxis: [{
categories: years,
plotBands: [{ // visualize
from: 4.5,
to: 6,
color: 'rgba(68, 170, 213, .2)'
}],
crosshair: true
}],
yAxis: [{ // Primary yAxis
labels: {
format: '{value}$',
style: {
color: Highcharts.getOptions().colors[1]
}
},
title: {
text: 'Amount in $',
style: {
color: Highcharts.getOptions().colors[1]
}
}
}, { // Secondary yAxis
title: {
text: 'Ratio in %',
style: {
color: Highcharts.getOptions().colors[0]
}
},
labels: {
format: '{value} %',
style: {
color: Highcharts.getOptions().colors[0]
}
},
opposite: true
}],
tooltip: {
shared: true
},
legend: {
layout: 'vertical',
align: 'left',
x: 120,
verticalAlign: 'top',
y: 100,
floating: true,
backgroundColor:
Highcharts.defaultOptions.legend.backgroundColor || // theme
'rgba(255,255,255,0.25)'
},
series: [{
name: 'columntwo',
type: 'areaspline',
data: columntwos,
tooltip: {
valueSuffix: '$'
}
}, {
name: 'columnthree',
type: 'areaspline',
data: columnthrees,
tooltip: {
valueSuffix: '$'
}
}, {
name: 'columnfour',
type: 'areaspline',
data: columnfours,
tooltip: {
valueSuffix: '$'
}
}, {
name: 'columnfive',
type: 'column',
data: columnfives,
tooltip: {
valueSuffix: '$'
}
}, {
name: 'columnsix',
type: 'spline',
yAxis: 1,
data: columnsixs,
tooltip: {
valueSuffix: '%'
}
}]
});
});
};
hammerchart();
I have a small problem with my Highcharts maps. I managed to get it plotting the data I want and even included a selector so the user can adjust which data series is shown. However, it appears that after rendering one particular series once, the Map is appending data objects to each series for those U.S. territories I don't have data for. My biggest series has 53 data objects (US States plus DC, Puerto Rico, Virgin Islands) but after being rendered once the series shows up with 62 structures including those for which I didn't have data (Saint John, Saipan, etc.). This is causing problems with the snippet of code I use to change the state code to the hc-key format needed by this map. While I could just add some extra checks to that function, i would like to understand why Highcharts is adding data to my data structures. Any help would be most appreciated. My Javascript code is below:
<script type="text/javascript">
var states ={resStates: [{code: "AK",value:3},{code: "AL",value:49},{code: "AR",value:9},{code: "AZ",value:28},{code: "CA",value:170},{code: "CO",value:26},{code: "CT",value:12},{code: "DC",value:10},{code: "DE",value:5},{code: "FL",value:126},{code: "GA",value:51},{code: "HI",value:7},{code: "IA",value:5},{code: "ID",value:13},{code: "IL",value:31},{code: "IN",value:28},{code: "KS",value:15},{code: "KY",value:12},{code: "LA",value:17},{code: "MA",value:54},{code: "MD",value:124},{code: "ME",value:13},{code: "MI",value:60},{code: "MN",value:8},{code: "MO",value:19},{code: "MS",value:16},{code: "MT",value:6},{code: "NC",value:43},{code: "ND",value:3},{code: "NE",value:10},{code: "NH",value:14},{code: "NJ",value:72},{code: "NM",value:35},{code: "NV",value:7},{code: "NY",value:70},{code: "OH",value:84},{code: "OK",value:23},{code: "OR",value:11},{code: "PA",value:101},{code: "PR",value:2},{code: "RI",value:20},{code: "SC",value:26},{code: "SD",value:5},{code: "TN",value:19},{code: "TX",value:91},{code: "UT",value:67},{code: "VA",value:112},{code: "VI",value:1},{code: "VT",value:1},{code: "WA",value:29},{code: "WI",value:13},{code: "WV",value:2},{code: "WY",value:4}],
acadStates: [{code: "AK",value:1},{code: "AL",value:47},{code: "AR",value:12},{code: "AZ",value:31},{code: "CA",value:163},{code: "CO",value:34},{code: "CT",value:9},{code: "DC",value:18},{code: "DE",value:7},{code: "FL",value:121},{code: "GA",value:80},{code: "HI",value:6},{code: "IA",value:9},{code: "ID",value:9},{code: "IL",value:29},{code: "IN",value:55},{code: "KS",value:16},{code: "KY",value:8},{code: "LA",value:14},{code: "MA",value:65},{code: "MD",value:81},{code: "ME",value:8},{code: "MI",value:66},{code: "MN",value:7},{code: "MO",value:20},{code: "MS",value:13},{code: "MT",value:4},{code: "NB",value:1},{code: "NC",value:53},{code: "ND",value:4},{code: "NE",value:8},{code: "NH",value:13},{code: "NJ",value:32},{code: "NM",value:32},{code: "NV",value:4},{code: "NY",value:93},{code: "OH",value:83},{code: "OK",value:25},{code: "OR",value:7},{code: "PA",value:123},{code: "PR",value:2},{code: "RI",value:15},{code: "SC",value:17},{code: "SD",value:4},{code: "TN",value:9},{code: "TX",value:103},{code: "UT",value:62},{code: "VA",value:114},{code: "VT",value:2},{code: "WA",value:27},{code: "WI",value:19},{code: "WV",value:4},{code: "WY",value:4}],
empStates: [{code: "AK",value:2},{code: "AL",value:71},{code: "AR",value:4},{code: "AZ",value:12},{code: "CA",value:235},{code: "CO",value:1},{code: "DC",value:36},{code: "FL",value:104},{code: "GA",value:70},{code: "HI",value:5},{code: "IL",value:17},{code: "IN",value:18},{code: "KS",value:14},{code: "LA",value:5},{code: "MA",value:61},{code: "MD",value:244},{code: "MI",value:39},{code: "MN",value:3},{code: "MO",value:4},{code: "MS",value:64},{code: "NC",value:22},{code: "NE",value:2},{code: "NH",value:20},{code: "NJ",value:62},{code: "NM",value:95},{code: "NY",value:22},{code: "OH",value:133},{code: "OK",value:53},{code: "OR",value:7},{code: "PA",value:38},{code: "RI",value:37},{code: "SC",value:26},{code: "TN",value:9},{code: "TX",value:22},{code: "UT",value:75},{code: "VA",value:126},{code: "WA",value:22},{code: "WV",value:2}]};
$(init)
function init() {
drawMap();
}
function drawMap() {
var map_select = $('#smartStates').val();
var map_text = $('#smartStates option:selected').text();
var mydata = states[map_select];
var mycolors = {resStates: {
min: 1,
type: 'linear',
minColor: '#EEEEFF',
maxColor: '#000022',
stops: [
[0, '#EFEFFF'],
[0.67, '#4444FF'],
[1, '#000022']
]
}, acadStates: {
min: 1,
type: 'linear',
minColor: '#FFEEEE',
maxColor: '#220000',
stops: [
[0, '#FFFFEE'],
[0.67, '#FF4444'],
[1, '#220000']
]
}, empStates: {
min: 1,
type: 'linear',
minColor: '#EEFFEE',
maxColor: '#002200',
stops: [
[0, '#EEFFEE'],
[0.67, '#44FF44'],
[1, '#002200']
]
}
}
// Adjusting codes to fit with the us-all-territories map
$.each(mydata, function(){ //looping through each instance of mydata
if (this.code == "PR") {
this.code = "pr-3614";
} else if (this.code == "undefined"){
//not sure what to do here
}
else if (this.code.length==2) {
this.code = "us-" + this.code.toLowerCase();
}
});
// Instanciate the map
$('#container').highcharts('Map', {
chart : {
borderWidth : 1
},
title : {
text : 'SMART Participant Counts by ' + map_text
},
legend: {
layout: 'horizontal',
borderWidth: 0,
backgroundColor: 'rgba(255,255,255,0.85)',
floating: true,
verticalAlign: 'top',
y: 25
},
mapNavigation: {
enabled: true
},
colorAxis: mycolors[map_select],
series : [{
animation: {
duration: 1000
},
data : mydata,
mapData: Highcharts.maps['countries/us/custom/us-all-territories'],
joinBy: ['hc-key', 'code'],
dataLabels: {
enabled: true,
color: '#FFFFFF',
format: '{point.name}'
},
name: 'SMART Participants',
tooltip: {
pointFormat: '{point.name}: {point.value} participants'
}
}]
});
}
</script>
This is because in your mapData property you have this:
mapData: Highcharts.maps['countries/us/custom/us-all-territories'],
us-all-territories, but as you stated, you don't have the data for all of the territories.
The solution is to use a different custom map from Highcharts, unfortunately it doesn't look like they have one with only the US territories you are looking for.
Update: as the comment by Kacper has mentioned: If you set allAreas in series to false, then empty map zones will not be displayed. Example: http://jsfiddle.net/oen00hec/
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.
Highmaps / highcharts is a javascript/jquery adapter that renders maps in browsers etc.
I have a map with a single country highlighted, however, the scale of the (world) map is such that I want zoom in after the map is loaded on the country in question.
Looking at the API I feel certain this is possible.
There is an event listener, such that I can execute custom functions on load. As illustrated with this example, where a series is added on load (Js fiddle)
Additionally there is a method mapZoom allowing you to specify a point to zoom to with the following arguments:
mapZoom (Number howMuch, [Number centerX], [Number centerY], [Number mouseX], [Number mouseY])
But when I try and call this method onload (my code attempt below, JS fiddle here), nothing happens.
$(function () {
$.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename=world-population-density.json&callback=?', function (data) {
// Assign id's
$.each(data, function () {
this.id = this.code;
});
// Initiate the chart
$('#container').highcharts('Map', {
chart: {
events: {
load: function () {
mapZoom(0.5, 100, 100);
}
}
},
title: {
text: 'Zoom on load attempt'
},
legend: {
title: {
text: 'Population density per km²'
}
},
colorAxis: {
min: 1,
max: 1000,
type: 'logarithmic'
},
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
series : [{
data : data,
mapData: Highcharts.maps['custom/world-highres'],
joinBy: ['iso-a2', 'code'],
name: 'Population density',
allowPointSelect: true,
cursor: 'pointer',
states: {
hover: {
color: '#BADA55'
}
},
tooltip: {
pointFormat: '{point.id} {point.name}',
valueSuffix: '/km²'
}
}]
});
});
});
mapZoom is a method that belongs to the chart object so, in order to call it as en event (load), you have to call it using this keyword.
You can edit your code like this (JSFiddle):
...
events: {
load: function () {
this.mapZoom(0.5, 100, 100);
}
}
}
...
Alternatively, you can call it whenever you want using a jQuery reference (JSFiddle):
$('#container').highcharts().mapZoom(0.5, 100, 100);
Zoom to a specific country on your map is easy
series : [{
...
data: [{code: 'HU', id: 'HU', value: 7.5, name: 'Hungary'}],
...
}
...and then...
var mapChart=$('#MapContainer').highcharts(); //get map chart object from DOM
mapChart.get('HU').zoomTo(); //zoom to the country using "id" from data serie
mapChart.mapZoom(5); //elevate viewpoint a little to see surrounding countries as well
If you want to zoom several countries, than you can try this
events: {
load: function () {
var mapChart = this;
['DE', 'HU', 'RO'].map(function(code){
return mapChart.get(code);
}).reduce(function(acc, current){
// Set map bounds
acc._minX = isNaN(acc._minX) ? current._minX : Math.min(acc._minX, current._minX);
acc._maxX = isNaN(acc._maxX) ? current._maxX : Math.max(acc._maxX, current._maxX);
acc._minY = isNaN(acc._minY) ? current._minY : Math.min(acc._minY, current._minY);
acc._maxY = isNaN(acc._maxY) ? current._maxY : Math.max(acc._maxY, current._maxY);
return acc;
}).zoomTo();
}}
I have a JSON file of the following format :
[
{ name: 'Pay Off',
data: [ [2850,0],
[3135,0],
[3420,0],
[3705,0],
[3990,0],
[4275,0],
[4560,0],
[4845,0],
[5130,0],
[5415,0],
[5700,0],
[5985,285],
[6270,570],
[6555,855],
[6840,1140],
[7125,1425],
[7410,1710],
[7695,1995],
[7980,2280],
[8265,2565],
[8550,2850]
]
},
{
name: 'Profit',
data: [ [2850,-250],
[3135,-250],
[3420,-250],
[3705,-250],
[3990,-250],
[4275,-250],
[4560,-250],
[4845,-250],
[5130,-250],
[5415,-250],
[5700,-250],
[5985,35],
[6270,320],
[6555,605],
[6840,890],
[7125,1175],
[7410,1460],
[7695,1745],
[7980,2030],
[8265,2315],
[8550,2600]
]
}
]
I have to plot graph for 'Pay Off' and 'Profit' together. Even more plots maybe added to the list as per requirement. The data array has x-axis as 0th element and y-axis as 1st element.
What I am currently doing is the following -
$(document).ready(function() {
var options = {
chart: {
renderTo: 'container',
type: 'line'
},
title: {
text: 'PayOff Curve'
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
borderWidth: 0
},
series: []
};
$.getJSON('data.json', function(list) {
var newseries = {
name: '',
data: []
};
$.each(list, function(i,item){
newseries.name = item.name;
newseries.data = item.data;
options.series.push(newseries);
});
// Create the chart
var chart = new Highcharts.Chart(options);
});
But I don't any graph at all. I can't figure out the issue with it as I am new to both JavaScript and Python. Please suggest an efficient method to do this.
Thanks for your help..
Your JSON isn't proper one, try to validate it. Properties names shold have double quotes, change from: name: 'Pay Off' to: "name": "Pay Off"
Your JSON is valid for a highcharts series - you don't need to try to transform it at all:
$(document).ready(function() {
var options = {
chart: {
renderTo: 'container',
type: 'line'
},
title: {
text: 'PayOff Curve'
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
borderWidth: 0
},
series: []
};
$.getJSON('data.json', function(list) {
options.series = list; // <- just assign the data to the series property.
var chart = new Highcharts.Chart(options);
});
});
If that still doesn't work, open the console to see whether there's a JavaScript error.
Here's a fiddle.