Visualització de dades i Open Data amb la llibreria Highcharts

De Wikijoan
Dreceres ràpides: navegació, cerca

Contingut

Introducció

Open Data API for Chart Creation with highcharts.com

https://www.highcharts.com/blog/post/233-world-bank-open-data-api-highcharts-part1/

(TBD)

Referències OpenData

Desenvolupament

Solució amb JQuery

script ex_part1.html:

<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
	var year_list = [];
	var arrayString = [];
	var array_final = [];

	$(document).ready(function(){


	var url = "http://api.worldbank.org/countries/NOR/indicators/NY.GDP.MKTP.KD.ZG?per_page=30&MRV=30&format=jsonP&prefix=?";
	$.getJSON(url, function(json) {
	$.each(json[1], function(i, data) {
		//Store indicator name
		country_name = data.country.value;

		// Store indicator label
		indicatorName = data.indicator.value;

		// fill the date array
		year_list.push(data.date);

		// fill the string data array
		arrayString.push(data.value);

		});

		for (var i = 0; i < arrayString.length; i++) {
			if (arrayString[i] != null) {
				array_final.push(parseFloat(arrayString[i]))
			} else {
				array_final.push(null)
			};
		}

		console.log (year_list);
		console.log (arrayString);
		console.log (array_final);


		var chart = new Highcharts.Chart({
		chart: {
		type: 'spline',
		renderTo: 'container'
		},
		title: {
		text: indicatorName
		},
		tooltip: {
		valueDecimals: 2
		},
		subtitle: {
		text: 'Source: World Bank Data'
		},
		xAxis: {
		categories: year_list.reverse() //.reverse() to have the min year on the left
		},
		series: [{
		name: country_name,
		data: array_final.reverse() //
		}]
		});

	});


});
</script>
</head>
<body>
<script src="../Highcharts-6.1.2/code/highcharts.js"></script>
<script src="../Highcharts-6.1.2/code/modules/exporting.js"></script>
<script src="../Highcharts-6.1.2/code/modules/export-data.js"></script>

<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>

</body>
</html>

I un exemple més complet, script ex_part2.html:

<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(function() {

  var region_list = ["Africa", "Arab World", "Central Europe and the Baltics", "East Asia & Pacific (all income levels)", "East Asia & Pacific (developing only)", "Europe & Central Asia (all income levels)", "Europe & Central Asia (developing only)", "European Union", "Latin America & Caribbean (all income levels)", "Latin America & Caribbean (developing only)", "Least developed countries: UN classification", "Middle East & North Africa (all income levels)", "Middle East & North Africa (developing only)", "North America", "South Asia", "Sub-Saharan Africa (all income levels)", "Sub-Saharan Africa (developing only)", "Sub-Saharan Africa (excluding high income)"];
  var income_list = ["High income", "High income: OECD", "High income: nonOECD", "Low income", "Middle income", "Lower middle income", "Upper middle income", "Low & middle income", "OECD members"];
  var world_list = ["World", "world", "WLD"];

  //Settings 
  var lenght = 20;
  var display = 4 * lenght;
  var url = "https://api.worldbank.org/v2/countries/ZWE;SSA;LIC;WLD/indicators/NY.GDP.MKTP.KD.ZG?per_page=" + display + "&MRV=" + lenght + "&format=json"

  var arrayRegion = [],
    arrayIncome = [],
    arrayCountry = [],
    arrayWorld = [],
    YearList = [];


  $.ajax({
    url: url,
    complete: function(json) {
      json = JSON.parse(json.responseText);

      $.each(json[1], function(i, data) {
        // test if country name is in region_list
        if ($.inArray(data.country.value, region_list) != -1) {
          //Store Region Name
          Region_name = data.country.value;
          // Append values to arrayRegion
          if (data.value != null) {
            arrayRegion.push(parseFloat(data.value));
          } else {
            arrayRegion.push(null)
          }
          // test if country name is in income_list
        } else if ($.inArray(data.country.value, income_list) != -1) {
          // Store Income name
          Income_name = data.country.value;
          if (data.value != null) {
            arrayIncome.push(parseFloat(data.value));
          } else {
            arrayIncome.push(null)
          }
        } else if ($.inArray(data.country.value, world_list) != -1) {
          // fill the Year list array. NB. We choose the World serie to do so, as this serie is the most likely to be complete (i.e. the one with the less missing values) 
          YearList.push(data.date);
          // store Indictor Label
          indicatorName = data.indicator.value;
          if (data.value != null) {
            arrayWorld.push(parseFloat(data.value));
          } else {
            arrayWorld.push(null)
          }
        } else {
          // Finally create the country data vector
          Country_name = data.country.value;
          if (data.value != null) {
            arrayCountry.push(parseFloat(data.value));
          } else {
            arrayCountry.push(null)
          }
        }
      });

      var chart = new Highcharts.Chart({
        chart: {
          type: 'spline',
          renderTo: 'container'
        },
        colors: ['#6e9fc5', '#ffdf51', '#a6ca6d', '#ad46d6', '#f26a2e', '#00adef', '#f4bb90'],
        title: {
          text: indicatorName
        },
        subtitle: {
          text: 'Source: World Bank Data'
        },
        xAxis: {
          categories: YearList.reverse() //.reverse() to have the min year on the left 
        },
        plotOptions: {
          series: {
            marker: {
              enabled: false
            }
          }
        },
        tooltip: {
          valueDecimals: 2,
          pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}%</b><br/>'
        },
        series: [{
          name: Country_name,
          data: arrayCountry.reverse()
        }, {
          name: Region_name,
          data: arrayRegion.reverse()
        }, {
          name: Income_name,
          data: arrayIncome.reverse()
        }, {
          name: 'World',
          data: arrayWorld.reverse()
        }]
      });
    },
    error: function() {
      console.log('there was an error!');
    }
  });
});

