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

  1. 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
  2. Con jQuery peschiamo il file GeoJSON in coordinate WGS84 dal server (riga 6)
  3. All’arrivo della risposta prendiamo l’oggetto ol.Format che avevamo definito per la sorgente e leggiamo il file (riga 8)
  4. allo stesso tempo chiediamo al formato di riproiettare le feature da WGS84 a Google Mercator
  5. 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.