OpenLayers offre due interazioni dedicate alla modifica di strati e di collezioni di elementi vettoriali. ol.interaction.Draw, per il disegno di nuove feature, e ol.interaction.Modify per la modifica di elementi preesistenti. Entrambe le interazioni possono operare su insiemi di feature contenenti qualsiasi tipo di geometria, le quali dovranno essere fornite all’interazione in forma di ol.source.Vector (dalla versione 4.3.0 di OpenLayers) oppure di collezione ol.Collection di feature, ad esempio per modificare solo il sottoinsieme di elementi ottenuti da una selezione. Per creare e attivare l’interazione facciamo in modo del tutto simile alle altre interazioni già viste.

Inserimento

Per l’inserimento di nuovi elementi attiviamo ol.interaction.Draw definendo le opzioni minime source e type. Con quest’ultima indichiamo all’interazione il tipo geometrico che vogliamo disegnare, tra quelli gestiti da OpenLayers (vedi ol.geom.GeometryType):

var drawInteraction = new ol.interaction.Draw({
    source: layer.getSource(),
    type: 'Polygon'
});

È possibile eseguire un disegno a mano libera se si tiene premuto il tasto Shift della tastiera. Questa opzione può essere:

  • resa sempre attiva impostando l’opzione freehand: true
  • disabilitata impostando l’opzione freehandCondition: ol.events.condition.never
  • attivata con un altro tasto sempre definendo la freehandCondition con un altro modificatore

Il disegno avviene aggiungendo i singoli vertici col tasto sinistro del mouse, e viene terminato con un doppio click del mouse. Una volta terminato il disegno è possibile, per le linee e i poligoni, inserire ulteriori vertici sui segmenti già presenti posizionando il mouse nell’intorno del segmento

Nel momento in cui si inizia il disegno l’interazione non fornisce delle azioni per abortirlo o per rimuovere i vertici appena inseriti. Di per sé siamo obbligati a terminare l’inserimento con il doppio click del mouse. Esistono tuttavia due soluzioni per aggiungere queste funzionalità:

1
2
3
4
5
6
7
8
9
10
document.addEventListener('keydown', function(e) {
    if (e.which == 8) {
        drawInteraction.removeLastPoint();
    }
    
    if (e.which == 46) {
        drawInteraction.setActive(false);
        drawInteraction.setActive(true);
    }
});
  • Riga 1: ci mettiamo in ascolto di un evento keydown del browser (lo registriamo sul documento HTML)
  • Riga 2: verifichiamo se il tasto cliccato è il BACK / INDIETRO. Se sì usiamo un metodo dell’interazione per rimuovere l’ultimo vertice inserito
  • Riga 6: se il tasto è il DEL / CANC dobbiamo fare un trucchetto un po’ sporco. Disabilitiamo temporaneamente l’interazione, in modo che venga rimosso lo sketch del disegno in corso, e immediatamente dopo la riabilitiamo. Così abbiamo ripulito il disegno.

Un’opzione più avanzata di ol.interaction.Draw permette di gestire anche la funzione da usare per generare la feature in base alle azioni di disegno. Di base questa funzione genera la geometria che ci aspetta, per i punti, le linee e i poligoni. Supponiamo invece di voler disegnare dei rettangoli. In questo caso possiamo immaginare di voler cliccare in un punto e trascinare il mouse per definire il vertice di un angolo del rettangolo. Per far ciò dobbiamo definire l’opzione geometryFunction con una funzione che generi il rettangolo. Fortunatamente ol.interaction.Draw ha già un paio di funzioni geometriche disponibili, tra cui una proprio per il disegno di rettangoli (o altri poligoni regolari), utilizzabile con il tipo “Circle”.

var drawInteraction = new ol.interaction.Draw({
    source: layer.getSource(),
    type: 'Circle',
    geometryFunction: new ol.interaction.Draw.createRegularPolygon(4)
});

La funzione viene generata con il metodo ol.interaction.Draw.createRegularPolygon a cui possiamo indicare il numero di lati del poligono regolare da disegnare (di default è 32).

Se proviamo ad usare questa interazione vedremo che il rettangolo viene ruotato in base alla posizione del mouse dopo il secondo click. E se invece volessimo fare in modo che i rettangoli disegnati non fossero ruotati? A questo punto dobbiamo definire una nostra funzione apposita.

function(coordinates, sketchGeometry) {
    var angle = 0;
    var radians = Math.PI/4 + (Math.PI * angle / 180);
    var sides = 4;
    var center = coordinates[0];
    var end = coordinates[1];
    var radius = Math.sqrt(ol.coordinate.squaredDistance(center, end));
    var geometry = sketchGeometry ? sketchGeometry : ol.geom.Polygon.fromCircle(new ol.geom.Circle(center), sides);
    ol.geom.Polygon.makeRegular(geometry, center, radius, radians);
    return geometry;
}

La funzione viene chiamata per ogni vertice inserito. La prima volta verrà fornita solo l’argomento coordinates, con le coordinate del punto cliccato, le seguenti verrà passata anche sketchGeometry, ovvero la geometria “che sarebbe stata disegnata se non ci fossimo messi in mezzo noi con la nostra funzione”. Noi modifichiamo questa geometria con un po’ di metodi offerti da OpenLayers per ottenere un poligono regolare e la restituiamo indietro. L’interazione a questo punto userà la nostra geometria per disegnarla sulla mappa.

Modifica

La modifica si attiva con una ol.interaction.Modify.

var modifyInteraction = new ol.interaction.Modify({
    source: layer.getSource()
});
map.addInteraction(modifyInteraction);
  • Si possono inserire nuovi vertici all’interno di segmenti preesistenti posizionando il mouse finché non si attiva lo snap del vertice sul segmento.
  • Si possono rimuovere vertici preesistenti cliccando sul vertice tenendo premuto il tast ALT. Questo modificatore può essere cambiato con l’opzione deleteCondition che di default è la seguente:
    function(mapBrowserEvent) {
        return ol.events.condition.altKeyOnly(mapBrowserEvent) && ol.events.condition.singleClick(mapBrowserEvent);
    };

Rimozione

Non esiste un’interazione specifica per rimuovere una o più features. Trattandosi di un’azione a due passi (selezione + rimozione), la rimozione viene normalmente implementata usando un’interazione di selezione seguita da una qualche azione (es. clck su un pulsante HTML o un tasto della tastiera) che provocherà la rimozione delle feature dalla sorgente o dalla collezione in cui si trovano.

Per rimuovere gli elementi selezionati usando il tasto CANC potremmo scrivere:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var selectInteraction = new ol.interaction.Select({
    source: poligoni.getSource()
});
map.addInteraction(selectInteraction);

document.addEventListener('keydown', function(e) {
    if (e.which == 46) {
        var selectedFeatures = selectInteraction.getFeatures();
        var source = poligoni.getSource();
        selectedFeatures.forEach(function(feature){
            source.removeFeature(feature);
        });
        selectedFeatures.clear();
    }
});
  • Riga 8: prendiamo le feature selezionate.
  • Riga 10: cicliamo su tutte le features e le rimuoviamo una ad una dalla sorgente, perché ol.source.Vector permette di rimuovere solo una feature alla volta.
  • Riga 13: puliamo la selezione.

NB: È necessario pulire la selezione, ovvero rimuovere le features dalla collezione delle features selezionate, altrimenti l’interazione si rompe perché a quel punto avrebbe un riferimento a delle features già rimosse dalla sorgente.