From a229f824fc1fab91f6abdce98792c3b47b4ee168 Mon Sep 17 00:00:00 2001 From: Christoph Friedrich Date: Tue, 11 Jul 2023 17:34:21 +0200 Subject: [PATCH] Add distance measurement tool Based on https://openlayers.org/en/latest/examples/measure.html but significantly simplified --- main.js | 9 +++- measuretool.js | 129 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 measuretool.js diff --git a/main.js b/main.js index bb82ceb..033d9ea 100644 --- a/main.js +++ b/main.js @@ -13,6 +13,8 @@ import LayerSwitcher from 'ol-layerswitcher'; import colormap from 'colormap'; +import {addMeasureTool} from './measuretool.js'; + proj4.defs('EPSG:32633', '+proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs'); register(proj4); @@ -67,8 +69,9 @@ for(var i = 160; i<=180; i++) { export function handleSlider(e) { let index = e.srcElement.value-160; - map.getLayers().item(index).setVisible(true); // set new one to visible - map.getLayers().forEach((e,i,a) => i==index+1 || i==0 ? e.setVisible(true) : e.setVisible(false)); // set all others to unvisible without hiding the one we just set to visible or the basemap + // set to visible: the current layer (given by index), the basemap (always the first layer), and the drawn measurements (always the last layer) + // and everything else to unvisible + map.getLayers().forEach((e,i,a) => i==index+1 || i==0 || i==a.length-1 ? e.setVisible(true) : e.setVisible(false)); document.getElementById('DOY').innerHTML = 'Day of year: ' + (index+160); } window.handleSlider = handleSlider; @@ -89,6 +92,8 @@ const map = new Map({ }), }); +addMeasureTool(map); + const layerSwitcher = new LayerSwitcher({ reverse: false, groupSelectStyle: 'group' diff --git a/measuretool.js b/measuretool.js new file mode 100644 index 0000000..98ba84b --- /dev/null +++ b/measuretool.js @@ -0,0 +1,129 @@ +import Draw from 'ol/interaction/Draw.js'; +import Overlay from 'ol/Overlay.js'; +import {Vector as VectorSource} from 'ol/source.js'; +import {Vector as VectorLayer} from 'ol/layer.js'; +import {getLength} from 'ol/sphere.js'; +import {unByKey} from 'ol/Observable.js'; +import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style.js'; + +export function addMeasureTool(map) { + + // Create and add layer that stores drawn lines + const drawSource = new VectorSource(); // also needed in Draw interaction + const drawLayer = new VectorLayer({ + source: drawSource, + style: { + 'fill-color': 'rgba(255, 255, 255, 0.2)', // subtle gray + 'stroke-color': '#ffcc33', // orange + 'stroke-width': 2, + 'circle-radius': 7, + 'circle-fill-color': '#ffcc33', // same orange + }, + }); + map.addLayer(drawLayer); + + // Create and add Draw interaction + let draw = new Draw({ + source: drawSource, + type: 'LineString', + style: new Style({ + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.2)', + }), + stroke: new Stroke({ // dashed line while drawing + color: 'rgba(0, 0, 0, 0.5)', + lineDash: [10, 10], + width: 2, + }), + image: new CircleStyle({ + radius: 5, + stroke: new Stroke({ + color: 'rgba(0, 0, 0, 0.7)', + }), + fill: new Fill({ + color: 'rgba(255, 255, 255, 0.2)', + }), + }), + }), + }); + map.addInteraction(draw); + + // These are used later in the event handlers, so they must be declared here + let helpTooltipElement; + let helpTooltip; + let measureTooltipElement; + let measureTooltip; + + // Create and add tooltip that says "Click to measure"/"Double-click to finish" + function createHelpTooltip() { + if (helpTooltipElement) { + helpTooltipElement.parentNode.removeChild(helpTooltipElement); + } + helpTooltipElement = document.createElement('div'); + helpTooltipElement.className = 'ol-tooltip hidden'; + helpTooltip = new Overlay({ + element: helpTooltipElement, + offset: [15, 0], + positioning: 'center-left', + }); + map.addOverlay(helpTooltip); + } + createHelpTooltip(); + + // Create and add tooltip that says "x meters" (code is similar to the one above but still significantly different) + function createMeasureTooltip() { + if (measureTooltipElement) { + measureTooltipElement.parentNode.removeChild(measureTooltipElement); + } + measureTooltipElement = document.createElement('div'); + measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure'; + measureTooltip = new Overlay({ + element: measureTooltipElement, + offset: [0, -15], + positioning: 'bottom-center', + stopEvent: false, + insertFirst: false, + }); + map.addOverlay(measureTooltip); + } + createMeasureTooltip(); + + // Now register the event handlers + + let sketch; + let listener; + + map.on('pointermove', function (evt) { + if (evt.dragging) { + return; + } + let helpMsg = sketch ? 'Double-click to finish' : 'Click to measure'; + helpTooltipElement.innerHTML = helpMsg; + helpTooltip.setPosition(evt.coordinate); + helpTooltipElement.classList.remove('hidden'); + }); + + map.getViewport().addEventListener('mouseout', function () { + helpTooltipElement.classList.add('hidden'); + }); + + draw.on('drawstart', function (evt) { + sketch = evt.feature; // set sketch + listener = sketch.getGeometry().on('change', function (evt) { // register another event handler within this event handler + const geom = evt.target; + measureTooltipElement.innerHTML = Math.round(getLength(geom)) + ' ' + 'm' + measureTooltip.setPosition(geom.getLastCoordinate()); + }); + }); + + draw.on('drawend', function () { + measureTooltipElement.className = 'ol-tooltip ol-tooltip-static'; + measureTooltip.setOffset([0, -7]); + sketch = null; // unset sketch + measureTooltipElement = null; // unset tooltip so that a new one can be created + createMeasureTooltip(); // create that new one + unByKey(listener); // unregister the event handler that was created within the other event handler + helpTooltipElement.innerHTML = 'Click to measure'; // setup for next measurement + }); + +}