[Riproduciamo un articolo di Cole Nussbaumer Knaflic, di cui Apogeo ha pubblicato Data Storytelling, libro con tentativi di imitazione. Il post originale, how you would visualize hurricanes, è tratto dal blog storytelling with data.]

[Gli altri post della serie sono apparsi in data 19 ottobre, 26 ottobre, 8 novembre, 22 novembre e 6 dicembre.]

Qualche tempo fa ho ripreso dall’Economist un grafico sugli uragani negli Stati Uniti e invitato i lettori a rielaborarlo, per dirmi che titolo avrebbero usato per descriverlo al posto dell’originale Gli uragani in America sono diventati meno frequenti. C’è voluto tempo per scegliere le sessanta migliori proposte [che presentiamo in più articoli su Apogeonline].

Questo è il grafico originale:

Economist_hurricane.png

Uragani, quanti e di quali intensità, dall’Atlantico a un diagramma.

 

I lettori hanno inviato le proposte più diverse e creative. Qui ne mostriamo una parte.

Jason C.

Jason ha visualizzato gli uragani totali e quelli sopra la media, portando l’attenzione sulla proiezione dei dati a completamento di questo decennio:

Hurricane_Jason.png

Pronostico elaborato sulla base delle serie storiche. Sarà centrato?

 

Jon L.

Jon ha costruito in D3 uno strumento per esplorare i dati e ha pubblicato su GitHub varie GIF animate e considerazioni personali.

Hurricane_Jon.png

D3 permette visualizzazioni interessanti, ma anche e soprattutto animazioni.

 

Kat G.

Kat (Twitter) ha messo insieme grafici e commenti:

Hurricane_Kat.png

Uragani più potenti, con eccezioni nel tempo recente.

 

Kettki D.

Scrive dall’India:

Mi sembra che dal dataset manchi una porzione importante: anni in cui l’uragano non ha raggiunto la terraferma. Vedere lo scherma generale è più significativo dei singoli numeri, specialmente se ragioniamo su dati riguardanti centinaia di anni. Certo è una bella sfida mostrare in poco spazio centocinquanta anni di informazione.

Hurricane_Kettki.jpg

Dovrebbero contare anche gli uragani che non hanno raggiunto la terraferma.

 

Kevin K.

Kevin (Twitter | blog) ha creato quanto segue. Lo si può vedere anche su Tableau Public.

Hurricane_KevinK.png

Modi diversi di rappresentare gli stessi dati: un corso accelerato.

 

[Presenteremo in altri articoli il resto delle soluzioni fornite dai lettori del blog di Cole Nussbaumer Knaflic. Se i lettori di Apogeonline vogliono cimentarsi nella sfida, possono inviarci il loro lavoro e relative considerazioni all’indirizzo info@apogeonline.com. Il dataset di partenza è scaricabile da Dropbox.]

Data storytelling

I dati raccontano fatti. Ma anche storie.

 

Didascalia della foto

Dida seconda

Figure e figcaption

Il testo è arricchito da esercizi, esempi reali e aneddoti in cui ogni programmatore può facilmente ritrovarsi per capire come evitare gli errori più importanti tipici.

$PLACEHOLDER
Didascalia della foto

Il testo è arricchito da esercizi, esempi reali e aneddoti in cui ogni programmatore può facilmente ritrovarsi per capire come evitare gli errori più importanti tipici.

$PLACEHOLDER
Didascalia della foto

