Friday, February 14, 2014

Mappe cartografiche e file SVG

I file con estensione SVG sono utilizzati per la grafica vettoriale, quella, per intenderci, che non sgrana quando si ingrandisce perchè l'immagine viene disegnata con linee e curve al momento della visualizzazione, anzichè, come per le immagini raster tipo jpg, png, bmp ecc., essere realizzata una volte per tutte all'atto della creazione (è il formato di base di applicazioni come Inkscape e il suo precursore Sodipodi in ambiente open source o Adobe Illustrator e Corel Draw in campo commerciale).

Il vantaggio della grafica vettoriale è che consente una rappresentazione di dettaglio che è utile per visualizzare, con molta fedeltà, dei tracciati e dei confini. Prima di riprendere lo studio del tempo in astrologia e quindi delle ore locali e solari, dei fusi orari e dell'ora legale, consentitemi una digressione in questo ambito della grafica, che ci tornerà molto utile nel seguito.

I file SVG (Scalar Vector Graphic) sono dei file XML. l'XML è uno standard nella conservazione delle informazioni su file, che si è affermato negli ultimi decenni grazie alla sua portabilità e alla sua semplicità di lettura e manipolazione, tanto per gli esseri umani che per i computer.

Un file XML, in estrema sintesi, racchiude i contenuti all'interno di tag di apertura e chiusura, un po' come HTML per il web:

<?xml version="1.0" encoding="UTF-8"?>
<utenti>
    <utente>
        <nome>Luca</nome>
        <cognome>Cicci</cognome>
        <indirizzo>Milano</indirizzo>
    </utente>
    <utente>
        <nome>Max</nome>
        <cognome>Rossi</cognome>
        <indirizzo>Roma</indirizzo>
    </utente>
</utenti>

I tag non sono vincolati, possono essere concepiti come i nomi dei campi in un database, con molta libertà. La complessità variabile fa sì che si possa arrivare a discreti livelli di nidificazione, e non è sempre facile manipolare questi file con le normali operazioni su file.

Python fornisce una libreria e diverse metodologie per l'accesso alle informazioni di un file XML e le useremo subito per estrarre dati cartografici da file SVG pubblicati sul web.

Sfortunatamente non posso allegare immagini vettoriali, ma un breve ricerca sul web ve ne mette a disposizione un numero notevole. Quella che vedete qui è stata ottenuta da un file SVG, tipicamente una mappa con chiari confini regionali, che permette una agevole rappresentazione grafica anche di dati socio-politici ed economici.

Come vedremo fra un attimo, il file SVG contiene le coordinate del tracciato delle linee che vanno a comporre l'immagine, insieme ad un certo numero di dati di stile, che permettono di variare spessore delle linee, colori, e altre caratteristiche. A differenza dei normali file XML, che, come nell'esempio sopra riportato, racchiudono in tag concentriche i dati interessanti, nei file SVG i dati sono contenuti nel corpo dei tag, e la loro estrazione richiede di valutare i tag stessi, cosa che faremo subito.

Il mio interesse per i file SVG non è fine a se stesso: nei post precedenti abbiamo utilizzato le canvas di Tkinter, e abbiamo visto che alcune figure si compongono dando come parametri delle posizioni x e y nella canvas stessa. Se riuscissimo a estrarre le informazioni che costituiscono il tracciato delle linee di un file SVG, potremmo tentare di trasformarle con un semplice script in Python, nelle informazioni equivalenti per creare le stesse linee in una canvas. Vediamo quindi, per prima cosa, come è formato un file SVG e quali sono le istruzioni per noi interessanti.

Il tag che identifica l'insieme dei punti che compongono la curva di confine di uno stato è il <path>. al suo interno, con una simbologia semplice, si scrivono le coordinate delle linee complesse che formano la curva.

Se esploriamo uno di questi file con un editor di testo, anzichè con il browser o il visualizzatore di immagini (io uso gedit su ubuntu, in ambiente Windows suggerirei Notepad++), all'interno di path troviamo il subtag d= seguito da lettere singole e numeri.

Un esempio tratto da una cartografia dell'Italia:

d="M61.759,101.27c-5.313,1.354  -9.802,6.955  -15.825,5.334c-5.442,2.572 -11.99,4.529 -17.632,1.63 c-2.721,1.465 -4.626,4.26 -8.239,3.793c-3.933,0.437  -7.61,4.156  -5.245,8.238c2.784,7.396,10.431,12.128,11.943,20.122  c1.742,2.854,6.42,3.213,7.201 -0.83c2.369,1.082,2.18,5.512,5.364,2.786c6.318 -1.545,12.173 -4.757,17.484 -8.09  c7.049  -1.133,14.792,4.075,21.011  -1.274c4.678  -3.889,1.782  -10.284,0.267  -14.995c-0.883  -4.695  -0.146  -11.463  -6.075  -12.743  C68.255,104.604,65.624,100.895,61.759,101.27z" />

la lettera m o M (moveto) identifica le coordinate del punto di partenza, la c o C (curveto) produce una curva di Bezier tra il punto precedente e un nuovo punto utilizzando due punti come punti di controllo. La l o L (lineto) definisce un segmento congiungente, per la via più breve, il punto precedente con il nuovo, infine la z o Z (closepath) chiude la poligonale iniziata con il primo punto.

Per i nostri scopi è sufficiente scandire il file SVG alla ricerca del tag path e recuperare le informazioni contenute nel subtag d. l'operazione puo' non essere agevole, per via dei differenti livelli di profondità che sono ottenuti dai software con cui si realizzano le immagini vettoriali. In questo caso ci aiuta una libreria di Python, la xml.etree.ElementTree, e un piccolo codice ricorsivo scritto da me.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#|/usr/bin/env python
from xml.etree import ElementTree as ET

def xmlrecur(x):
    for i in x:
        if set(['id','style','d']).issubset(i.attrib):
                file_out.write(i.attrib['id'])
                file_out.write('\n')
                file_out.write(i.attrib['style'])
                file_out.write('\n')
                file_out.write(i.attrib['d'])
                file_out.write('\n')
        else:
            xmlrecur(i)

file_out=open('provaxml.txt','w')
tree = ET.parse('Europe_regions.svg')
root = tree.getroot()
xmlrecur(root)
file_out.close()

Il programma importa la libreria di cui sopra, che permette di esplorare i tag e i loro attribution in un file esterno. Se chiediamo di recuperare le informazioni relative ad un particolare tag, è probabile che siamo costretti a cercare a profondità variabili, da cui la necessità di una funzione ricorsiva, che chiama se stessa proponendo come input il nuovo livello di tag, fino a concludere il lavoro e stamparlo su un file esterno. Ho imposto la verifica simultanea su tre subtag (id, style e d) per essere certo di avere un numero di linee multiplo di tre. Il file che si ottiene è composto esclusivamente dai contenuti di questi tre subtag, nell'ordine, e funziona, ovviamente, solo se il file SVG li contiene tutti e tre, altrimenti occorre un adattamento. Se volete sapere cosa sono i set e il metodo issubset, vi rimando alla documentazione ufficiale di Python.

Nel prossimo post proveremo a fare la conversione tra il nuovo file txt e il metodo create_polygon di canvas scrivendo un piccolo parser. A presto.

No comments:

Post a Comment

How to create a virtual linux machine with qemu under Debian or Ubuntu with near native graphics performance

It's been a long time since my latest post. I know, I'm lazy. But every now and then I like to publish something that other people c...