openlayersangular-openlayers

Snapping support for moving polygons in OpenLayers


OpenLayers has an example on how to implement snapping support for points on a map. Though I'm investigating whether snapping is also supported for moving and snapping polygons as a whole, instead of moving and snapping single points.

Though I have not found any documentation or example illustrating this. Looking in the API docs of OpenLayers, I see that Snap extends from PointerEvent, and I assume that the snapping behaviour only supports snapping at the position of the mouse pointer, which is not what I need.

Am I missing something? Or am I correct that snapping polygons as a whole currently is not supported in OpenLayers?


Solution

  • Use the Translate interaction as in https://openlayers.org/en/latest/examples/translate-features.html to move the polygons

    const translate = new Translate({
      source: vector.getSource(),
    });
    
    const snap = new Snap({
      source: vector.getSource(),
    });
    
    const map = new Map({
      interactions: defaultInteractions().extend([translate, snap]),
      layers: [vector],
      ...
    

    To snap one edge to another you need to drag the translated polygon by its edge. To avoid the Translate interaction dragging the interior of a polygon you could style fill and stroke separately and use a layer filter. Example:

    <!DOCTYPE html>
    <html>
      <head>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v9.2.4/ol.css">
        <style>
          .map {
            width: 100%;
            height: 400px;
          }
        </style>
        <script src="https://cdn.jsdelivr.net/npm/ol@v9.2.4/dist/ol.js"></script>
      </head>
      <body>
        <div id="map" class="map"></div>
        <script>
    
    const vector = new ol.layer.Vector({
      source: new ol.source.Vector({
        url: 'https://openlayers.org/data/vector/us-states.json',
        format: new ol.format.GeoJSON(),
      }),
      style: new ol.style.Style({
        stroke: new ol.style.Stroke({
          color: 'black',
          width: 2,
        }),
      }),
    });
    
    const vectorFill = new ol.layer.Vector({
      source: vector.getSource(),
      style: new ol.style.Style({
        fill: new ol.style.Fill({
          color: 'red',
        }),
      }),
    })
    
    const translate = new ol.interaction.Translate({
      source: vector.getSource(),
      layers: [vector],
    });
    
    const snap = new ol.interaction.Snap({
      source: vector.getSource(),
    });
    
    const map = new ol.Map({
      interactions: ol.interaction.defaults.defaults().extend([translate, snap]),
      layers: [vectorFill, vector],
      target: 'map',
      view: new ol.View({
        center: ol.proj.fromLonLat([-100, 38.5]),
        zoom: 4,
      }),
    });
    
        </script>
      </body>
    </html>