Selezione puntuale

In questa lezione per interrogazione di strati vettoriali si intende selezionare e ottenere informazioni da elementi di uno strato vettoriale già presenti sul client, ovvero già ottenuti da eventuali sorgenti remote ed eventualmente già visibili in mappa.

Cominciamo dal vedere come si possono eseguire selezioni di elementi vettoriali su mappa su base puntuale. Il modo più semplice è farlo tramite un’apposita interazione: ol.interaction.Select. Questa interazione permette di selezionare gli elementi di uno o più layer al click del mouse. Nel momento in cui vengono individuati uno o più elementi viene emesso l’evento select, sul quale registreremo la nostra funzione di gestione

1
2
3
4
5
6
7
8
9
10
11
var multiPointSelection = new ol.interaction.Select({
    layers: [poi],
    style: selectionStyle,
    // toggleCondition: ol.events.condition.never, // nel caso volessimo disabilitare le selezioni multiple
    // multi: false // nel caso volessimo limitare la selezione ad una singola feature
});
map.addInteraction(multiPointSelection);
multiPointSelection.on('select',function(evt){
    var lastSelectedFeature = evt.selected;
    var allSelectedFeatures = evt.target.getFeatures();
});

Istanziamo una ol.interaction.Select e, in questo esempio, definiamo l’unico layer su cui vogliamo che operi (un ol.layer.Vector) e lo stile con cui vogliamo che vengano disegnati gli elementi selezionati (di base è il celeste classico di OL). Due opzioni utili sono:

  • toggleCondition: tramite questa condizione è possibile definire un comportamento per permettere di aggiungere nuovi elementi alla selezione. Di default questa è impostata a ol.events.condition.shiftKeyOnly, cioè è possibile selezionare più elementi tenendo premuto il tasto Shift. Impostandola con ol.events.condition.never disabilitiamo la selezione multipla.
  • multi: definisce se al momento del click, in caso di più features insistenti sulla stessa coordinata, debbano essere selezionate tutte le features o una sola (la prima trovata). Questa proprietà è predefinita a true.

Aggiungiamo l’interazione alla mappa (riga 7) e poi ci mettiamo in ascolto dell’evento select. A riga 9, tramite la proprietà event.selected possiamo ottenere l’ultima feature selezionata, mentre se vogliamo accedere a tutte le feature selezionate possiamo ottenerle con il metodo o.interaction.Select.getFeatures(). La variabile this, nel contesto della callback, è l’istanza dell’interazione che ha generato l’evento.

Selezione areale

Per eseguire una selezione per rettangolo possiamo sfruttare ancora una volta ol.interaction.DragBox. Stavolta lo utilizzeremo per ottenere il rettangolo sulla base del quale selezioneremo le feature della nostra sorgente vettoriale. A differenza della selezione puntuale siamo noi a dover gestire sia la selezione delle feature che il layer vettoriale con cui, eventualmente, vorremo rappresentare le feature selezionate. Per quest’ultimo aspetto dobbiamo creare un layer vettoriale “di appoggio” che, di volta in volta, svuoteremo e ripopoleremo con le feature ottenute dalla selezione.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// layer vettoriale per mostrare le feature selezionate
var bboxFeatureOverlay = new ol.layer.Vector({
    source: new ol.source.Vector(),
    style: selectionStyle
});
map.addLayer(bboxFeatureOverlay);

var bboxSelection = new ol.interaction.DragBox();
map.addInteraction(bboxSelection);
bboxSelection.setActive(false);
bboxSelection.on('boxend', function(evt) {
    var bboxGeometry = this.getGeometry(); // ottengo la geometria del rettangolo di selezione
    var features = poi.getSource().getFeaturesInExtent(bboxGeometry.getExtent()); // ottengo le feature che intersecano il bbox
    
    var bboxFeatureSource = bboxFeatureOverlay.getSource();
    bboxFeatureSource.clear();  // tolgo i precdenti elementi dal layer dei risultati
    bboxFeatureSource.addFeatures(features); // e aggiungo i nuovi
});

La selezione effettiva avviene tramite il metodo ol.source.Vector.getFeaturesInExtent che si aspetta di ricevere un ol.Extent. Non è possibile usare una geometria poligonale, per cui questo tipo di selezione può essere fatta solo su base rettangolare.

Nel caso volessimo fare una selezione sulla base di una geometria poligonale, che sia ottenuta tramite disegno su mappa (che vedremo nell prossime lezioni) o da un elemento poligonale preesistente, possiamo sfruttare gli operatori di intersezione forniti da OL. Ad esempio:

1
2
3
4
5
6
7
8
9
10
11
var polygonBBOX = polygon.getExtent();
var indexSelection = poi.getSource().getFeaturesInExtent(polygonBBOX);

var selectedFeatures = [];
for (var i=0; i<indexSelection.length; i++) {
    var feature = indexSelection[i];
    var coordinates = feature.getGeometry()).getCoordinates();
    if (polygon.intersectsCoordinate(coordinates)) {
        selectedFeatures.push(feature);
    }
}

Nelle prime due righe sgrossiamo le feature del layer che volgiamo testare, allo stesso modo di come faremmo con una selezione rettangolare (e di come opera un inidice). Questo riduce il numero di elementi su cui testare l’intersezione col poligono, che è un’operazione costosa. Nel successivo ciclo otteniamo le coordinate della feature e verifichiamo se intersecano il poligono (riga 8). In tal caso l’aggiungiamo all’array delle feature selezionate.

OpenLayers offre solo un confronto per “intersezione”, quindi una relazione molto generale (l’inverso è il “disgiunto”). Per poter operare confronti sulla base delle altre relazioni geometriche elencate dallo standard OGC delle Simple Features (touch, contains, covers, ecc.) è necessario utilizzare altre librerie esterne come JSTS e Turf, che si integrano perfettamente con le strutture goemetriche di OpenLayers.