javascriptmoduleopenlayers

OpenLayers 4 ruler


I'm refactoring the whole code of the app with modules and I'm having some problem with the Ruler function of OpenLayers 4 now.

In the previous code was working OK, but now when I double click on the map to stop the current measure and start a new one, the only thing left in the screen is the popup, the line (the line of what I was measuring before) is removed.

Here my code:

const initVector = (mapView) => {
  /**
   * vector layers sources
   */
  $('.form-inline').click(function () {
    addRuler()
    alert("initialize")
  })

  let Rsource = new ol.source.Vector()

  let rulerLayer = new ol.layer.Vector({
    source: Rsource,
    style: new ol.style.Style({
      fill: new ol.style.Fill({
        color: 'rgba(255, 255, 255, 0.2)'
      }),
      stroke: new ol.style.Stroke({
        color: 'rgba(0, 0, 0, 0.7)',
        width: 3
      }),
      image: new ol.style.Circle({
        radius: 7,
        fill: new ol.style.Fill({
          color: 'rgba(0, 0, 0, 0.7)'
        })
      })
    })
  })
  /*
   * ruler tool handler
   */

  /**
   * global vars
   */

  // this style is done by javascript to bypass the override rule that enter in conflict with FRA
  $('.markers button').css({'margin': '5px 7px', 'padding': '0 10px'})
  $('#markloc').css('margin-left', '5px')

  // these var are for the creation of the text and all the related element during the measure
  let sketch
  let helpTooltipElement
  let helpTooltip
  let measureTooltipElement
  let measureTooltip
  let ruler

  /*
   * pointer handler
   */

  const pointerMoveHandler = (evt) => {
    /*
     * if the mouse is dragging the map return
     */

    if (evt.dragging) {
      return
    }

    /**
     * default message to display
     */

    let helpMsg = 'Click to start drawing'

    /**
     * check the message if you are measuring
     */

    if (sketch) {
      helpMsg = 'Click to continue drawing the line or double click to stop.'
    }

    /**
     * attach to the tooltip the correct message
     * set the position near the mouse cursor
     * display the tooltip
     */

    helpTooltipElement.innerHTML = helpMsg
    helpTooltip.setPosition(evt.coordinate)
    helpTooltipElement.classList.remove('hidden')
  }

  /**
   * display the actual measured length
   */

  function formatLength (line) {
    const length = ol.Sphere.getLength(line)
    let output

    if (length > 100) {
      output = `${Math.round(length / 1000 * 100) / 100} km`
    }
    else {
      output = `${Math.round(length * 100) / 100} m`
    }
    return output
  }

  /**
   * create a new tooltip
   */

  function createHelpTooltip () {
    helpTooltipElement = document.createElement('div')
    helpTooltipElement.className = 'tooltip hidden'
    helpTooltip = new ol.Overlay({
      element: helpTooltipElement,
      offset: [15, 0],
      positioning: 'center-left'
    })
    mapView.addOverlay(helpTooltip)
  }

  /**
   * Creates a new measure tooltip
   */

  function createMeasureTooltip () {
    measureTooltipElement = document.createElement('div')
    measureTooltipElement.className = 'tooltip tooltip-measure'
    measureTooltip = new ol.Overlay({
      element: measureTooltipElement,
      offset: [0, -15],
      positioning: 'bottom-center'
    })
    mapView.addOverlay(measureTooltip)
  }

  /**
   * add the ruler when you click on the button
   */

  function addRuler () {
    /**
     * add a selected class to the ruler button to make it visible that it's in use
     */

    $('#tlruler').addClass('custbtnsel')

    /**
     * styling ruler
     */

    ruler = new ol.interaction.Draw({
      source: Rsource,
      type: 'LineString',
      style: new ol.style.Style({
        stroke: new ol.style.Stroke({
          color: 'rgba(0, 0, 0, 0.5)',
          lineDash: [10, 10],
          width: 2
        }),
        image: new ol.style.Circle({
          radius: 5,
          stroke: new ol.style.Stroke({
            color: 'rgba(0, 0, 0, 0.7)'
          })
        })
      })
    })

    /**
     * call the pointerMoveHandler to create the element on the screen when the ruler it's in use
     */

    mapView.on('pointermove', pointerMoveHandler)

    /**
     * mouseout event listener to hidden the popup
     */

    mapView.getViewport().addEventListener('mouseout', () => {
      helpTooltipElement.classList.add('hidden')
    })

    /**
     * add the ruler interaction to the map
     */

    mapView.addInteraction(ruler)

    /**
     * create the tooltip
     */

    createMeasureTooltip()
    createHelpTooltip()

    let listener

    /**
     * drawstart event
     */

    ruler.on('drawstart', function (evt) {
      // set sketch
      sketch = evt.feature
      // tooltip coordinate
      let tooltipCoord = evt.coordinate

      /**
       * sketch event listener on change
       * called during a mouse move
       */

      listener = sketch.getGeometry().on('change', function (evt) {
        let geom = evt.target

        /**
         * as we don't use polygon we check justfor line
         * get last position of the cursor and the length
         */

        let output

        // OL 5 CODE
        // if (geom instanceof LineString)

        if (geom instanceof ol.geom.LineString) {
          output = formatLength(geom)
          tooltipCoord = geom.getLastCoordinate()
        }

        /**
         * append to the tooltip the measure
         * set the position of the tooltip to the last cursor coord
         */

        measureTooltipElement.innerHTML = output
        measureTooltip.setPosition(tooltipCoord)
      })
    }, this)

    /**
     * drawend event
     */

    ruler.on('drawend', () => {
      /**
       * create the static tooltip with the last measure
       */
      console.log('drawend')

      measureTooltipElement.className = 'tooltip tooltip-static'
      measureTooltip.setOffset([0, -7])

      /**
       * set sketch and the tooltip element to null
       */

      sketch = null
      measureTooltipElement = null

      /**
       * set sketch and the tooltip element to null
       */

      createMeasureTooltip()
      // OL 5 code
      // unByKey(listener);
      ol.Observable.unByKey(listener)
    }, this)

    /**
     * end addRuler function
     */
  }
}

const mapLayer = new ol.layer.Tile({
  source: new ol.source.OSM()
});

let map = new ol.Map({
  layers: [mapLayer],
  target: 'map',
  view: new ol.View({
    center: [0, 0],
    zoom: 10
  })
})
initVector(map)  

the link to the pen so you can test it: (pen deleted)

and here how should work:

https://openlayers.org/en/latest/examples/measure.html

Notice that I created a function to initialise the ruler to simulate the module, as my map is created inside a module and the ruler in another, in the map module then I import that function and I initialise it with the map variable


Solution

  • Thanks to @Mike that answer me in Gis.stackexchange.

    In initVector after creating rulerLayer you need to add it to the map map.addLayer(rulerLayer);

    A very small error.