Setup Menus in Admin Panel

School.Dataninja.it

Visualizzazioni dati con d3js: accedere alle fonti dei dati

Il primo passo è mettere a disposizione della tua applicazione i dati che ne guideranno la costruzione della pagina.

Per costruire un documento data-driven è necessario avere a disposizione dei dati. Dato che userai javascript per manipolarli, associarli agli elementi della pagina e quindi visualizzarli, alla fine avrai a disposizione i tipi nativi di questo linguaggio: numeri, stringhe, array e oggetti. Originariamente però questi dati possono venire da un gran numero di fonti diverse e presentarsi in un altrettanto grande numero di formati diversi. A volte non puoi intervenire sull’origine e la forma di questi dati, altre volte invece sta a te decidere come renderli disponibili alla tua applicazione. In questa unità vedrai un po’ tutti i casi possibili.

Prego, i dati son serviti…

A seconda di dove si trovano i dati originari, devi andarli a prendere in qualche modo. Alla fine hai bisogno di array e oggetti che puoi manipolare con javascript, quindi se non li hai già in questa forma, dovrai effettuare un parsing, quindi una trasformazione da stringhe a oggetti nativi. Andiamo però con ordine.

Variabile locale

Il modo più semplice per mettere a disposizione dei dati alla tua applicazione è assegnarli direttamente a una variabile a cui hai accesso.

In data ora hai un array di oggetti con cui puoi fare sostanzialmente quello che vuoi, con i metodi standard del javascript o con quelli più avanzati messi a disposizione da librerie esterne come jQuery, d3js o Underscorejs. Devi solo fare attenzione alla visibilità che ha la variabile data nelle tue funzioni, ricordando che in javascript lo scoping è funzionale.

Un esempio di cosa è possibile fare? Arricchisci il dataset originario, calcolando per esempio l’età dei personaggi della Disney, sapendo in che anno hanno fatto la loro prima apparizione: dataDisney.forEach(function(el) { el.age = (new Date()).getFullYear() - el.year; }) . Prova nella console del tuo browser, se scrivi dataDisney vedrai già valorizzata la variabile all’interno di questa pagina. Copia incolla il codice e poi guarda nuovamente il contenuto della variabile…

Può sembrare strano scrivere il dataset all’interno dell’applicazione, ma in realtà potrebbe essere in un file js autonomo che viene incluso nella pagina web mediante tag script: <script src="js/data.js"></script> . A valle di questo tag la variabile dataDisney sara disponibile a tutta l’applicazione.

In generale questo approccio non è dei migliori, per molti motivi. Il primo è che il caricamento avviene in maniera sincrona, un problema se i dati sono molti (si blocca la pagina finché il browser non ha scaricato tutto!). Poi la variabile diventa globale e il suo nome può confliggere con altre variabili globali, definite magari da librerie esterne. Ha però il vantaggio di non sottostare alle restrizioni della same-origin policy (la vedrai tra poco nel capitolo sui servizi esterni).

Funzione generatrice

A volte non serve specificare i dati uno a uno, ma lo può fare per te una funzione in javascript che genera i dati a partire da un algoritmo. È il caso di dati casuali, generati dalla funzione Math.random().

Anche in questo caso la variabile è già valorizzata in questa pagina, apri la console del browser e digita dataTextVar per vederla. A ogni refresh della pagina i numeri associati alle lettere dell’alfabeto saranno sempre diversi. E se volessi avere numeri sempre diversi ogni volta che leggi la variabile? In tal caso deve contenere una funzione:

Prova a eseguire la funzione dataTextFn() nella console del browser. Ogni volta fornirà numeri diversi con cui puoi costruire la tua visualizzazione.

Non è molto comune vedere applicazioni basate su numeri casuali, se non per motivi di esercizio. Ti ho mostrato questi esempi, però, per sottolineare un concetto: anche in una semplice stringa c’è potenzialmente un dataset con cui lavorare. Se provi per esempio a calcolare davvero il numero di occorrenze delle lettere in una stringa o se invece delle lettere prendi in considerazione le parole, di fatto puoi ottenere una word cloud direttamente da un testo (lo vedrai nella prossima unità).

File esterno locale

Nella stragrande maggioranza dei casi il dataset su cui vuoi lavorare è esterno alla tua applicazione, in particolare si trova in un file sul disco del server (lo stesso che serve i file della tua applicazione). Il metodo migliore per caricare un file esterno in una pagina web è farlo con una chiamata AJAX, da Asynchronous JavaScript and XML, perché il caricamento avviene in background, senza bloccare la pagina e l’esecuzione di altri script. Poi il contenuto viene usato non appena disponibile.

D3js mette a disposizione numerosi metodi per lavorare con le chiamate asincrone. Benché AJAX non sia una tecnologia banale da comprendere e usare, d3js nasconde tutta la complessità dietro a una sintassi molto semplice e alcuni strumenti di utilità che si occupano di fare tutto, dalla richiesta al trasferimento al parsing, fino a giungere all’oggetto javascript finale con cui lavorare. Li vedrai uno a uno nel prossimo capitolo sui formati.

Servizio remoto

A volte non ti servirà richiedere un vero e proprio file, ma inoltrare una richiesta a un servizio che produrrà dinamicamente una risposta nel formato che ti aspetti, per esempio leggendo i dati da un database. Dal tuo punto di vista non cambia nulla, che tu prenda i dati da un file o da un servizio che li genera al volo, dovrai sempre effettuare una chiamata asincrona a un url specifico.

