Per i layer vettoriali vale gran parte delle cose già descritte per i layer in generale e per quelli di immagini. Le differenze sostanziali riguardano la stilizzazione grafica e le sorgenti dati.

Una sorgente di dati vettoriali viene gestita dall’oggetto ol.source.Vector. Questa classe svolge fondamentalmente due funzioni:

  • contenitore della collezione di elementi vettoriali (features)
  • eventuale accesso a sorgenti vettoriali remote (file statici, tiles vettoriali, servizi WFS, ecc.)

ol.source.Vector quindi è in grado di gestire sia layer vettoriali locali che remoti. Ma andiamo per passi, cominciamo a vedere come sono fatte le feature.

Le feature

Una feature è un oggetto concettualmente molto semplice. Il modello è del tutto simile a quelli di molti software GIS: un contenitore di un singolo elemento geometrico, composta da una geometria e da una serie di attributi, ovvero coppie campo/valore. A differenza di modelli GIS più articolati non esiste il concetto di “schema” di un layer, dove normalmente vengono definiti il nome e il tipo dei campi del layer vettoriale (comuni a tutte le feature). In OpenLayers è un oggetto che ha vita propria e che può essere costruito al di fuori di un contenitore. Una collezione di features (una ol.Collection) è del tutto ignara di come siano fatte le feature contenute, per cui la loro corretta gestione (coerenza dei tipi geometrici e degli attributi) è deputata a chi le gestisce, noi.

Cominciamo intanto a vedere l’aspetto geometrico di una feature.

Geometrie

Le classi dedicate alla gestione delle geometrie si trovano sotto il namespace ol.geom.*. Ogni tipo geometrico in OL è gestito da una classe dedicata. La classe padre di tutti i tipi geometrici è ol.geom.Geometry, che fornisce i metodi e le proprietà comuni a tutte le goemetrie. Le classi figlie sono ol.geom.SimpleGeometry e ol.geom.GeometryCollection. Come si può desumere dal nome la prima gestisce geometrie singole (singlepart o multipart), mentre la seconda è dedicata alle collezioni di geometrie. Occupiamoci intanto delle geometrie singole.

  • punto: ol.geom.Point / ol.geom.MultiPoint
  • linea: ol.geom.LineString / ol.geom.MultiLineString
  • poligono : ol.geom.Polygon / ol.geom.MultiPolygon

(ci sono anche ol.geom.LinearRing e ol.geom.Circle come ulteriori specializzazioni).

Ogni classe geometrica offre vari metodi per la creazione e la manipolazione delle sue coordinate, che fondamentalmente altro non sono che una lista di coordinate (2D, 3D o 4D). Una coordinata in OL è rappresentata dall’oggetto ol.Coordinate che da un punto di vista pratico corrisponde ad un array di valori numerici lat/lon (coordinate geografiche), x/y (coordinate planari 2D), x/y/z (coordinate planari 3D) oppure x/y/z/m (coordinate planari con misura). Ogni volta che nelle API di OL vediamo il riferimento ad un oggetto ol.Coordinate possiamo quindi tradurlo mentalmente in un array numerico.

Creiamo la geometria puntuale del centro di Firenze:

var firenzeCenter = new ol.geom.Point([1681450.785672498, 4848967.319261061]);

Notare che nella definizione geometrica non si fa alcun riferimento al SR. Questo aspetto è demandato all’ol.layer.Vector che eventualmente conterrà le feature con le geometrie in un certo SR.

Per definire una linea o un poligono si procede in modo del tutto simile, ma stavolta useremo array innestati:

  • una linea è un array di coordinate, una multilinea un array di array di coordinate
  • un poligono è un array di array di coordinate (minimo uno per il suo exterior ring ed eventualmente uno o più ler gli interior rings, le isole), un moltipolygon è un array di array di array di coordinate

Se vogliamo creare una geometria lineare che rappresenta una porzione del fiume Arno, come quella in figura

LineString

dovremo creare una ol.geom.LineString a partire dall’array di coordinate:

var arno = new ol.geom.LineString([1240938.68420606502331793, 5432280.02825019601732492],[1243365.98971865512430668, 5432809.62218021601438522],[1245462.2990249830763787, 5433052.35273147467523813],[1246653.88536752760410309, 5432897.88783521857112646],[1248352.99922634079121053, 5431750.43432017602026463],[1252170.48880523280240595, 5429896.85556510742753744],[1255127.38824784266762435, 5428992.13260132353752851],[1257245.76396792149171233, 5429323.12880758568644524],[1259055.2098954888060689, 5428705.2692225631326437])

mentre per definire un poligono dell’estensione di un’area di Firenze

