Présentation de la version 1

2011-02-18 Petits exercices avec Gnuplot

Tags: gnuplot

ENTRAVAUX

Gnuplot est connu comme un outil puissant quoique rébarbatif, essentiellement pour sa capacité à tracer des fonctions mathématiques ou des graphiques de type conventionnel, comme les histogrammes, les nuages de points, etc.

On peut cependant l'utiliser pour des travaux plus diversifiés. Des données associées à des coordonnées géographiques peuvent par exemple tracer d'assez jolies cartes; elles n'ont pas la prétention de rivaliser avec celles produites par des outils dédiés à ce domaine (en particulier parce que ces cartes ne relèvent que d'une projection élémentaire dite «plate carrée»), mais elles permettent au moins d'appréhender des données de façon intuitive par rapport à leur contexte spatial.

Dans l'exemple qui suit, j'ai cherché à représenter les observations météorologiques connues sous le nom de METAR[1], qui proviennent pour la plupart d'aéroports et sont disponibles en permanence sur le site de la National Oceanic and Atmospheric Administration .

Ces données sont dans un format ASCII bien documenté, et des librairies en divers langages (dont Perl) permettent de faciliter leur interprétation. Elles semblent d'ailleurs largement utilisées par tous les sites, commerciaux ou non, qui proposent des applets ou des bouts de code javascript permettant d'afficher le temps, sur le bureau ou une page web.

Elles se présentent soit sous forme de données isolées propres à une seule station[2], soit sous forme de fichiers assez important rassemblant toutes les observations d'une tranche horaire (cycles[3]).

Une première chose à considérer est le format sous lequel transmettre des données à gnuplot. Celui-ci attend le plus souvent des données sous forme de lignes dont les éléments sont séparés par des espaces. Par exemple

  # Latitude Longitude	Température
    

Mais dès qu'on envisage d'effectuer des sélections parmi celles-ci, il devient intéressant de se tourner vers la capacité de Gnuplot à lire en entrée le résultat d'une commande externe, ce qui permet d'utiliser toutes les combinaisons des outils en ligne de commande chaînés les uns aux autres, tels que sed, cut, etc.

Mais toute cette tuyauterie me semble plutôt réservée à des utilisateurs assez aguerris. Les exemples de la documentation proposent assez souvent d'utiliser awk, langage que je n'ai jamais eu le courage d'aborder; j'ai trouvé beaucoup plus commode d'utiliser SQL, et en particulier sqlite qui permet de conserver ses données dans le répertoire de travail sans même avoir à maintenir un serveur de base de données.

Les données METAR ne contiennent pas de coordonnées géographiques, mais les aéroports auxquels elles sont associées sont identifiés par un code ICAO[4]. Celui-ci pourra être utilisé comme index dans les tables. J'ai retrouvé un fichier rassemblant toutes ces coordonnées et importé celles-ci ainsi que quelques données éventuellement exploitables (la nationalité, l'altitude) dans une table sqlite3. Les scripts et les détails de ces opérations sont donnés de manière extensive dans une autre page pour ne pas alourdir celle-ci.