</script>
</head>
<body>
<script src="../Highcharts-6.1.2/code/highcharts.js"></script>
<script src="../Highcharts-6.1.2/code/modules/exporting.js"></script>
<script src="../Highcharts-6.1.2/code/modules/export-data.js"></script>

<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>

</body>
</html>

Sense JQuery, Native XMLHttpRequest Object

Amb JQuery no hem tingut cap problema per accedir a la API de api.worldbank.org. Però si volem fer la crida sense JQuery, de forma nativa, ens trobem de nou amb els problemes de Access-Control-Allow-Origin.

NOTA. Per poder accedir a la API hem de fer servir la v2 de la API, que és la que permet el Access-Control-Allow-Origin com es veu a continuació:

$ curl -si http://api.worldbank.org/countries/NOR/indicators/NY.GDP.MKTP.KD.ZG?per_page=30&MRV=30&format=jsonP&prefix=?
$ HTTP/1.1 200 OK
Date: Mon, 10 Sep 2018 12:49:56 GMT
Content-Type: text/xml; charset=UTF-8
Content-Length: 7768
Connection: keep-alive
X-Powered-By: ASP.NET
Set-Cookie: TS019266c8=017189f94739a155453832e0d4b7f131856d2e00e6da16b8d7868059e7a7ca56f8e0101f06; Path=/
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: deny
...

$ curl -si http://api.worldbank.org/v2/countries/NOR/indicators/NY.GDP.MKTP.KD.ZG?per_page=30&MRV=30&format=jsonP&prefix=?

$ HTTP/1.1 200 OK
Date: Mon, 10 Sep 2018 12:49:09 GMT
Content-Type: text/xml; charset=UTF-8
Content-Length: 10523
Connection: keep-alive
Access-Control-Allow-Origin: * <<========================= IMPORTANT =========
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: X-Requested-With
X-Powered-By: ASP.NET
...

La v2 té implementat el Access-Control-Allow-Origin, com es comenta a https://groups.google.com/forum/#!topic/world-bank-api/Gjvqt6pzSOs no hi ha cap problema amb al Access-Control-Allow-Origin

Explicació Cross-Origin Resource Sharing (CORS)

script ex_part1_native_ajax.html:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>Simple use of Cross-Site XMLHttpRequest (Using Access Control)</title>
    <script>
    
    var invocation = new XMLHttpRequest();
    //important! s'ha de fer servir la versió 2 de la API, que permet fer el Access-Control-Allow-Origin
    //en la url veiem que format=jsonP. Així doncs, de fet, aquesta tècnica és JSONP
    var url = 'http://api.worldbank.org/v2/countries/NOR/indicators/NY.GDP.MKTP.KD.ZG?per_page=30&MRV=30&format=jsonP&prefix=?';

    var year_list = [];
    var arrayString = [];
    var array_final = [];
    var country_name;
    var indicatorName;

    var invocationHistoryText;
    
    function callOtherDomain(){
        if(invocation)
        {    
            invocation.open('GET', url, true);
            invocation.onreadystatechange = handler;
            invocation.send(); 
        }
        else
        {
            invocationHistoryText = "No Invocation TookPlace At All";
            var textNode = document.createTextNode(invocationHistoryText);
            var textDiv = document.getElementById("textDiv");
            textDiv.appendChild(textNode);
        }
        
    }

    function handler(evtXHR)
    {
        if (invocation.readyState == 4)
        {
                if (invocation.status == 200)
                {
                    resposta = invocation.responseText;
                    //console.log(resposta);
                    var cad = resposta.substring(2, resposta.length-1); //això és molt cutre fer-ho així
                    //console.log(cad);
                    obj = JSON.parse(cad)
                    dades = obj[1];
                    //finalment ja tenim les dades que  interessen
                    //console.log(dades[0]);
                    country_name = dades[0].country.value
                    indicatorName = dades[0].indicator.value;
                    for (var i=0; i<dades.length;i++) {
                        //console.log(dades[i].date);
                        //console.log(dades[i].value);
                        year_list.push(dades[i].date);
                        arrayString.push(dades[i].value);
                    }

                    for (var i = 0; i < arrayString.length; i++) {
                        if (arrayString[i] != null) {
                            array_final.push(parseFloat(arrayString[i]))
                        } else {
                            array_final.push(null)
                        };
                    }

                    console.log (year_list);
                    console.log (array_final);

                    var chart = new Highcharts.Chart({
                    chart: {
                    type: 'spline',
                    renderTo: 'container'
                    },
                    title: {
                    text: indicatorName
                    },
                    tooltip: {
                    valueDecimals: 2
                    },
                    subtitle: {
                    text: 'Source: World Bank Data'
                    },
                    xAxis: {
                    categories: year_list.reverse() //.reverse() to have the min year on the left
                    },
                    series: [{
                    name: country_name,
                    data: array_final.reverse() //
                    }]
                    });

                }
                else
                    alert("Invocation Errors Occured");
        }
    }

    </script>
</head>
<body onload="callOtherDomain()">
    <script src="../Highcharts-6.1.2/code/highcharts.js"></script>
    <script src="../Highcharts-6.1.2/code/modules/exporting.js"></script>
    <script src="../Highcharts-6.1.2/code/modules/export-data.js"></script>

    <div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
</body>
</html>

creat per Joan Quintana Compte, setembre 2018

Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
Institut Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines