News

Build interactive maps and charts with Highcharts

Bring data to life and give users a hands-on experience with this JavaScript library

HC

HC

DOWNLOAD TUTORIALS FILES

Load the HTML

As you might expect there, the HTML file does not contain a huge amount of interesting content in this tutorial. In the head of the file, jQuery is loaded together with a small stylesheet for page layout and a stylesheet for the country flags.

001     <!DOCTYPE HTML>
002        <html>
003        <head>
004        <meta http-equiv="Content-Type"     content="text/html; charset=utf-8">
005        <title>Highmaps Example</title>
006        <script type="text/javascript"         src="http://ajax.googleapis.com/ajax/libs/    jquery/1.8.2/jquery.min.js"></script>
007        <link rel="stylesheet" href="css/style.    css">
008        <link rel="stylesheet" type="text/    css" href="http://cloud.github.com/downloads/    lafeber/world-flags-sprite/flags32.css" />
009        </head>010

World flags sprite

This space efficient solution by Martign Lafebar weighs in at 172KB for the PNG (a file that is 32 pixel wide by 7808 pixel tall!) and 10KB for the CSS file used to control PNG positioning. The tutorial only uses 28 flags so the file size could actually be reduced by much more. These files are hosted on GitHub together with the generous usage license.

Lay out the chart and map

The info div appears first and contains the relevant flag icon and the country chart. The JS configuration file is responsible for making the correct flag and country name appear together with the chart legend and usage note. The countries’ map is rendered within the container div.

001    <body>
002        <div id="wrapper">
003        <div id="info">
004        <span><span id="flag"></    span></span>
005        <h2></h2>
006        <div>Click countries     to view history</div>
007        <div id="country-chart"></div>
008    </div>
009    <div id="container"></div>
010        </div>

Run the scripts

At the end of the HTML body, the scripts are loaded. The main configurable file is unemployment.js and derived from the Highcharts ‘rich information on click’ demo file. Then comes the chart library (highcharts.js), the Highmaps module (map.js), the map data for the demo (european-union.js) and a tutorial theme (ub40.js).

001    <script src="js/unemployment.js"></script>
002    <script src="js/highcharts.js"></script>
003    <script src="js/modules/map.js"></script>
004    <script src="js/european-union.js"></    script>    
005    <script src="js/themes/ub40.js"></script>
006    </body>
007    </html>

Style the page

The CSS starts with a full page background and is followed by setting a Google Font for all text. Note that @import is not used here for loading the font. The Google Fonts page provides the code for different methods of loading fonts and you might find it useful to try out these different methods.

001    html { 
002        background: url(../img/bg.jpg) no-repeat center center fixed;
003        -webkit-background-size: cover;
004        -moz-background-size: cover;
005        -o-background-size: cover;
006        background-size: cover;
007    }
008     body {
009        font-family: 'Raleway', sans-serif;
010    }

Position main elements

The wrappers div is used for centring the content in the browser. Highcharts works well on mobile devices and you can add your content to responsive layouts without creating a load of extra code. The map is floated right and the info box is floated left so that the map doesn’t move when the info box isn’t displayed (in other words when no country is selected).

001    #wrapper {
002        height: 500px;
003        width: 980px;
004        margin: 50px auto;
005        padding: 0;
006    }
007    #container {
008     float: right;
009     height: 500px;
010        width: 570px;
011    }
012    #info {
013        float: left;
014        width: 380px;
015    }
016    

Format the info panel content

The country name is displayed as an H2 heading and set to display inline so that it sits alongside the flag sprite. Not all flags are the same shape and vertical-align: bottom is used so that they all sit on the same baseline.

001    #info h2 {
002            display: inline;
003    }
004    #info .f32 .flag {
005            vertical-align: bottom !important;
006    }
007    #info h4 {
008            margin: 2em 0 0 0;
009    }

Encode the data

For simpler datasets, the data can simply be added to the script for loading into arrays. However for larger volumes of data you should use a CSV file. JSONP.PHP is used to get the data. JSONP is used instead of JSON as it works across domains. This step requires server-side processing so you will need to link to a valid JSONP.PHP online.

001    $(function () {
002        $.getJSON('http://yourdomain-goes-here/    highmaps/data/jsonp.php?
003        filename=unemployment.csv&callback=?',     function (csv) {
004        function CSVtoArray(text) {
005        return text.replace(/^"/, '')        006        .replace(/",$/, '')
007        .split('","');
008        };
009        csv = csv.split(/n/);
010        var countries = {},
011        mapChart,
012        countryChart,
013        numRegex = /^[0-9.]+$/,
014        quoteRegex = /"/g,
015    categories = CSVtoArray(csv[1]).    slice(4);
016     

Parse the CSV

The CSV used here is an edited version of the dataset “SL.UEM.TOTL.ZS” from the World Bank which provides a huge amount of data on a diverse range indicators. Each row represents a different country and a new array is created for each, then each item within the row is loaded into the array.

001    $.each(csv.slice(2), function (j, line) {
002    var row = CSVtoArray(line),
003    data = row.slice(4);
004    $.each(data, function (i, val) {
006    val = val.replace(quoteRegex, '');
007    if (numRegex.test(val)) {
008    val = parseInt(val);
009    } else if (!val) {
010    val = null;
011    }
012    data[i] = val;
013    });
014    countries[row[1]] = {
015    name: row[0],
016    code3: row[1],
017    data: data
018                };
019        });

Get the latest percentage

The array is searched and the values for the most recent figures are added to the end of the array using the push() method so that this data may be used in the map. During the search the break statement is used to jump out of the search loop whilst the array contains a value (=== ‘number’). When no data is found the loop carries on to the push().

001    var data = [];
002    for (var code3 in countries) {
003    var value = null,
004    year,
005    itemData = countries[code3].data,
006    i = itemData.length;
007    while (i--) {
008 if (typeof itemData[i] === 'number') {
009    value = itemData[i];
010    year = categories[i];
011    break;
012 }
013 }
014    data.push({
015    name: countries[code3].name,
016    code3: code3,
017    value: value,
018    year: year    
019 });    
020    }

Add country codes

In preparation for joining the map data with the unemployment data, the two character country codes are loaded from Highcharts and converted to lower case characters so that they may be successfully matched.

001    var mapData = Highcharts.        geojson(Highcharts.maps['custom/european-    union']);
002    $.each(mapData, function () {
003    this.id = this.properties['hc-key'];     //     for Chart.get()
004    this.flag = this.id.replace('UK',     'GB').    toLowerCase();
005    });

Total selected points

Highcharts can overlay data so that it may easily be compared. The user simply holds shift and clicks on each country (point) they want to compare. This code checks how many points have been selected. If only one, the country’s flag and name is displayed. If more than one, the title “Comparing countries” is displayed.

001    Highcharts.wrap(Highcharts.Point.prototype, 'select', function (proceed) {
002      proceed.apply(this, Array.prototype.    slice.call(arguments, 1));
003        var points = mapChart.        getSelectedPoints();
004    if (points.length) {
005    if (points.length === 1) {
006    $('#info #flag').attr('class', 'flag ' +      points[0].flag);
007    $('#info h2').html(points[0].name);
008    } else {
009    $('#info #flag').attr('class', 'flag');
010    $('#info h2').html('Comparing         countries');
011    }

Configure the country chart

Some chart styling is done here and the rest are done using a separate theme file. Compare this example with the Highcharts rich information, on click demo you will see that as percentages are being used the per cent symbol has been added: ‘{value}%’ for the yAxis label and % for the valueSuffix tooltip.

001    $('#info .subheader').html('<h4>Historic     % unemployment</h4><small><em>Shift + Click on     map to compare countries</em></small>')
002        if (!countryChart) {
003        countryChart = $('#country-chart').    highcharts({ chart: {
004    height: 396, width: 380, marginTop:     30, spacingLeft: 15 },
005    credits: { enabled: false },
006    title: { text: null },
007    subtitle: { text: null },
008    xAxis: { tickPixelInterval: 60,                    crosshair: true },
009    yAxis: { title: null, labels: {     format:      '{value}%' } },
010    tooltip: { shared: true, valueSuffix: '%'},
011    plotOptions: { series: { animation: {     duration: 500 },
012    marker: { enabled: false },
013    threshold: 0,
014    pointStart: parseInt(categories[0]),
015    }
016    }
017    }).highcharts();
018    } 

Display the chart

The unemployment data is displayed on the chart for each selected country. Colours are assigned for each country from the list of colours specified at the beginning of the theme file.

001    $.each(points, function (i) {
002        // Update
003        if (countryChart.series[i]) {
004         /*$.each(countries[this.code3].data,     function (pointI, value) {
005        countryChart.series[i].points[pointI].    update(value, false);            006        });*/
007        countryChart.series[i].update({
008        name: this.name,
009        data: countries[this.code3].data,
010    type: points.length > 1 ? 'line' : 'area'
011        }, false);
012    } else {
013        countryChart.addSeries({
014      name: this.name,
015        data: countries[this.code3].data,
016    type: points.length > 1 ? 'line' : 'area'
017        }, false);
018          }
019    });

Carry on looping

The countryChart is added to and redrawn until all of the selected countries have been displayed. If only one country is selected then the flag, country name and subheader are displayed. If no countries are selected (by single-clicking on the currently selected country) the chart is removed using countryChart.destroy().

001    while (countryChart.series.length > points.length) {
002    countryChart.series[countryChart.series.    length - 1].remove(false);
003        }
004        countryChart.redraw();
005    } else {
006      $('#info #flag').attr('class', '');
007        $('#info h2').html('');
008        $('#info .subheader').html('');
009        if (countryChart) {
010            countryChart = countryChart.destroy();
011        }
012    }
013     });

Colour me useful

The colorAxis option within the mapChart has a major impact on the look and usefulness of the map chart. This demo uses a set of colours taken from a currently popular Adobe Color CC set: Neutral Blue. Take some time to explore the colorAxis options so your mapChart is both attractive and useful.

001     colorAxis: {
002            type: 'linear',
003            minColor: '#EEEEFF',
004            maxColor: '#000022',
005    stops: [
006            [0, '#FCFFF5'],
007            [0.25, '#D1DBBD'],
008            [0.5, '#91AA9D'],
009            [0.75, '#3E606F'],
010            [1, '#193441']
011        ]
012    },

Display the map

With its charting heritage, Highmaps content is also treated as a series of datapoints and the options for these are configured here. The unemployment data and map data are both loaded and the unemployment data for each country is linked to the country (joinBy). Finally, in this file there is an option to preselect a country.

001    series : [{
002            data : data,
003            mapData: mapData,
004            joinBy: ['iso-a3', 'code3'],
005            name: 'Current unemployment',
006            allowPointSelect: true,
007            cursor: 'pointer',
008            states: {
009                select: {
010                    color: '#A4A800',
011                    borderColor: '#A4A800',
012                      dashStyle: 'solid'
013                }
014            }    
015        }]
016        }).highcharts();
017        // Pre-select a country
018        mapChart.get('gb').select();
019        });
020
×