Capitolo 4: IMPLEMENTAZIONE
L’applicazione per la generazione di ambienti urbani è stata realizzata in C++, utilizzando come piattaforma di sviluppo Microsoft Visual Studio .NET 2002, in particolare l’ambiente
Visual C++.
Le librerie grafiche utilizzate per visualizzare i dati in output sono le OpenGL.
Per le operazioni di creazione e gestione delle immagini associate ai modelli generati è stata utilizzata la libreria DevIL.
Sono state inoltre ampiamente utilizzati le strutture dati e gli algoritmi messi a disposizione dalla libreria STL (Standard Template Library) del linguaggio C++.
Per il salvataggio di alcuni dati inerenti il CityGraph si è scelto un formato ASCII essenziale, che verrà descritto in seguito.
Nel seguito del capitolo verrà fornita una descrizione della semplice interfaccia messa a disposizione dall’applicazione per l’editing e la visualizzazione dell’ambiente virtuale urbano sfruttando XVR: tool di sviluppo per le applicazioni di realtà virtuale, messo a disposizione dal Laboratorio PERCRO della Scuola Superiore S. Anna.
4.1. Implementazione del sistema
L’applicazione è articolata in tre fasi distinte: una fase di inizializzazione e semplificazione della mappa necessaria a rendere la mappa più facilmente interpretabile, una fase di riconoscimento delle entità urbane, di calcolo e analisi dei dati per la generazione del modello
tridimensionale, e una fase di editing e modifica parziale dei dati acquisiti automaticamente (questa fase in realtà non è totalmente distinta e successiva alla precedente ma interviene nel corso della precedente elaborazione dei dati)
Nella prima fase vengono svolte le seguenti operazioni:
• Eventuale cleaning della mappa raster in input.
• Skeletonizzazione della mappa al fine di riconoscere le varie entità urbane.
Nel secondo (ed eventualmente nel terzo) stadio di elaborazione della mappa, precedentemente modificata, si distinguono le seguenti fasi:
• Riconoscimento e tipizzazione di incroci, strade, piazze, e isolati.
• Eventuale editing e modifica degli elementi riconosciuti
• Creazione del modello 3D
4.2. Classi e strutture dati
Le principali classi utilizzate dall’applicazione possono essere suddivise in due gruppi a seconda delle loro funzionalità:
• Le classi che rappresentano i dati elementari con cui è rappresentato un CityGraph e la classe che rappresenta il CityGraph stesso.
• Le classi utilizzate a supporto delle precedenti, sia per la rappresentazione di semplici entità geometriche, sia per la rappresentazione dell’immagine raster e degli altri dati in input.
In [figura 4.1] è presentato uno schema riassuntivo e semplificato dell’organizzazione in classi del programma, nel quale vengono evidenziate le relazioni tra i vari elementi che compongono il CityGraph. In azzurro sono colorate le classi che rappresentano le entità principali di un ambiente urbano (CityGraph compreso). In giallo le classi di supporto usate
per rappresentare l’immagine raster e il file TFW per la conversione in coordinate reali. Nello schema mancano le classi minimali che servono a rappresentare i punti nello spazio con coordinate sia float che intere e quella che serve a rappresentare un pixel nello spazio di colori
RGB.
Ogni singola classe che rappresenta le entità urbane, contiene attributi e metodi sia per il riconoscimento e la localizzazione all’interno della mappa, sia per la conversione in coordinate 3D e successiva visualizzazione:
A differenza dell’immagine raster e del world file, i file di input riguardanti i DTM (sia terreno che edifici) non necessitano di una rappresentazione in classi, in quanto il loro utilizzo si limita all’acquisizione dei dati all’interno dei due metodi della classe CityGraph, che ne fanno utilizzo.
Come si può vedere dallo schema di [figura 4.1], le 4 entità principale di un CityGraph (nodi, link, piazze e isolati), sono memorizzati in liste (in realtà le liste non contengono direttamente gli oggetti ma solo i puntatori a questi). Le liste vengono usate anche in altri casi, ad esempio all’interno di un nodo per elencare i link connessi. A seconda quindi dell’utilizzo si è preferito usare talvolta la classe deque e talvolta la classe list entrambe implementate nella libreria STL del C++. Entrambe le strutture dati permettono di accedere, inserire e cancellare elementi in testa e in coda alla lista con complessità costante. Le due strutture si differenziano per quanto riguarda l’accesso ai dati, la cancellazione e l’inserimento in posizioni che non siano di testa o di coda. La deque permette infatti l’accesso casuale agli elementi, i quali sono accessibili tramite indice, ma proprio per questo le operazioni di cancellazione e inserimento sono enormemente più lente. La lista invece non permette l’accesso casuale ai dati ma le operazioni di inserimento e cancellazione hanno complessità costante. [STROUS]
Di seguito si riporta in dettaglio la struttura delle classi più rappresentative e gli aspetti principali di ognuna di esse (vengono volutamente esclusi dalla descrizione metodi e attributi ausiliari, costruttori, distruttori e operatori standard).
4.2.1.
La classe ColorRGB
Questa classe rappresenta un pixel nello spazio di colore RGB attraverso i 3 canali Red,
Green e Blue. La classe contiene inoltre un attributo in più di tipo intero che serve a integrare
eventuali informazioni aggiuntive sul pixel (esempio può essere utile epr capire se il pixel è stato visitato o meno ecc).
• char R, G, B: I tre canali nello spazio di colore RGB (1 byte per canale)
4.2.2.
La classe RasterImage
Questa classe rappresenta l’immagine attraverso una matrice di oggetti ColorRGB. Inoltre contiene i metodi usati per l’inizializzazione e la semplificazione dell’immagine. [par. 3.2] La classe fa ampio uso dei metodi messi a disposizione della libreria DevIL [DEVIL] per il caricamento in memoria e il salvataggio su di disco di un’immagine raster. [par. 1.3.1]
• char* filename: Nome del file dell’immagine
• ColorRGB** pixels: matrice a due dimensioni che memorizza i pixel dell’immagine.
• unsigned int w: larghezza dell’immagine
• unsigned int h: lunghezza dell’immagine
• void ReduceColor (): Riduce il numero dei colori dell’immagine, individuandone un
insieme ristretto. [par. 3.2.1]
• void DeleteColor (ColorRGB &c): Elimina dalla mappa il colore c, sostituendolo con
la moda dei colori confinanti. [par. 3.2.1]
• void Skeletonization (int skeleton): Skeletonizza l’immagine, ma lo skeleton viene
rappresentato esclusivamente nei campi marker dei singoli elementi di tipo ColorRGB. Il parametro intero skeleton, specifica il valore del marker dei pixel da skeletonizzare ed è anche il valore da assegnare ai pixel che dopo l’algoritmo faranno parte dello skeleton. [par. 3.2.2]
• bool nextpixel(int currx, int curry, float m, int &nextx, int &nexty, int n):
Restituisce l’enensimo pixel a aprtire da un punto lungo una determinate direzione. [par. 3.3.2]
• void findnextPixelColor(int x0, int y0,int mark,int &nextx,int &nexty,float m, bool
semipiano): Restituisce l’oggetto di tipo ColorRGB più vicino al pixel dato, che abbia
un determinato marker. La ricerca permette di scegliere il semipiano tramite un parametro booleano. [par. 3.3.2]
4.2.3.
La classe TFW
Questa classe contiene tutti i parametri di conversione da coordinate in pixel a coordinate in valori reali. Fornisce anche i metodi necessari ad operare la conversione di un singolo punto. [par 3.4.1].
• float A, B, C, D, E, F. A1, B1, C1, D1, E1, F1: questi sono i parametri che
permettono la trasformazione da coordinate in pixel a coordinate reali in viceversa. Sono spiegati nel dettaglio in [par 3.4.1].
• void generate (float A, float E, float B,float D,float C,float F): Calcola i parametri
per la trasformazione da coordinate reali a coordinate in pixel.
• bool generate (char* filename): Genera i parametri per la trasformazione
direttamente dal worldfile in input. Restituisce false nel caso non riesca ad aprire il file.
• int Xpixel(float x, float y) e int Ypixel(float x, float y): calcola le coordinate in pixel
di un punto in coordinate reali.
• float Xworld(int x,int y) e float Yworld(int x,int y): calcola le coordinate reali di un
punto in coordinate in pixel.
• float Xmeters(int x,int y) e float Ymeters(int x,int y): calcola le coordinate in metri di
un punto in coordinate in pixel. In pratica sono le coordinate reali con l’origine traslata e coincidente con quella in pixel.
4.2.4.
Le classi Point2Int, Point2Float e Point3Float
Queste tre classi rappresentano rispettivamente un punto sull’immagine raster (due sole coordinate di tipo intero) e un punto nello spazio 3D (due o tre coordinate reali). Per semplicità viene descritta nel dettaglio solo la classe Point2Int.
• int x,y: coordinate di un punto
• void distancepoint (const Point3Int &p, int npixel): Calcola il punto che dista npixel
dal punto corrente rispetto al segmento, individuato dal punto corrente con il punto p.
• void partitionpoint(const Point3Int &p, int npartition): Calcola il punto che dista una
lunghezza pari alla partizione n-esima (npartition) dal punto corrente rispetto al segmento, individuato dal punto corrente con il punto p.
4.2.5.
La classe Node
Questa classe rappresenta i nodi del CityGraph. [par 3.3]
• int id: identificativo numerico del nodo
• Point2Int p: Coordinate in pixel del nodo
• Point3Float pf: Coordinate reali del nodo
• unsigned int what: Tipo del nodo. Il nodo può essere “nullo” (0), “estremo di un
vicolo cieco” (1), “curva” (2), “incrocio” (3), “vertice di piazza” (4). [par 3.3]
• square* square: Puntatore a all’eventuale piazza di cui potrebbe far parte il nodo. Se
il nodo non è un vertice di piazza, questo campo contiene il puntatore nullo.
• list<link*> linkconnected: lista dei puntatori ai link connessi al nodo.
• list<int> cyclesid: lista degli id dei cicli minimali a cui il nodo appartiene
• deque<Point2Int> coord: lista di coordinate in pixel (in senso anti-orario) che
rappresentano i reali contorni del nodo. Il nodo ha un proprio reale contorno solo nel caso sia di tipo 3. In questo caso il nodo è per costruzione un poligono convesso.
• deque<Point3Float> fcoord: lista di coordinate in metri che rappresentano il contorno
di un nodo.
• deque<Point2Float> tcoord; Coordinate di texture del nodo
• node* getNodeConnectedFromLink(link *l): restituisce il nodo connesso al nodo
• void addLink(link *l): aggiunge un link in coda alla lista dei link (senza però
ri-ordinare la lista)
• void removelink(link *l): rimuove il link l dalla lista.
• void removeLinkExcept(link *l): rimuove tutti i link dallista eccetto il link l.
• void OrdinateLinks(): Ordina la stella di link connessi al nodo secondo l’angolo di
inclinazione del link, considerando come origine il nodo stesso.
• link* prevlink(link *l): Restituisce il link che si trova in lista prima del link in input
(nel caso in input ci sia il primo link, viene restituito l’ultimo).
• link* nextlink(link *l): Restituisce il link che si trova in lista dopo il link in input (nel
caso in input ci sia l’ultimo link, viene restituito il primo).
• bool hasLinkBetween(link*l1,link*l2): Restituisce true, se in lista esiste almeno un
link che si trova fra i due link dati in input.
• link* getLinkFromNode(node *n): cerca i link che connette il nodo corrente con il
nodo passato in input.
4.2.6.
La classe Link
La classe link rappresenta i frammenti di strada, che connettono i nodi nel CityGraph. [par 3.3]
• int id: identificativo numerico del link
• unsigned int what: Tipo del link. Il link può essere “nullo” (0), può rappresentare una
strada (da 1 a 5 in ordine di importanza della strada), “corso d’acqua” (6), “ferrovia” (7). [par 3.3]
• node* node1, node2: puntatori ai nodi connessi dal link
• Point2Int coord[4]: Coordinate in pixel del contorno di un link. Il link è sempre
vengono memorizzate in questo modo: coord[0] è il punto a sinistra (o in alto di)
node1, coord[1] è quello a sinistra di node2, coord[2] a destra di node2 e coord[3]
quello a destra di node1.
• Point3Float fcoord[4]: Coordinate in metri del contorno di un link.
• Point2Float tcoord[4]: Coordinate di texture del link
• float length: lunghezza in pixel del link (distanza da nodo a nodo)
• float width: larghezza di un link calcolata nel suo punto medio. [par. 3.3.2]
• float setlenght(): Calcola la lunghezza della strada come distanza fra i due nodi.
• void linkRectangle(): Calcola il rettangolo che rappresenta idealmente il link, avendo
come riferimenti i due nodi e la larghezza. Questo passo è necessario a preparare la tessellazione vera e propria. [par. 3.4.3]
4.2.7.
La classe Square
La classe square rappresenta le piazze nel CityGraph. Ogni piazza è descritta tramite un lista di nodi che ne rappresentano il contorno ideale. I nodi sono scelti in modo che la piazza possa essere rappresentata sempre da un poligono convesso. [par. 3.3.3]
• int id: identificativo numerico della piazza.
• unsigned short what: Tipo della piazza. La piazza può essere “nulla” (0), una “piazza
vera e propria” (1), uno “specchio d’acqua” (2) o una “stazione ferroviaria” (3)
• list<node*> vertex: lista di puntatori ai nodi che descrivono idealmente il contorno
della piazza. I nodi sono ordinati in senso antiorario.
• deque<Point2Int> coord: lista di coordinate in pixel (in senso anti-orario) che
rappresentano il reale contorno della piazza, calcolato dopo la tessellazzione. [par. 3.4.3]
• deque<Point3Float> fcoord: lista di coordinate in metri che rappresentano il contorno
di una piazza.
• deque<Point2Float> tcoord; Coordinate di texture del nodo
• float makePlaceFromNodeDeque(deque <node*> &nodeset): Dato un insieme di
nodi in input, ne calcola l’inviluppo convesso e riempie la lista dei vertici coi nodi trovati, memorizzandoli in senso antiorario. [par. 3.3.3]
• node* nextvertex(node *n): Calcola il nodo della lista dei vertici, successivo a quello
in input.
• node* prevvertex(node *n): Calcola il nodo della lista dei vertici, precedente a quello
in input.
• link* getNextFromPreviousPlaceLate(node *n): Restituisce il link uscente dal nodo
in input, più vicino (con angolo minore) rispetto al lato della piazza, che collega il nodo col vertice precedente.
• link* getPrevFromNextPlaceLate(node *n): Restituisce il link uscente dal nodo in
input, più vicino (con angolo minore) rispetto al lato della piazza, che collega il nodo col vertice successivo.
4.2.8.
La classe HouseBlock
La classe HouseBlock rappresenta gli isolati nel CityGraph. Ogni isolato è descritto tramite un lista di nodi che ne rappresentano il contorno. In realtà ogni isolato non è altro che un ciclo minimale del CityGraph. [par.3.3.4]
• int id: identificativo numerico dell’isolato.
• float fz: Altezza media degli eventuali edifici dell’isolato.
• unsigned short what: tipo dell’isolato. L’isolato può essere “nullo” (0), un “edificio”
(da 1 a 3), un “giardino/parco” (4), uno “specchio d’acqua” (5), un “cortile/parcheggio asfaltato” (6).
• deque <node*> nodes: lista di puntatori ai nodi che descrivono il cilo minimale del
CityGraph che rappresenta un isolato.
• deque <Point2Int> coord: lista di coordinate in pixel (in senso anti-orario) che
rappresentano il reale contorno dell’isolato, calcolato dalla tessellazione degli altri elementi stradali. [par. 3.4.3]
• deque <Point3Float> fcoord: lista di coordinate in metri che rappresentano il
contorno di un isolato.
• deque <Point3Int> triangoles: L’isolato non è necessariamente un poligono convesso
per cui è necessario triangolarlo per poi venire rappresentato in 3D. In questa lista ogni tre vertici (in senso antiorario) viene definito un triangolo.
• void triangulate(): Trangolarizza il poligono che rappresenta l’isolato. Per farlo
sfrutta una libreria esterna e open-source, implementata da John W.Ratcliff. [RATCH]
4.2.9.
La classe CityGraph
La classe CityGraph è la classe che rappresenta il CityGraph nella sua interezza e collega fra loro le varie entità dell’ambiente urbano.
Le quattro entità principali: nodi, link, piazze e isolati, sono accessibili alla classe CityGraph, tramite quattro (una per ognuna) liste di puntatori ai vari oggetti. I puntatori ai nodi oltre ad essere memorizzati in una lista, lo sono anche in un matrice (di dimensioni uguali alla mappa raster in input), in modo da velocizzarne notevolmente l’accesso, che avviene quindi anche tramite le coordinate in pixel di ciascun nodo.
La classe controlla inoltre i successivi stadi del processo di riconoscimento della mappa e di creazione dell’ambiente 3D.
• int nodeidcount, linkidcount, placeidcount, houseblockidcount: id degli ultimi
elementi aggiunti al CityGraph.
• TFW* tfw: oggetto TFW che contiene attributi e metodi per la conversione da
• RasterImage* base: immagine raster “ripulita” della mappa 2D.
• class NodeMatrix: inner class, costituita da una matrice e dai relative metodi per
accedere ai vari elementi, formata da puntatori ai nodi. Se per una coppia di coordinate non esiste un nodo di riferimento la cella contiene il puntatore nullo.
• NodeMatrix* nmatrix: oggetto della classe NodeMatrix.
• list<node*> nodes: lista di puntatori ai nodi del CityGraph.
• list<link*> links: lista di puntatori ai link del CityGraph
• list<square*> squares: lista di puntatori alle piazze del CityGraph
• list<houseblock*> blocks: lista di puntatori agli isolati del CityGraph
• ColorRGB c_street1, c_street2, c_street3, c_street4, c_street5, c_river, c_railway e ColorRGB c_building1, c_building2, c_building3, c_water, c_garden, c_carpark:
Colore con cui sono rappresentati sulla mappa (già ripulita) i vari tipi di link (ed eventualmente piazze) e di isolati. Se il campo marker della classe ColorRGB è posto a 0 significa che sulla mappa non ci sono elementi di quel tipo.
• bool generateTFW(char *fname) e void generateTFW(float A, float E, float B, float
D, float C, float F): Generano i parametri di conversione Pixel-World direttamente da
file
• bool addnode(node *n): aggiunge il puntatore a un nodo sia in coda alla lista dei nodi
sia nella matrice di nodi relativamente alle proprie coordinate sulla rastermap.
• void addlink(link *l): aggiunge in coda alla lista dei link un puntatore al link l.
• void addsquare (deque <node*> &setnode, unsigned short square_what, float
minarea, float maxarea): Data una lista di nodi candidata a far parte di una piazza ne
calcola l’eventuale inviluppo convesso e nel caso la piazza abbia i giusti requisiti di area minima e massima, la piazza viene aggiunta al CityGraph.
• void addhouseblock(list <node*> &setnode): data una lista di nodi, ordinati e
caratterizzanti un ciclo minimale del CityGraph, crea un oggetto di tipo houseblock e ne aggiunge il puntatore alla relative lista.
• link* addItem(const Point2Int& p1,const Point2Int& p2): Aggiunge i due nodi alla
struttura dati. Crea un link che li congiunge e aggiunge anch’esso al CityGraph.
• node* clonenodeat (node *n, int x, int y): Crea o modifica (se già esistente), alla
posizione in input, un nuovo nodo con proprietà identiche a quello dato, esclusi ovviamente i link connessi. La funzione restituisce il nodo clonato.
• node* movenode (node *n,int newx, int newy, bool linkrectangle=true): muove il
nodo dato nella nuova posizione. Se la posizione è già occupata da un altro nodo, lo cancella e lo sostituisce. Nel caso il parametro linktriangle sia posto a true, significa che deve essere rieseguita la funzione linktriangle() su tutti i link del nodo.
• void collapseNodeAndMarkLink(link *l): “Collassa” assieme due nodi in uno (in una
posizione a metà strada fra le due). Il nuovo nodo contiene la somma dei link di entrambi i nodi che lo hanno generato. Il link che li congiungeva viene “segnato” (id=-1), per venire in seguito eliminato.
• void markNodeforDelete(node *n): “segna” un nodo (id=-1) per una successiva
eliminazione.
• void markLinkforDelete(link *l,bool piazza): “segna” un link (id=-1) per una
successiva eliminazione.
• void delLinkAndMarkNode(link *l): cancella un link e “segna” un nodo (id=-1) per
una successiva eliminazione.
• void setColorLink(const ColorRGB obj[8]): assegna il colore ad ogni tipo di link
• void findcross(int tovisit=1,int visited=2,int mcross=3): Ricerca gli incroci sulla
mappa in seguito alla skeletonizzazione. “Segna” ogni singolo pixel come “incrocio visitato”, “da visitare”, “incrocio trovato”. [par. 3.3.1]
• void findcurve(int tovisit=2,int visited=1,int mcross=3): Ricerca le curve sulla mappa
skeletonizzata e con gli incroci “segnati”. [par. 3.3.1]
• float getWidth(const Point3Int &p, float m,Point3Int &p1,Point3Int &p2): Calcola
la larghezza del link. [par. 3.3.2]
• void SemplifyNet (float l=4): Semplifica la rete, a seguito della ricerca incroci e
curve. Il parametro l indica la distanza massima dei nodi da accorpare. [par. 3.3.1]
• void findplace(): Per ogni nodo esegue una ricerca in profondità sul grafo finché trova
nodi e link candidate a far parte di una piazza. Ogni insieme di nodi viene poi passato alla funzione addPlace. [par. 3.3.3]
• void searchNodeforPlace(node *n, deque<node*> &nodeset, int recursion,unsigned
short place_what) e void searchNodeforPlace(node *n,deque<node*> &nodeset, unsigned short place_what): funzione ricorsiva che esegue una ricerca in profondità
fra tutti i possibili nodi candidati a far parte di una piazza e memorizza i nodi trovati nella lista nodeset.
• bool PlaceFilling(place *p,deque <node*> &setnode): Esegue una scan line di una
piazza alla ricerca di possibili nodi interni da eliminare o spastare lung oil margine della piazza. [par. 3.3.3]
• void moveNodeOnPlaceEdge(node* n, place* p): Muove un determinato nodo
interno alla piazza su un suo lato. Calcola l’intersezione dei suoi link uscenti coi vari lati della piazza ed eventualmente oltre a spostare il nodo lo clona anche in altre posizioni. [par. 3.3.3]
• void ResolvePlaceIntersection(place *p): Controlla che due piazze riconosciute non
• void semplifyNetAfterPlace(): Semplifica il CityGraph a seguito della creazione delle
piazze. [par. 3.3.3]
• void semplifyPlacecontour(place *p, float mindist=10,float maxdist=25): Semplifica
il contorno della piazza, “collassando” nodi troppo vicini, o creandone di nuovi nel caso ci siano nodi troppo distanti. [par. 3.3.3]
• void recalculateLinksRectangleCoordperNode(node *nodo): Calcola la tessellazione
dei link e dei nodi, in prossimità di incroci. e curve. [par. 3.4.3]
• void recalculateLinksRectangleCoordperPlace(place *pzza): Calcola la tessellazione
dei link, dei nodi e delle piazze, in prossimità delle piazze. [par. 3.4.3]
• void findcycles(): Ricerca dei cicli minimali all’interno del CityGraph. Ogni ciclo
minimale viene poi elaborato e aggiunto alla struttra dati del CityGraph attraverso la funzione addHouseBlock. [par. 3.3.4]
• void cycleTriangulate(): triangolarizza tutti gli isolati.
• void setTypeOfBlocks(): Riconosce il tipo di un isolato, in base alla moda dei colori
dei pixel interni al contorno dell’isolato stesso. [par. 3.3.4]
• void TFWconverting(): Calcola le coordinate reali di ciascun elemento del CityGraph
• bool findZground(char *name): carica il file DTM in input (se non ci riesce
restituisce false) e calcola l’altitudine dei nodi e di conseguenza di link e piazze.
• void findZBuilding(char *name): carica il file DTM in input (se non ci riesce
restituisce false) e calcola l’altitudine media dei palazzi che fanno parte di un isolato.
• void middlelinecoord(float length): Calcola le coordinate per l’eventuale riga di
mezzeria di una strada
• void CalculateLinksTex(float texw, float texh): Calcola le texture coordinates degli
elementi stradali. Texw e Texh indicano le dimensioni reali del pezzettino di asfalto rappresentato dall’immagine usata come texture
• void IDdefrag(): Riassegna gli ID ai vari oggetti, in modo che siano sequenziali e
senza salti.
4.3. Serializzazione degli oggetti del CityGraph
L’applicazione al termine del processo di creazione del CityGraph, permette di salvare la struttura dati creata in un semplice file ASCII.
Il file ASCII generato in output è diviso in 4 sezioni, ognuna identificata da un tag specifico. Ogni sezione rappresenta una lista di oggetti della stessa classe. In ogni sezione, uno per riga, si trovano le descrizioni degli oggetti di quella particolare classe. Ogni attributo è separato dagli altri da uno spazio. Nel caso l’oggetto contenga una lista di oggetti ad esso collegati (ad esempio: piazze e vertici), la lista sarà indicata da un tag che la rappresenta, il tag sarà seguito dal numero degli elementi e successivamente dall’identificativo degli elementi veri e propri, i quali saranno a loro volta separati da spazi. Se la lista non contiene elementi è seguita dal solo numero “0”. I tipi dei vari elementi sono espressi tramite interi. Se un nodo non fa parte di una piazza l’id della piazza è posto a -1.
<NODES>
id_n1 x y z tipo id_pzza <L> #link id_l1 id_l2 … id_ln <C> #cicli id_c1 id_c2 … id_cn id_n2 x y z tipo id_pzza <L> #link id_l1 id_l2 … id_ln <C> #cicli id_c1 id_c2 … id_cn …
id_nn x y z tipo id_pzza <L> #link id_l1 id_l2 … id_ln <C> #cicli id_c1 id_c2 … id_cn <LINKS>
id_l1 tipo <N> id_n1 id_n2 …
<SQUARES>
id_p1 tipo <N> #nodi id_n1 id_n2 … id_nn …
<BLOCKS>
id-b1 tipo altezza <N> #nodi id_n1 id_n2 … id_nn
Questa rappresentazione è di facile utilizzo e lettura quasi quanto un file in formato xml, ma meno ingombrante in termini di dimensioni del file. Rispetto a un file binario, può a fini di debug, essere facilmente editata manualmente. Ciò non toglie che in sviluppi futuri, per
ridurne l’ingombro e velocizzarne il trasporto in rete, il formato ideale per questo file sia appunto un formato binario.
4.4. Descrizione
dell’editor
Parallelamente all’applicazione che genera automaticamente il CityGraph è stato implementato, usando XVR, un semplicissimo editor visuale. L’editor permette di leggere in input il file contenente la descrizione del CityGraph e di apportarvi alcune modifiche quali lo spostamento, la creazione e la distruzione di nodi, link, piazze e isolati oppure la possibilità di cambiare le proprietà di ciascuno di questi elementi, come il tipo e l’altitudine, oppure per quanto riguarda i link anche la lunghezza e la larghezza.
Al fine di rendere più semplici le operazioni di authoring, il city graph viene sovrapposto alla mappa raster originale. Sono inoltre impostabili vari livelli di zoom o di scroll ed è possibile regolare la trasparenza dell’immagine sullo sfondo.
figura 4.2 - Alcuni screenshot dell'editor