A un aspetto però devi stare attento. Le chiamate asincrone via javascript hanno una limitazione dovuta a questioni di sicurezza. Possono richiedere senza restrizioni delle risorse che si trovano nello stesso dominio della pagina, mentre generalmente vengono interdette se cercano di raggiungere risorse su altri domini. Questa limitazione è dovuta alla cosiddetta same-origin policy. Per esempio chiamate AJAX da questa pagina possono raggiungere solo risorse il cui url inizia con http://school.dataninja.it/. Solo un’impostazione lato server di chi serve la risorsa richiesta può bypassare questa restrizione, mediante il meccanismo CORS, da Cross-origin resource sharing (e in generale non è così, appunto per motivi di sicurezza).

C’è un’altra scappatoia e si chiama JSONP. In pratica la chiamata AJAX in realtà inserisce dinamicamente un tag script nella pagina con l’url della risorsa remota, che viene così caricata correttamente (i tag script non sottostanno alla same-origin policy). La risposta però deve essere javascript valido che viene eseguito immediatamente e tipicamente consiste nella dichiarazione di una variabile globale con dentro i dati richiesti. Si torna così al caso visto all’inizio delle variabili locali.

Se hai pieno accesso al web server in cui risiede la tua applicazione puoi anche impostare un proxy che rediriga (lato server) determinate richieste locali (stesso dominio dell’applicazione) a risorse remote.

Tenendo presente quanto detto fin qui, comunque, verifica sempre che non ci sia qualche metodo ad-hoc per il servizio specifico che ti serve. Per esempio nel caso dei Google Spreadsheet c’è una libreria javascript molto versatile, Tabletop.js, con cui puoi interrogare i tuoi fogli di calcolo in cloud come se fossero tabelle di un database.

I formati dei dati

Qualunque sia la loro fonte e qualunque sia il metodo che decidi di (o sei costretto a) usare per richiederli, i dati che ti servono ti arriveranno in un determinato formato che dovrai gestire per ottenere alla fine sempre e solo array e oggetti con cui lavorare.

Tipi nativi di javascript

Se i dati sono già all’interno di una variabile accessibile all’applicazione (perché scritti direttamente nel codice, perché caricati all’interno di un tag script o sotto forma di JSONP) o sono generati dinamicamente da una funzione, sono già nel formato giusto e puoi semplicemente usarli così come sono.

Risorse JSON

Il formato JSON (JavaScript Object Notation) è senza dubbio il formato più comodo con cui puoi ottenere i tuoi dati. Un file JSON è sempre un file di puro testo, ma ha un formato che ricorda quello dei tipi nativi di javascript: numeri, stringhe, array e oggetti. Ti consiglio di dare un’occhiata alla pagina ufficiale delle specifiche, in ogni caso d3js mette a disposizione un metodo che fa sostanzialmente tutto da solo: d3.json(url[,callback]) .

Tutto il codice che ha a che fare con i dati richiesti deve essere eseguito all’interno della funzione di callback, che viene chiamata con due parametri: un oggetto che contiene eventuali errori e il contenuto del file già codificato. Ecco un esempio che puoi provare nella console del tuo browser.

In realtà la chiamata AJAX in sé riceve un file di testo, ma il metodo json() ne fa passare il contenuto in una funzione di parsing che trasforma il JSON in un oggetto nativo di javascript (nel caso dell’esempio, un array di oggetti) prima di invocare la tua funzione di callback.

Testo delimitato (CSV, TSV, …)

Per i formati di testo delimitati (da virgole nel caso del formato CSV e da tabulazioni in quello del formato TSV) vale esattamente lo stesso discorso fatto per il JSON.

La differenza sta che nel caso del JSON l’oggetto data ritornato è di qualsiasi tipo, a seconda del contenuto del JSON stesso (può essere un array o un oggetto). Nel caso dei testi delimitati, invece, in data c’è sempre un array di oggetti che hanno come chiavi i campi della prima riga (di intestazione) e come valori i campi di ogni riga successiva alla prima. Di fatto il risultato ha la stessa struttura del JSON, come è giusto che sia, perché i dati non cambiano, cambia solo il formato in cui sono memorizzati e serviti.

Fai attenzione ai numeri! Il formato JSON prevede valori numerici, che sono rappresentati da numeri non circondati da virgolette doppie. L’oggetto risultante avrà quindi all’interno dei numeri. Nei formati CSV & Co., invece, ci sono solo stringhe. Per cui l’oggetto risultante conterrà solo stringhe come valori, anche se in realtà composte da numeri. Per lavorare con i numeri veri è necessario effettuare prima un type casting esplicito, da stringa a numero, che in javascript si effettua facilmente antemponendo un + alla stringa numerica che sai essere un numero. Se non lo fosse, ritornerebbe un oggetto NaN (Not a Number). Non prendere però sotto gamba il problema del casting, soprattutto se hai a che fare con dati sporchi.

In realtà i metodi csv() e tsv() sono due scorciatoie per un metodo più generico che puoi usare direttamente se hai delimitatori meno standard (come i punti e virgola): d3.dsv().

Formato XML

È molto poco comune, comunque d3js mette a disposizione un metodo anche per l’XML: d3.xml().

Gestire un mondo asincrono

La maggior parte dei tuoi dati provengono da file che hai predisposto tu stesso e che la tua applicazione carica con una chiamata asincrona e mette a disposizione dopo aver effettuato un parsing opportuno. Non dimenticare che i metodi asincroni ritornano immediatamente, per cui il codice a valle viene eseguito subito, senza aspettare che le risorse richieste siano disponibili. L’unico modo per lavorare con il contenuto dei file richiesti è inserire tutto il codice che ne fa appunto uso all’interno delle funzioni di callback, che come dice il nome vengono chiamate dopo che i dati sono effettivamente disponibili, indipendentemente da quanto tempo richieda il loro caricamento.

Letture: 531