mapview_interactions_draw.mjs

/**
## Mapview.interactions.draw()

@module /mapview/interactions/draw

@param {Object} params
The params object argument.
*/

export default function (params) {
  const mapview = this;

  // Finish the current interaction.
  mapview.interaction?.finish();

  // Assign params onto the defaults as mapview.interaction.
  mapview.interaction = {
    type: 'draw',

    finish,

    getFeature,

    format: new ol.format.GeoJSON(),

    source: new ol.source.Vector(),

    // The Layer is required for generated features such as isolines.
    Layer: new ol.layer.Vector({
      zIndex: params.layer?.zIndex || 99,
    }),

    // Bind context menu from mapp ui elements.
    drawend: mapp.ui?.elements.contextMenu.draw.bind(this),

    vertices: [],

    // Whether the draw interaction event should be handled.
    condition: (e) => {
      if (mapview.interaction.wait) return false;

      // Right click
      if (e.originalEvent.buttons === 2) {
        // Remove last vertex.
        mapview.interaction.interaction.removeLastPoint();
        mapview.interaction.vertices.pop();

        const moveEvent = new ol.MapBrowserEvent(
          'pointermove',
          mapview.Map,
          e.originalEvent,
        );
        mapview.interaction.interaction.handleEvent(moveEvent);

        mapview.interaction.conditions?.forEach(
          (fn) => typeof fn === 'function' && fn(e),
        );
        return false;
      }

      // Left click.
      if (e.originalEvent.buttons === 1) {
        mapview.interaction.vertices.push(e.coordinate);
        mapview.popup(null);

        mapview.interaction.conditions?.forEach(
          (fn) => typeof fn === 'function' && fn(e),
        );
        return true;
      }
    },

    // OL Style for sketch feature.
    style: [
      new ol.style.Style({
        stroke: new ol.style.Stroke({
          color: '#3399CC',
          width: 1.25,
        }),
      }),
      new ol.style.Style({
        image: new ol.style.Circle({
          radius: 5,
          fill: new ol.style.Fill({
            color: '#eee',
          }),
          stroke: new ol.style.Stroke({
            color: '#3399CC',
            width: 1.25,
          }),
        }),
        geometry: mapp.utils.verticeGeoms,
      }),
    ],

    // Spread params argument.
    ...params,
  };

  // Change cursor style over mapview element.
  mapview.Map.getTargetElement().style.cursor = 'crosshair';

  // Set mapview.interaction.Layer source.
  mapview.interaction.Layer.setSource(mapview.interaction.source);

  // Add mapview.interaction.Layer to mapview.
  mapview.Map.addLayer(mapview.interaction.Layer);

  document.addEventListener('keyup', escape);

  function escape(e) {
    e.key === 'Escape' && mapview.interaction.finish();
  }

  // Create OL draw interaction.
  mapview.interaction.interaction = new ol.interaction.Draw(
    mapview.interaction,
  );

  // Set drawstart event method.
  mapview.interaction.interaction.on('drawstart', (e) => {
    // Get the draw feature geometry.
    const geometry = e.feature.getGeometry();

    async function onChange() {
      mapview.popup({
        content: mapp.utils.html.node`
          <div style="padding: 5px">
          ${await mapp.utils.convert(
            // Get the geometry metric figure.
            mapview.metrics[mapview.interaction.tooltip.metric](geometry),

            // Options argument for conversion.
            mapview.interaction.tooltip,
          )}`,
      });
    }

    // Assign an onchange method to the geometry for the tooltip.
    mapview.interaction.tooltip &&
      geometry.on('change', mapview.interaction.tooltip.onChange || onChange);

    // Clear the source
    mapview.interaction.source.clear();

    // Remove the popup.
    mapview.popup(null);
  });

  if (typeof mapview.interaction.drawend === 'function') {
    mapview.interaction.interaction.on('drawend', mapview.interaction.drawend);
  }

  // Add OL interaction to mapview.Map
  mapview.Map.addInteraction(mapview.interaction.interaction);

  // Assign snap interaction.
  mapview.interactions.snap(mapview);

  // Get first feature from mapview.interaction.source as GeoJSON.
  function getFeature() {
    // Return feature as geojson.
    return JSON.parse(
      mapview.interaction.format.writeFeature(
        // Get first OL feature from source.
        mapview.interaction.source.getFeatures()[0],
        {
          // Use mapview.interaction.srid as dataProjection if defined in params.
          dataProjection:
            'EPSG:' + mapview.interaction.layer?.srid ||
            mapview.interaction.srid ||
            mapview.srid,
          featureProjection: 'EPSG:' + mapview.srid,
        },
      ),
    );
  }

  function finish(feature) {
    document.removeEventListener('keyup', escape);

    // Remove snap interaction.
    mapview.interaction.snap?.remove?.();

    // Reset the cursor style.
    mapview.Map.getTargetElement().style.cursor = 'default';

    // Remove popup from mapview.
    mapview.popup(null);

    // Remove interaction from mapview.Map.
    mapview.Map.removeInteraction(mapview.interaction.interaction);

    // Remove draw Layer from mapview.Map.
    mapview.Map.removeLayer(mapview.interaction.Layer);

    mapview.interaction.callback?.(feature);
  }
}