Search code examples
javascriptgisopenlayers

Add tooltip on hover to node in OpenLayers


I have an OpenLayers map which people can draw and modify geometries on. For Polygons and LineStrings you are able to delete the individual nodes by holding down alt and clicking the node, but this is not obvious to the user without further instruction.

I want to be able to add a tooltip when a user hovers over a node telling them that they can do this, but I can't figure out the exact steps to do it.

Take the following code, modified from the Draw and Modify and Custom Polygon Styles examples (working code sandbox below)

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

const source = new VectorSource();
const vector = new VectorLayer({
  source: source,
  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"
        })
      })
    }),
    new Style({
      image: new CircleStyle({
        radius: 5,
        fill: new Fill({
          color: "orange"
        })
      }),
      geometry: function (feature) {
        // return the coordinates of the first ring of the polygon
        const coordinates = feature.getGeometry().getCoordinates()[0];
        return new MultiPoint(coordinates);
      }
    })
  ]
});

// Limit multi-world panning to one world east and west of the real world.
// Geometry coordinates have to be within that range.
const map = new Map({
  layers: [raster, vector],
  target: "map",
  view: new View({
    center: [-11000000, 4600000],
    zoom: 4
  })
});

const modify = new Modify({
  source: source
});
map.addInteraction(modify);

let draw, snap; // global so we can remove them later
const typeSelect = document.getElementById("type");

function addInteractions() {
  draw = new Draw({
    source: source,
    type: typeSelect.value
  });
  map.addInteraction(draw);
  // snap = new Snap({ source: source });
  // map.addInteraction(snap);
}

addInteractions();

https://codesandbox.io/s/draw-and-modify-features-forked-cl85wd?file=/main.js:1860-1975

This uses a custom polygon style to show the nodes, what I would like is, when you hover over the node it comes up with a small tooltip alongside it telling the user how to delete the node.

I feel this could be done one of two ways, but both I am stuck on a particular part.

Option 1: Add it to the style. You can add 'text' to the style used to render the nodes, but this will apply always and I can't see a way of only making it apply when the individual node is hovered.

Option 2: Add a 'pointerMove' event and detect when you are hovering a node, then show a tooltip next to the mouse (much like how the measure example has a tooltip next to the mouse). I can detect when a feature is hovered over, but not whether the user is hovering over a node (and in range for the Alt Click deletion to work)


Solution

  • Taking Mikes suggestions in the comments, I ended up with a solution where the the help text would appear on hover of a node and only is there were more than 3 coordinates in the polygon (any less and you can't delete a node, as it would then no longer be a valid polygon.

    The relevant code is here

    modify.getOverlay().setStyle(function (feature) {
      const coordinate = feature.getGeometry().getCoordinates();
      let style = defaultEditingStyleFunction;
      map.forEachFeatureAtPixel(map.getPixelFromCoordinate(coordinate), function (
        feature
      ) {
        const geometry = feature.getGeometry();
        if (geometry.getType() === "Polygon") {
          const coordinates = geometry
            .getCoordinates()[0]
            .map(function (coordinate) {
              return coordinate.toString();
            });
          if (
            coordinates.length > 4 &&
            coordinates.includes(coordinate.toString())
          ) {
            style = function () {
              return vertexStyle;
            };
            return true;
          }
        }
      });
      return style(feature);
    });
    

    Full codesandbox example