Nel libro Sviluppare applicazioni con Angular abbiamo discusso di come Angular effettui il change detection, ma senza fare un esempio pratico. Tratteremo anche un altro argomento che più volte mi è stato sollecitato, ovvero la possibilità di includere dinamicamente uno script esterno. Zone.js è una libreria sviluppata dal team Angular ispirata da Dart, il linguaggio che implementa la gestione delle zone. Immaginiamo le zone come contenitori in cui verranno effettuate chiamate asincrone. Angular fa uso di questa libreria per sapere cosa effettivamente sia stato eseguito in maniera asincrona ed è in grado di rilevare gli eventuali cambiamenti. Il sistema di base è piuttosto semplice quanto ingegnoso. Ogni chiamata che genera una routine asincrona viene grabbata, ingabbiata dalla libreria, affinché al suo termine generi un change detection. Rispetto alla versione precedente, la propagazione del detection avviene in maniera unidirezionale dalla radice verso i figli. Questo sistema garantisce una propagazione uniforme, cosa che non accadeva con AngularJS, dove non era certo chi avrebbe eseguito il detection per primo. Chi ha lavorato con AngularJS ricorderà l’uso smodato di cicli digest causati da questo problema. Angular è più rigido nel binding ed in generale nello sharing soprattutto per questo motivo, onde evitare che la pioggia di two-way data binding generi il caos che ha collassato la vecchia versione del nostro amato framework. Procediamo con il nostro esempio.

app.service.ts

import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';

declare var document: any;

@Injectable()
export class LoadMapService {
  constructor() {}
  load(): Observable {
    return new Observable(observer => {
      const script = document.createElement('script');
      script.type = 'text/javascript';
      document.getElementsByTagName('head')[0].appendChild(script);
      script.src = 'https://maps.googleapis.com/maps/api/js?key=KEY';
      script.onload = function(){ observer.next(true); observer.complete(); };
      script.onerror = function(){ observer.next(false); observer.complete(); };
    });
  }
}

Questa tecnica è abbastanza usata ma, a oggi, è preferibile sicuramente gestire le dipendenze tramite module loader come webpack o systemjs, che permettono la gestione lazy (on demand) dei moduli. In questo contesto, la gestione della libreria via iniezione dello script nello head risulta ugualmente accettabile, in quanto lo script in questione sarà il fulcro della nostra applicazione e non un aspetto secondario. Il metodo load della classe LoadMap crea un elemento script di tipo text/javascript. AppendChild appende script nello head e imposta la sua proprietà src con il link corrispondente allo script da importare. Terminata questa routine, il browser troverà questo codice all’interno del DOM e potrà solo elaborarlo.

<script type="text/javascript" src=" https://maps.googleapis.com/maps/api/js?key=KEY;"></script>

Gli eventi onload e onerror vengono evocati al termine del caricamento dello script. Nel primo caso l’import da parte del browser avrà esito positivo; diversamente, verrà evocato onerror. L’observer emetterà un valore corrispondente all’elaborazione dello script (true, false) e concluderà lo stream. Per venire elaborato, un Observable vuole all’altro capo dello stream un subscribe. Nel nostro caso il subscribe notificherà che l’oggetto google è disponibile. Dovevamo lavorare così per avere la certezza che al momento dell’evocazione l’oggetto google si trovasse già all’interno del DOM. Implementare Google Maps nel codice che stiamo per analizzare era assolutamente propedeutico per affrontare il nostro esempio. Procediamo.

app.component.ts

import {
  Component,
  ViewContainerRef,
  AfterContentInit,
  ComponentFactoryResolver,
  AfterViewInit,
  NgZone
 } from '@angular/core';
 import { LoadMapService } from './app.service';

declare var document, google, window;

 @Component({
   selector: 'app-injecthtml',
   template: '
TEST: {{test|json}}
'
 })
 export class InjectHTMLComponent {
   test: any;
 }

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
  componentRef: any;
  map: any;

  constructor(
    private zone: NgZone,
    private viewContainer: ViewContainerRef,
    private componentResolver: ComponentFactoryResolver,
    private loadMap: LoadMapService
  ) {
    const componentFactory = this.componentResolver.resolveComponentFactory(InjectHTMLComponent);
    this.componentRef = this.viewContainer.createComponent(componentFactory);
  }
  ngAfterViewInit() {
    this.loadMap.load().subscribe((bool) => {
      if (!bool) { return; }
      this.map = new google.maps.Map(document.getElementById('map'), {
        center: {lat: -34.397, lng: 150.644},
        zoom: 8
      });
      const marker = new google.maps.Marker({
        position: {lat: -34.397, lng: 150.644},
        map: this.map
      });

      marker.addListener('click', () => {
          (this.componentRef.instance).test = {test: 'Google Maps'};
      });

      document.getElementById('btn').addEventListener('click', ( event ) => {
        (this.componentRef.instance).test = {test: 'Button'};
      }, false);
    });
  }
  onClick() {
    (this.componentRef.instance).test = {test: 'Button 2'};
  }
}

 

