Search code examples
javascriptopenlayerspolygondrag

Example of limiting a vertex from being dragged outside a polygon in OpenLayers6


I have the following situation:

The base polygon is where I defined that all other polygons should be drawn.

When using the draw interaction, setting the condition option is enought to limit where the point is placed on the map (in my case, the base polygon).

The problem is when I'm modifying them. I can drag a vertex outside the base polygon and I want to disable that behaviour but I can't find any solution to that.

If I use the same condition option from the draw interaction on the modify interaction, it disables the dragging only after the vertex was placed.

So my problem comes down to disabling drag outside a specified polygon for any polygon inside it.

Edit 1: In the end I've ended up with this:

    const constrainGeometry = (feature: RenderFeature | Feature<Geometry>) => {
      const geometry = feature.getGeometry() as Polygon | undefined;
      if (feature instanceof RenderFeature || geometry === undefined) {
        return geometry;
      }
      const coordinates = geometry.getCoordinates()[0].map((coordinate) => {
        const boundingGeometry = props.boundingFeature.getGeometry();
        if (boundingGeometry) {
          if (boundingGeometry.intersectsCoordinate(coordinate)) {
            return coordinate;
          } else {
            return boundingGeometry.getClosestPoint(coordinate);
          }
        } else {
          return coordinate;
        }
      });
      return new Polygon([coordinates]);
    };

Haven't found a good solution of working with TS but for the moment seems ok.


Solution

  • There is no condition which applies while dragging. You could style your feature with a constrained geometry, and set that geometry at the end of the modify.

    <!doctype html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.8.1/css/ol.css" type="text/css">
        <style>
          html, body, .map {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
          }
        </style>
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.8.1/build/ol.js"></script>
      </head>
      <body>
        <div id="map" class="map"></div>
        <script type="text/javascript">
    
          const boundingPolygon = ol.geom.Polygon.fromCircle(
            new ol.geom.Circle([0, 0], 1e6),
            5
          );
    
          const constrainGeometry = function(feature) {
            const geometry = feature.getGeometry();
            if (geometry.getType() === 'Polygon') {
              coordinates = geometry.getCoordinates()[0].map(function(coordinate) {
                if (boundingPolygon.intersectsCoordinate(coordinate)) {
                  return coordinate;
                } else {
                  return boundingPolygon.getClosestPoint(coordinate);
                }
              });
              return new ol.geom.Polygon([coordinates]);
            } else {
              return geometry;
            }
          }
    
          const style = new ol.style.Style({
            geometry: constrainGeometry,
            fill: new ol.style.Fill({
              color: 'rgba(255, 255, 255, 0.2)',
            }),
            stroke: new ol.style.Stroke({
              color: '#ffcc33',
              width: 2,
            }),
            image: new ol.style.Circle({
              radius: 7,
              fill: new ol.style.Fill({
                color: '#ffcc33',
              }),
            }),
          });
    
          const vector1 = new ol.layer.Vector({
            source: new ol.source.Vector({
              features: [
                new ol.Feature(boundingPolygon)
              ]
            })
          });
    
          const vector2 = new ol.layer.Vector({
            source: new ol.source.Vector({
              features: [
                new ol.Feature(ol.geom.Polygon.fromExtent([-5e5, -5e5, 5e5, 5e5]))
              ]
            }),
            style: style
          });
    
          const map = new ol.Map({
            target: 'map',
            layers: [
              new ol.layer.Tile({
                source: new ol.source.OSM()
              }),
              vector1,
              vector2
            ],
            view: new ol.View({
              center: [0, 0],
              zoom: 4
            })
          });
    
          const modify = new ol.interaction.Modify({
            source: vector2.getSource()
          });
    
          modify.on('modifyend', function (event) {
            event.features.forEach(function (feature) {
              feature.setGeometry(constrainGeometry(feature));
            });
          });
    
          map.addInteraction(modify);
    
        </script>
      </body>
    </html>