Search code examples
javascriptopenlayers-5

Snapping to features on multiple layers


I am having problems using the OpenLayers snapping functionality. I need to have several layers on my map and use the snap on all. I have tried an option creating a collection but it has not worked. Another option does work but I don't know if it is the most optimal and I would need to confirm if there are any more options.

I have created a collection regarding the OpenLayers API documentation, I have several layers

//Map layers added

var raster = new TileLayer({
    source: new OSM()
});

var vectorLayer = new VectorLayer({
    source: new VectorSource({
        url: 'https://openlayers.org/en/latest/examples/data/geojson/countries.geojson',
        format: new GeoJSON()
    })
});


var vectorLayer2 = new VectorLayer({
    source: new VectorSource({
        url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_110m_admin_1_states_provinces_shp.geojson',
        format: new GeoJSON()
    })
});

var vector = new VectorLayer({
    source: new VectorSource(),
    style: new Style({
        fill: new Fill({
            color: 'rgba(255, 255, 255, 0.2)'
        }),
        stroke: new Stroke({
            color: '#ffcc33',
            width: 2
        }),
        image: new CircleStyle({
            radius: 7,
            fill: new Fill({
                color: '#ffcc33'
            })
        })
    })
});

If I create a snap object for each of the layers if it works but I do not see that this case is very practical and I am looking for if there is any other option. This code does work.

var snapCollection = new Collection({
    unique: true
});

[vector, vectorLayer, vectorLayer2].forEach(function(layer) {
    layer.getSource().on('addfeature', function(evt) {
        snapCollection.push(evt.feature);
    });
    layer.getSource().on('removefeature', function(evt) {
        snapCollection.remove(evt.feature);
    });
});

var map = new Map({
    layers: [raster, vector, vectorLayer],
    target: 'map-container',
    view: new View({
        projection: 'EPSG:4326',
        center: [-100.937, 38.725],
        zoom: 15
    })
});

var snapPrueba = new Snap({
    features: snapCollection
});

snapPrueba.on('change', function(event) {
    console.log(this.target);
});

map.addInteraction(snapPrueba);


Can anybody help me?


Solution

  • The collection takes an array of features (not sources) as a parameter (not an option):

    features: new Collection(
        vectorLayer2.getSource().getFeatures().concat(vector.getSource().getFeatures()).concat(vectorLayer.getSource().getFeatures()),
        { unique: true }
    )
    

    You would also need to maintain the collection if features are to be added or removed from the layers, so it might not be any more practical than separate interactions.

    var snapCollection = new Collection([], {
        unique: true
    });
    
    [vector, vectorLayer, vectorLayer2].forEach(function(layer) {
        layer.getSource().on('addfeature', function(evt) {
            snapCollection.push(evt.feature);
        });
        layer.getSource().on('removefeature', function(evt) {
            snapCollection.remove(evt.feature);
        });
    });
    
    var map = new Map({
        layers: [raster, vector, vectorLayer],
        target: 'map-container',
        view: new View({
            projection: 'EPSG:4326',
            center: [-100.937, 38.725],
            zoom: 15
        })
    });
    
    var snapPrueba = new Snap({
        features: snapCollection
    });
    
    snapPrueba.on('change', function(event) {
        console.log(this.target);
    });
    
    map.addInteraction(snapPrueba);