Sviluppare applicazioni con Angular

Pronto per qualsiasi sfida di uso di Angular.

In questo esempio implementiamo un concetto che in AngularJS era richiamato tramite il servizio $compile. Lo scopo è fare elaborare un template Angular dopo che il browser abbia terminato la compilazione. Immaginiamo di creare un elemento DOM e al suo interno inserire un’interpolazione, un semplice {{Hello}}. Il browser, una volta avviata l’applicazione, avrà già terminato la compilazione e di conseguenza agirà, da quel momento in poi, fuori dall’ecosistema Angular. L’interpolazione non verrà elaborata da Angular poiché non è stata compilata in Angular. Scenari di questo tipo sono piuttosto comuni, soprattutto quando si scrivono direttive che agiscono dinamicamente sul DOM. La classe ComponentFactoryResolver viene in nostro aiuto, nel predisporre un’istanza dinamica del componente esterno InjectHTMLComponent, che porta con sé il suo template di riferimento. Questa operazione viene finalizzata istanziando il componente InjectHTMLComponent tramite il metodo createComponent. In questo modo il template del componente iniettato verrà ancorato alla view corrente. Il riferimento alla view è dato dalla classe ViewContainerRef.

app.component.html

<div id="map"></div>

<button id="btn"></button>
<button (click)="onClick()"></button>

  Avviando l’applicazione (ricordiamoci di inserire i servizi ed i componenti in app.modules) importante ricordarsi di specificare il componente che utilizzeremo dinamicamente all’interno dell’array entryComponents in @NgModule, oltre che in declarations.

