Sorgenti remote e formati vettoriali
Definizione del formato di una sorgente
Nel precedente capito abbiamo creato una sorgente impiegando direttamente una lista di oggetti ol.Feature
, tuttavia è più usuale dover definire dei layer vettoriali a partire da strati rappresentati nei formati più comuni nei WebGIS, come ad quelli di tipo testuale come GeoJSON, TopoJSON, KML, GML, oppure binari come il Mapbox Vector Tiles, che usa il protocollo Protobuf (PBF).
Una sorgente ol.source.Vector è ignara di come si interpretino i vettoriali in questi formati. Per far sì che li possa impiegare sta a noi specificare il formato della nostra sorgente tramite un oggetto di tipo ol.format.*
. OL ne fornisce già una serie già pronti all’uso, tra cui:
- ol.format.GeoJSON
- ol.format.TopoJSON
- ol.format.WFS
- ol.format.KML
- ol.format.GML
- ol.format.MVT
- ol.format.GPX (dati GPS in formato NMEA)
Riprendiamo l’esempio geometrico semplificato della porzione del fiume Arno. La sua rappresentazione all’interno di un layer vettoriale GeoJSON
var arnoGeoJSON = {
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{
"type": "Feature",
"properties": { },
"geometry": {
"type": "LineString",
"coordinates": [ [ 1240938.684206065, 5432280.028250196 ], [ 1243365.9897186551, 5432809.622180216 ], [ 1245462.2990249831, 5433052.3527314747 ], [ 1246653.8853675276, 5432897.8878352186 ], [ 1248352.9992263408, 5431750.434320176 ], [ 1252170.4888052328, 5429896.8555651074 ], [ 1255127.3882478427, 5428992.1326013235 ], [ 1257245.7639679215, 5429323.1288075857 ], [ 1259055.2098954888, 5428705.2692225631 ] ]
}
}
]
}
Per far sì che ol.source.Vector
possa interpretarlo correttamente dovremo usare il formato ol.format.GeoJSON
e più precisamente il suo metodo readFeatures
che ritornerà una lista di ol.Feature
pronte per essere inserite nell’ ol.source.Vector
.
var fiumi = new ol.source.Vector();
var geoJSONFormat = new ol.format.GeoJSON();
fiumi.addFeatures(geoJSONFormat.readFeatures(arnoGeoJSON)); // aggiungo successivamente la feature
Sorgenti remote
Per poter sfruttare gli stessi formati ma da sorgenti remote manca solo di aggiungere l’URL del servizio. In questo caso però l’interpretazione del formato dobbiamo delegarla agli strumenti interni usati da ol.source.Vector
per ottenere i dati dal server remoto. Infatti in questo caso il formato dovrà essere semplicemente specificato in fase di inizializzazione della sorgente, poi sarà lei ad usarla quando opportuno.
var fiumi = new ol.source.Vector({
url: "http://nostroserver.it/dati/arno.geojson", // esempio di URL
format: new ol.format.GeoJSON()
});
Nel momento in cui il layer vettoriale sarà inserito in mappa chiederà a ol.source.Vector
i dati per una certa estensione e zoom, e ol.source.Vector
, tramite il proprio ol.FeatureLoader
, interrogherà il server ed eseguirà il parsing dei dati ritornati.
var fiumiSource = new ol.source.Vector({
url: "/assets/dati/fiumi.geojson",
format: new ol.format.GeoJSON()
});
var fiumi = new ol.layer.Vector({
source: fiumiSource
})
Nel caso in cui i dati GeoJSON non fossero nello stesso SR della mappa si aggiunge una complicazione: starà a noi gestire direttamente il flusso di recupero dei dati dal server, la loro riproiezione e l’inserimento nella sorgente vettoriale.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var fiumiSource = new ol.source.Vector({
url: "/assets/dati/fiumiWGS84.geojson",
format: new ol.format.GeoJSON(),
loader: function(extent, res, proj) {
var self = this;
$.get(self.getUrl()) // this è l'istanza ol.source.Vector
.done(function(data) {
var features = self.getFormat().readFeatures(data,{
dataProjection: "EPSG:4326",
featureProjection: "EPSG.3857"
})
self.addFeatures();
})
}
});
var fiumi = new ol.layer.Vector({
source: fiumiSource
})
La proprietà loader
si aspetta una funzione (ol.FeatureLoader
) che verrà richiamata ogni volta che la sorgente avrà bisogno dei dati. Quante volte viene richiamata dipende dalla strategia impostata nelle porprietà di ol.source.Vector
. Di default viene usata una strategia ol.strategy.all
, ovvero la sorgente chiederà una sola volta tutti i dati non appena il layer a cui appartiene viene inserito in mappa.
Quindi nel nostro esempio
- la funzione viene chiamata una prima (e unica volta) con i seguenti parametri:
- extent: estensione attuale della mappa
- res: risoluzione attuale della mappa
- proj: proiezione della mappa;
this
nella funzione si riferisce all’ol.source.Vector
- Con jQuery peschiamo il file GeoJSON in coordinate WGS84 dal server (riga 6)
- All’arrivo della risposta prendiamo l’oggetto
ol.Format
che avevamo definito per la sorgente e leggiamo il file (riga 8) - allo stesso tempo chiediamo al formato di riproiettare le feature da WGS84 a Google Mercator
- Inseriamo le feature nella sorgente (riga 12)
Nel caso il nostro servizio dati supportasse il filtraggio delle richieste per BBOX potremmo impostare una diversa strategia di caricamento, ol.loadingstrategy.bbox
. In questo modo la sorgente chiamerà la nostra funzione ogni qual volta ci spostiamo o zoomiamo nella mappa e noi potremo sfruttare i valori dei parametri per generare le chiamate al nostro backend, in modo del tutto simile a quello che avviene con i servizi di immagini per tile o WMS.
var fiumiSource = new ol.source.Vector({
url: "/assets/dati/fiumiWGS84.geojson",
format: new ol.format.GeoJSON(),
strategy: ol.strategy.bbox
loader: function(extent, res, proj) {
// la funzione sarà chiamata ad ogni pan e zoom sulla mappa
}
});
NOTA BENE: In un layer vettoriale con una sorgente di dati da remoto, il caricamento effetivo dei dati inizia nel momento in cui viene inserito in mappa ed avviene in modo asincrono. Questo significa che immediatamente dopo l’aggiunta alla mappa la sorgente vettoriale risulterà ancora vuota, per cui non è possibile eseguire funzioni che necessitino dei suoi dati. Ad esempio se volessimo adattare la vista della mappa all’estensione dei dati, dovremmo farlo solo dopo esserci assicurati che i dati siano effettivamente arrivati e siano stati aggiunti alla sorgente vettoriale. Per verificarlo possiamo registrare una callback sull’evento change
di ol.source.Vector
, all’interno della quale, dopo aver controllato che che effettivamente la sorgente sia pronta, potremo posizionare la vista sulle feature del layer:
fiumiSource.once('change', function(){
// this si riferisce all'oggetto che ha emesso l'evento, in questo caso la sorgente vettoriale
if (this.getState() == 'ready'){
map.getView().fit(this.getExtent());
}
})
Notare che abbiamo usato la registrazione once
in modo che la funzione venga eseguita una sola volta.
Servizi WFS
Il caso dei servizi WFS è molto simile, tuttavia dobbiamo fare un’aggiunta. A differenza delle sorgenti ol.source.ImageWMS
per il WFS non esiste una sorgente apposita, per cui dovendo usare una sorgente vettoriale generica è necessario gestire diversamente la URL del servizio.
Mentre nel caso di un file la URL è, in genere, statica, nel caso di un servizio WFS le chiamate HTTP dovranno essere opportunamente parametrizzate, come minimo col BBOX dei dati richiesti. Per questo scopo l’ ol.source.Vector
mette a disposizione un’altra opportunità: generare noi, tramite una funzione, la URL che di volta in volta dovrà essere indirizzata al server WFS
Prendiamo per esempio il layer degli ingombri delle particelle catastali regionali fornito dal servizio WFS della Regione Toscana
var particelleSource = new ol.source.Vector({
url: function(extent) {
var baseUrl = "http://www502.regione.toscana.it:80/wfsvector/com.rt.wfs.RTmap/wfs";
var layer = "sita:idcatastoparticellebbrtpoly";
var bbox = extent.join(',');
return baseUrl + "&service=WFS&" +
"version=1.1.0&request=GetFeature&" +
"outputFormat=application/json&srsname=EPSG:3857&" +
"typename=" + layer + "&extent=" + bbox + "";
}
format: new ol.format.GeoJSON(), // oppure ol.format.GML2 / ol.format.GML3 in base a quello che offre il server WFS
strategy: ol.loadingstrategy.bbox
});
Questo è sufficiente fintanto che il server espone i layer nel SR della mappa, altrimenti dobbiamo tornare a definire un nostro loader
nel quale riproietteremo le feature prima di passarle alla sorgente vettoriale.