À la suite de quoi, il est déjà possible de sélectionner les stations en fonction de leur nationalité, soit de manière interactive:

  gv@spirou:~/travaux/gnuplot/airports$ sqlite3 airports.db
  SQLite version 3.7.3
  Enter ".help" for instructions
  Enter SQL statements terminated with a ";"
  sqlite> .schema airports
  CREATE TABLE airports (
    ICAO varchar(4) primary key,
    COUNTRY varchar(2),
    LAT decimal(5,2),
    LON decimal(5,2),
    ALTITUDE mediumint,
    NAME varchar(16)
  );
  sqlite> .separator '  '
  sqlite> SELECT ICAO,NAME FROM airports WHERE COUNTRY LIKE 'BE' ORDER BY NAME;
  EBAW  ANTWERP/DEURNE
  EBBE  BEAUVECHAIN (BAF
  EBBX  BERTRIX (BEL-AFB
  EBLG  BIERSET/LIEGE (C
  ...
  EBSZ  SEMMERZAKE (MIL)
  EBSP  SPA/LA SAUVENIER
  EBST  ST. TRUIDEN (BAF
  EBWE  WEELDE (MIL)
  sqlite> .quit

soit depuis le shell:

  gv@spirou:~$ sqlite3 -separator ' ' airports.db \
   "SELECT COUNT(ICAO) FROM airports WHERE COUNTRY LIKE 'FR'"
  149

Y a-t-il des aéroports sous le niveau de la mer ?

  gv@spirou:~$ sqlite3 -separator ' ' airports.db
  "SELECT ICAO,COUNTRY,NAME,ALTITUDE FROM airports WHERE ALTITUDE < 0"
  KNJK US EL CENTRO NAF    -13
  KIPL US IMPERIAL         -17
  KTRM US THERMAL/PALM SPG -35
  SKSP CO SAN ANDRES ISLAN -2
  EHLE NL LELYSTAD         -6
  EHRD NL ROTTERDAM AIRPOR -5
  OIGG IR RASHT            -13
  OINN IR NOSHAHR          -19
  OINR IR RAMSAR           -20
  UATG KZ ATYRAU           -22
  UBBB AZ BAKU/BINE ARPT   -6
  URWA RU ASTRAKHAN        -20

Apparemment, oui :) !

Pour décoder les données METAR, j'ai utilisé le module xxxx de xxxx.

Le problème est que ces données sont parfois incomplètes ou mal formées. Or Gnuplot interrompra le tracé dès qu'il rencontrera une donnée erronée. Il faut donc veiller à vérifier la validité des données extraites avant de l'insérer, et à ce la table ne contienne que des valeurs 'NULL' dans le cas contraire, ce qui permet de faire appel à cette condition dans les requêtes.

Au final, j'ai donc pu utiliser la requête suivante pour obtenir les coordonnées et la température des stations comprise dans une zone correspondant à l'Europe sans la Scandinavie.

 $ sqlite3 -separator ' ' airports.db 'SELECT LON,LAT,TEMP FROM airports \
   LEFT JOIN metar ON metar.ICAO=airports.ICAO \
   WHERE lat < 65 and lat > 33 and lon > -25 and lon < 40 \
   AND TEMP IS NOT NULL '
   -22.6 63.97 1
   -21.9 64.13 2
   4.47 51.2 5
   5.47 51.17 6
   4.5 50.9 6
   4.45 50.47 6
   [...]

Les données retournées sont donc des triplets qui permettent de positionner des points dans un espace en 3 dimensions.

Nous utiliserons tout d'abord une fonctionnalits de Gnuplot qui permet de reconstituer une surface sous forme de filet à partir de points répartis irrégulièrement. Il s'agit de la fonction dgrid3d qui demande comme arguments 2 premiers entiers qui détermineront le nombre de mailles désiré selon les 2 axes horizontaux (et donc la finesse du résultat), et un troisième dont la définition est assez compliquée à résumer (je renvoie à la documentation de gnuplot). La hauteur de chaque point résultant est influencée par l'ensemble des points de donnée de manière inversément proportionnelle à leur distance selon un calcul où intervient ce dernier facteur; en pratique, la surface s'adoucit quand celui-ci est plus grand. C'est une fonction assez gourmande en ressources, et respecter la recommandation de la doc consistant à utiliser des puissances de 2 permet effectivement d'accélérer le rendu.

Il est ensuite possible de représenter une telle surface en lui attribuant une couleur en fonction de la hauteur, ce qui améliore beaucoup la lisibilité du résultat; voici le résultat avec la palette par défaut et l'index des couleurs sur la droite.

C'est plus joli mais évidemment guère explicite. Il faudra adopter un point de vue depuis le haut pour obtenir une carte. Il y a une instruction spécifique pour cela, c'est set view map.

Pour en faire une carte compréhensible, il restera à y superposer le tracé des lignes côtières. Du moment qu'on dispose de ce type de données sous forme de couples de coordonnées, on peut les tracer avec gnuplot. Tant qu'on se contente d'une définition assez faible, on peut trouver assez facilement des données de ce type librement utilisables. J'en ai répertorié sur cette page, et pour cet exemple j'ai retenu celles du NOAA: http://www.ngdc.noaa.gov/mgg/coast/ qu'on peut sélectionner en précisant les latitudes et longitudes de la zone désirée.

Il est également possible de spécifier une palette de couleur différente. Je trouve que celle-ci, en réservant les couleurs les plus froides à l'intervalle sous 0°, et les couleurs les plus claires aux extrémités de l'échelle, est assez explicite (plus que ceci http://weather.uk.msn.com/region.aspx?wealocations=Europe par exemple).

Enfin, on pourra y ajouter des lignes isothermes, reliant sur la surface obtenue tous les points de même hauteur, à la manière de courbes de niveaux.

Le fichier de commande rassemblant toutes ces opérations est repris in extenso sur la page précitée, ainsi qu'un script Perl permettant de créer cette carte à la volée avec les dernières données disponibles.

Mais en fin de compte, le résultat est-il fiable ?