@NgModule({
  declarations: [
    AppComponent,
    InjectHTMLComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  entryComponents: [
    InjectHTMLComponent
  ],
  providers: [LoadMapService],
  bootstrap: [AppComponent]
})
export class AppModule {
}

  Google Maps implementato in Angular

Lavoro sulle mappe di Google attraverso Angular e zone.js.

  La stringa contenuta in TEST: farà riferimento alla proprietà test di InjectHTMLComponent. Valorizziamo la proprietà nel componente iniettato tramite la sua istanza cui facciamo riferimento con this.componentRef.instance. Superiamo il type safe di TypeScript effettuando un cast con questo codice:

(<any>this.componentRef.instance).test = {test: 'Button'};

  In questo modo abbiamo istanziato un componente esterno all’interno del nostro componente e, di conseguenza, effettuato quello che era comune fare tramite $compile in AngularJS. Se non avessimo operato in questo modo, il template sarebbe stato elaborato da Angular come semplice testo. Consiglio di effettuare una la prova giocando con questo esempio ed inserendo il template all’interno di una infowindow associata al marker, a verificare che l’interpolazione non verrà elaborata. In questo esempio, il clic sul marker non elaborerà alcun cambiamento sulla view. Perché? La risposta è zone.js. Dopo ottomila caratteri, possiamo concludere in meno di venti secondi. Zone.js, ripetiamolo nuovamente, grabba entrambi gli eventi legati ai due clic, ma non quello sul marker. Quella è una callback slegata dalle chiamate asincrone preventivate dalla libreria e zone.js non può sapere che quel clic genererà qualcosa di asincrono. Ecco spiegato il perché sulla nostra istanza non si riflette il cambiamento di test. Se avete l’occhio allenato vi siete già resi conto che ho incluso NgZone nel nostro componente; ma finora non era necessario. Modifichiamo il sorgente per racchiudere l’addListener del marker all’interno di una zona.

marker.addListener('click', () => {
   this.zone.run(() => {
     (this.componentRef.instance).test = {test: 'Google Maps'};
   });
});

  Al termine dell’esecuzione verrà effettuato il change detection che magicamente propagherà alla view il cambio di stato di test. Desidero salutare Alberto che mi ha dato lo spunto per questo post. Buono studio!

var inputs = document.querySelectorAll('.autocomplete');
for (var i=0; i< inputs.lenght; i++) {
createAutocomplete(inputs[i]);
}


Analizziamo il numero totale di host

Abbiamo analizzato gli ultimi dati forniti dal RIPE, praticamente l’INTERNIC europeo, che nelle sue ultime statistiche aggiornate al febbraio 1998 ha calcolato circa 6 milioni di host connessi alla Rete. Di questi circa un milione e centomila si trovano sia in Germania che in Gran Bretagna. Seguono a distanza e nell’ordine Finlandia, Olanda, Francia, Svezia e Norvegia con un numero di stazioni collegate che vanno dalle 400 mila alle 300 mila. Solo in ottava posizione arriva la nostra Italia telematica con più o meno 270 mila host.

NAZ HOST
de 1 162 484
uk 1 117 356
fi 412 641
nl 409 476
fr 399 764
se 352 647
no 305 104
it 268 311
es 202 860
ch 198 634
dk 179 739
be 173 049
at 126 971
ru 121 576
pl 95 633
il 95 151
hu 73 307
cz 60 059
pt 43 136
su 42 251
ie 41 980
tr 37 537
gr 26 152
si 19 778
is 19 756
ee 18 075
ua 16 075
sk 15 761
ro 14 320
hr 9 237
lv 8 007
bg 7 578
yu 5 435
lu 5 001
lt 4 294
cy 4 230
eg 2 659
ma 1 324
mt 1 036
li 965
by 594
am 547
mk 497
ge 446
az 422
ba 384
fo 303
md 260
sm 220
al 125
tn 93
dz 45
gb 42
va 11

L'autore


Vuoi che ti mandiamo via e-mail gli articoli su Programmazione?

Lorem ipsum dolor sit amet consectetur adipisicing elit. Eius quisquam, quos voluptas sint.

$PLACEHOLDER

Corsi correlati

Vedi tutti
$PLACEHOLDER Corso online

Sviluppare in ambienti Windows, OS X e GNU/Linux

Lorem ipsum dolor sit amet consectetur adipisicing elit.

99€+IVA

di Tom Butler e Mike Davis

$PLACEHOLDER Corso online

Alla scoperta del Deep Web e del Bitcoin

Saepe, odit? Deserunt itaque sunt.

99€+IVA

di Tom Butler e Mike Davis

$PLACEHOLDER Corso in aula

Progettare interfacce responsive

Lorem ipsum dolor sit amet consectetur adipisicing elit.

229€+IVA

459€ -70%

Milano - 12/09/2018
Venezia - 14/11/2018
Bologna - 06/12/2018

di Tom Butler e Mike Davis


Libri correlati

Vedi tutti

Sviluppare applicazioni con PHP e MySQL

Lorem ipsum dolor sit amet consectetur adipisicing elit.

51,40

29,70€ -70%

17,80

29,70€ -70%

7,80

29,70€ -70%

di Tom Butler e Mike Davis

Sviluppare applicazioni con PHP e MySQL

Lorem ipsum dolor sit amet consectetur adipisicing elit.

51,40

29,70€ -70%

17,80

29,70€ -70%

7,80

29,70€ -70%

di Tom Butler e Mike Davis

Sviluppare applicazioni con PHP e MySQL

Lorem ipsum dolor sit amet consectetur adipisicing elit.

51,40

29,70€ -70%

17,80

29,70€ -70%

7,80

29,70€ -70%

di Tom Butler e Mike Davis


Articoli correlati

Vedi tutti