Search code examples
javascriptgeometryopenlayers

Problem with geometryFunction for draw interaction


I'm using geometryFunction in draw interaction to change geometry coords of current drawing and I'm also using styling function for handling labels etc.

The problem is that style function gets couple features to handle while drawing but geometryFunction returns only one.

For instance when I'm drawing LineString, geometryFunction returns modified LineString geometry, but style handles 2 features with Point and LineString geometry so feature with Point geometry is not modified.

Same when drawing Polygon: style function handles Point, LineString and Polygon features, but geometryFunction returns only Polygon geometry and in style function only feature with Polygon geometry is modified.

I've tried many ways of changing geometries in style function, but it didn't work as expected.

The question is how to handle coordinate change on pointermove for all features while using draw interaction?

This is my geometryFunction:

const obliqueGeom = (coords, geom) => {
  const center = this.obliCoords[0];
  const last = this.obliCoords[this.obliCoords.length - 1];
  const dx = center[0] - last[0];
  const dy = center[1] - last[1];
  const radius = Math.sqrt(dx * dx + dy * dy);

  if (!geom) {
    if (measureType === "LineString") {
      geom = new ol.geom.LineString(this.obliCoords)
    }
    if (measureType === "Polygon") {
      geom = new ol.geom.Polygon([this.obliCoords])
    }
    if (measureType === "Circle") {
      geom = new ol.geom.Circle(center, radius)
    }
    if (measureType === "Point") {
      geom = new ol.geom.Point(this.obliCoords)
    }
  } else {
    if (geom instanceof ol.geom.Circle) {
      geom.setCenterAndRadius(center, radius)
    } else if (geom instanceof ol.geom.Polygon) {
      geom.setCoordinates([this.obliCoords])
    } else {
      geom.setCoordinates(this.obliCoords)
    }
  }

  return geom
}

That's how I'm collecting coords:

this.clickEvt = this.map.on("click", (evt) => {
  newPx = [evt.pixel[0], evt.pixel[1] / 0.83]
  newCoord = this.map.getCoordinateFromPixel(newPx)
  if (measureType !== "Polygon") {
    this.obliCoords.push(newCoord)
  } else {
    if (this.obliCoords.length <= 1) {
      this.obliCoords.splice(0, 0, newCoord)
      this.obliCoords.push(newCoord)
    } else {
      this.obliCoords.splice(this.obliCoords.length - 1, 0, newCoord)
    }
  }
})

this.moveEvt = this.map.on("pointermove", (evt) => {
  newPx = [evt.pixel[0], evt.pixel[1] / 0.83]
  newCoord = this.map.getCoordinateFromPixel(newPx)
  if (measureType !== "Polygon") {
    this.obliCoords.pop()
    this.obliCoords.push(newCoord)
  } else {
    if (this.obliCoords.length <= 1) {
      this.obliCoords.pop()
      this.obliCoords.push(newCoord)
    } else {
      this.obliCoords.splice(this.obliCoords.length - 2, 1, newCoord)
    }
  }
})

Solution

  • Depending on the type specified when constructing the interaction the interaction's overlay will have up to 3 features draw.getOverlay().getSource().getFeatures().length One will have the geometry returned by the geometry function, one will have a point geometry using the final coordinate passed to the geometry function (the pointer position), and in the case of polygon type one will be a linestring feature made from the input cooordinates to the geometry function. In a custom style function you might wnat to ignore some geometry types, and if the polygon has been modified by the geometry function return an array of styles based on that to substitute for the auto-generated features which no longer match the polygon, for example

    function(feature) {
      const geometry = feature.getGeometry();
      if (geometry.getType() == 'Polygon') {
        const coordinates = geometry.getCoordinates()[0];
        return [
          new Style({
            fill: new Fill({
              color: fillColor
            })
          }),
          new Style({
            stroke: new Stroke({
              width: 2,
              color: strokeColor
            }),
            geometry: new LineString(coordinates.slice(0, -1))
          }),
          new Style({
            image: new CircleStyle({
              radius: 5,
              fill: circleFill,
              stroke: circleStroke
            }),
            geometry: new Point(coordinates[coordinates.length - 2])
          })
        ];
      }
    }
    

    Note that you should close a polygon

    geom = new ol.geom.Polygon([this.obliCoords.concat([this.obliCoords[0]])])