Polygon

dovremo costruire un array di coordinate come exterior ring del poligono, da inserire nell’array dei ring del poligono:

var centerBbox = new ol.geom.Polygon([[1250206.577981409849599, 5432169.69618144258856773],[1250206.577981409849599, 5427315.08515626192092896],[1255303.91955784941092134, 5427315.08515626192092896],[1255303.91955784941092134, 5432169.69618144258856773],[1250206.577981409849599, 5432169.69618144258856773]])

I tipi multipart non sono altro che ulteriori gradi di annidamento dei tipi semplici.

Le classi geometriche offrono un nutrito numero di metodi per la maniplazione della geometria, ovvero delle sue coordinate. Ci sono metodi per traslare, scalare e ruotare la geometria, metodi per verificare vicinanze e intersezioni tra coordinate ed elementi geometrici distinti. Esiste anche un metodo per la riproiezione dell’intera geometria: ol.geom.Geometry.transform(source, dest) è un metodo comune a tutti i tipi geometrici. Indicando la proiezione di origine e di destinazione OpenLayers eseguirà la trasformazione di ogni singola coordinate, e quindi dell’intera geometria.

var arnoWGS85 = arno.transform("ESPG:3857","EPSG:4326") // -> ottengo la LineString con coordinate WGS84

Attributi di una feature

Gli attributi di un oggetto vettoriale sono definiti in forma di oggetto literal Javascript, ovvero un oggetto contenente semplicemente il nome dei campi come chiavi e i loro valori come valori delle chiavi.

var attributiFirenze = {
    id: 14623,
    nome: "Firenze",
    cod: "D612",
    istat: "048017"
}

Costruiamo la feature

A questo punto siamo pronti per costruire la nostra feature usando la classe ol.Feature. Il metodo più chiaro consiste nel costruire una feature vuota e quindi inserire geometria e proprietà (attributi).

//METODO 1
var firenze = new ol.Feature();
firenze.setGeomety(firenzeCenter);
firenze.setProperties(attributiFirenze);

in alternativa si può creare direttamente una feature contenente solo la geometria e aggiungere le proprietà in seguito:

//METODO 2
var firenze = new ol.Feature(firenzeCenter);
firenze.setProperties(attributiFirenze);

oppure inserire tutte le proprietà (compresa quella geometrica identificata dalla chiave speciale geometry) in fase di creazione

//METODO 3
var firenze = new ol.Feature({
    goemetry: firenzeCenter,
    id: 14623,
    nome: "Firenze",
    cod: "D612",
    istat: "048017"
});

Definiamo la sorgente vettoriale

Per poter inserire le nostre feature in un layer è necessario creare una sorgente vettoriale che punti alle collezione delle nostra feature (una o più). Come consigliato in tutti i modelli GIS è consigliato distinguere le collezioni di feature per tipo gometrico. OL non darà errore se si mischiano oggetti puntuali con oggetti lineare nella stessa collezione, ma sicuramente emergerebbero problemi al momento della loro visualizzazione su mappa.

Abbiamo anticipato nell’introduzione di questa lezione che una sorgente vettoriale è gestita da oggetti ol.source.Vector. A questi oggetti, di base, interessa che gli si fornisca una collezione di feature, indipendentemente da dove provengono. Se sono feature costruite come oggetti ol.feature lui sarà in grado di usarle direttamente, altrimenti se l’origine (remota o locale) le fornisce in un determinato formato (es. GeoJSON, GML, MVT, ecc.) saremo noi a doverlo indicare in modo che ol.source.Vector possa eseguire tutte le operazioni per il recupero dei dati (es. da una URL remota) e processarle per costruire lui stesso la collezione di oggetti ol.Feature.

Vediamo intanto come possiamo usare la nostra feature puntuale di Firenze per costuire una sorgente di dati direttamente da oggetti ol.Feature (in questo caso contenente un solo elemento):

var comuni = new ol.source.Vector({
    features: [firenze] // array di oggetti ol.Feature
})

oppure

var comuni = new ol.source.Vector();
comuni.addFeature(firenze); // aggiungo successivamente la feature

Adesso è tutto pronto per poter creare il layer vettoriale ed inserirlo in mappa;

var layerComuni = new ol.layer.Vector({
    source: comuni
});
map.addLayer(layerComuni);

Ed ecco l’esempio completo. Tralasciate per ora la proprietà style del layer, di cui parleremo in un capitolo apposito. Notare che la geometria è stata trasformata in “ESPG:3857” al momento dell’inserimento nella feature.

Nel prossimo capitolo vedremo come usare sorgenti vettoriali remote e i formati di interscambio standard.