2

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?

1 Answers1

2

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);
Mike
  • 16,042
  • 2
  • 14
  • 30
  • I have modified my code with which you have provided me and it has not worked :( it does not Snapping @Mike – joseantonio Sep 20 '19 at 12:09
  • Are your vector layers pre-populated with features or are they loaded from a url (i.e. asynchronously after the map opens)? – Mike Sep 20 '19 at 12:18
  • My layers are loaded from a url. All charges as the following example. Sorry, I'm new with OpenLayers. ```var vectorLayer = new VectorLayer({ source: new VectorSource({ url: 'https://openlayers.org/en/latest/examples/data/geojson/countries.geojson', format: new GeoJSON() }) });``` – joseantonio Sep 20 '19 at 12:32
  • `getFeatures()` will return an empty array until the url has loaded, so you **will** need to maintain the collection as features are added (and removed if you intend removing them). I've added code to do that to my answer. – Mike Sep 20 '19 at 13:04
  • It still doesn't work with your last answer. I am sorry that it is not working for me :(. Thank you very much for your effort @Mike – joseantonio Sep 23 '19 at 07:25
  • I have modified the primary code that I wrote in the publication in case you think you can help me. All my code is like this currently. Thank you. @Mike – joseantonio Sep 23 '19 at 08:19
  • If you specify options for the collection object it must have an array parameter, even if empty. Also the snap interaction doesn't have an event where you can get features as it is intended to be used with other interactions such as select and modify. You can obtain the features from those https://codesandbox.io/s/select-features-p0n4f Note the large pixel tolerance of the snap which allows a neighbouring features to be selected. – Mike Sep 23 '19 at 15:32
  • In the end, the code worked, using the collection, the "draw" layer was deactivated and therefore it did not work. Thank you! – joseantonio Sep 24 '19 at 07:01