修改qgis2web中的一个测量工具-openlayers export-增加面积功能

Modify a measure tool in qgis2web - openlayers export - add area function

我在同一个网站上找到了这篇文章 我也用qgis2web,导出openlayers。我想用他的代码在不删除现有的“测量长度”功能的情况下添加“测量面积”功能。 谢谢

qgis2web.js(只有面积测量)

    var measuring = false;
var measureControl = (function (Control) {
    measureControl = function(opt_options) {

      var options = opt_options || {};

      var button = document.createElement('button');
      button.className += ' fas fa-ruler ';

      var this_ = this;
      var handleMeasure = function(e) {
        if (!measuring) {
            this_.getMap().addInteraction(draw);
            createHelpTooltip();
            createMeasureTooltip();
            measuring = true;
        } else {
            this_.getMap().removeInteraction(draw);
            measuring = false;
            this_.getMap().removeOverlay(helpTooltip);
            this_.getMap().removeOverlay(measureTooltip);
        }
      };

      button.addEventListener('click', handleMeasure, false);
      button.addEventListener('touchstart', handleMeasure, false);

      var element = document.createElement('div');
      element.className = 'measure-control ol-unselectable ol-control';
      element.appendChild(button);

      ol.control.Control.call(this, {
        element: element,
        target: options.target
      });

    };
    if (Control) measureControl.__proto__ = Control;
    measureControl.prototype = Object.create(Control && Control.prototype);
    measureControl.prototype.constructor = measureControl;
    return measureControl;
}(ol.control.Control));
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var closer = document.getElementById('popup-closer');
var sketch;

closer.onclick = function() {
    container.style.display = 'none';
    closer.blur();
    return false;
};
var overlayPopup = new ol.Overlay({
    element: container
});

var expandedAttribution = new ol.control.Attribution({
    collapsible: false
});

var map = new ol.Map({
    controls: ol.control.defaults({attribution:false}).extend([
        expandedAttribution,new ol.control.ScaleLine({}),new measureControl()
    ]),
    target: document.getElementById('map'),
    renderer: 'canvas',
    overlays: [overlayPopup],
    layers: layersList,
    view: new ol.View({
        extent: [2283125.045170, 4809940.953436, 2383123.537919, 4874627.954073], maxZoom: 28, minZoom: 1, projection: new ol.proj.Projection({
            code: 'EPSG:3004',
            extent: [-20037508.342789, -20037508.342789, 20037508.342789, 20037508.342789],
            units: 'm'})
    })
});

var layerSwitcher = new ol.control.LayerSwitcher({tipLabel: "Layers"});
map.addControl(layerSwitcher);

map.getView().fit([2283125.045170, 4809940.953436, 2383123.537919, 4874627.954073], map.getSize());

var NO_POPUP = 0
var ALL_FIELDS = 1

/**
 * Returns either NO_POPUP, ALL_FIELDS or the name of a single field to use for
 * a given layer
 * @param layerList {Array} List of ol.Layer instances
 * @param layer {ol.Layer} Layer to find field info about
 */
function getPopupFields(layerList, layer) {
    // Determine the index that the layer will have in the popupLayers Array,
    // if the layersList contains more items than popupLayers then we need to
    // adjust the index to take into account the base maps group
    var idx = layersList.indexOf(layer) - (layersList.length - popupLayers.length);
    return popupLayers[idx];
}


var collection = new ol.Collection();
var featureOverlay = new ol.layer.Vector({
    map: map,
    source: new ol.source.Vector({
        features: collection,
        useSpatialIndex: false // optional, might improve performance
    }),
    style: [new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: '#f00',
            width: 1
        }),
        fill: new ol.style.Fill({
            color: 'rgba(255,0,0,0.1)'
        }),
    })],
    updateWhileAnimating: true, // optional, for instant visual feedback
    updateWhileInteracting: true // optional, for instant visual feedback
});

var doHighlight = false;
var doHover = false;

var highlight;
var autolinker = new Autolinker({truncate: {length: 30, location: 'smart'}});
var onPointerMove = function(evt) {
    if (!doHover && !doHighlight) {
        return;
    }
    var pixel = map.getEventPixel(evt.originalEvent);
    var coord = evt.coordinate;
    var popupField;
    var currentFeature;
    var currentLayer;
    var currentFeatureKeys;
    var clusteredFeatures;
    var popupText = '<ul>';
    map.forEachFeatureAtPixel(pixel, function(feature, layer) {
        // We only care about features from layers in the layersList, ignore
        // any other layers which the map might contain such as the vector
        // layer used by the measure tool
        if (layersList.indexOf(layer) === -1) {
            return;
        }
        var doPopup = false;
        for (k in layer.get('fieldImages')) {
            if (layer.get('fieldImages')[k] != "Hidden") {
                doPopup = true;
            }
        }
        currentFeature = feature;
        currentLayer = layer;
        clusteredFeatures = feature.get("features");
        var clusterFeature;
        if (typeof clusteredFeatures !== "undefined") {
            if (doPopup) {
                for(var n=0; n<clusteredFeatures.length; n++) {
                    clusterFeature = clusteredFeatures[n];
                    currentFeatureKeys = clusterFeature.getKeys();
                    popupText += '<li><table>'
                    for (var i=0; i<currentFeatureKeys.length; i++) {
                        if (currentFeatureKeys[i] != 'geometry') {
                            popupField = '';
                            if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
                            popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
                            } else {
                                popupField += '<td colspan="2">';
                            }
                            if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "header label") {
                                popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
                            }
                            if (layer.get('fieldImages')[currentFeatureKeys[i]] != "ExternalResource") {
                                popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? autolinker.link(clusterFeature.get(currentFeatureKeys[i]).toLocaleString()) + '</td>' : '');
                            } else {
                                popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + clusterFeature.get(currentFeatureKeys[i]).replace(/[\\/:]/g, '_').trim()  + '" /></td>' : '');
                            }
                            popupText += '<tr>' + popupField + '</tr>';
                        }
                    } 
                    popupText += '</table></li>';    
                }
            }
        } else {
            currentFeatureKeys = currentFeature.getKeys();
            if (doPopup) {
                popupText += '<li><table>';
                for (var i=0; i<currentFeatureKeys.length; i++) {
                    if (currentFeatureKeys[i] != 'geometry') {
                        popupField = '';
                        if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
                            popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
                        } else {
                            popupField += '<td colspan="2">';
                        }
                        if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "header label") {
                            popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
                        }
                        if (layer.get('fieldImages')[currentFeatureKeys[i]] != "ExternalResource") {
                            popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? autolinker.link(currentFeature.get(currentFeatureKeys[i]).toLocaleString()) + '</td>' : '');
                        } else {
                            popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + currentFeature.get(currentFeatureKeys[i]).replace(/[\\/:]/g, '_').trim()  + '" /></td>' : '');
                        }
                        popupText += '<tr>' + popupField + '</tr>';
                    }
                }
                popupText += '</table></li>';
            }
        }
    });
    if (popupText == '<ul>') {
        popupText = '';
    } else {
        popupText += '</ul>';
    }

    if (doHighlight) {
        if (currentFeature !== highlight) {
            if (highlight) {
                featureOverlay.getSource().removeFeature(highlight);
            }
            if (currentFeature) {
                var styleDefinition = currentLayer.getStyle().toString();

                if (currentFeature.getGeometry().getType() == 'Point') {
                    var radius = styleDefinition.split('radius')[1].split(' ')[1];

                    highlightStyle = new ol.style.Style({
                        image: new ol.style.Circle({
                            fill: new ol.style.Fill({
                                color: "#ffff00"
                            }),
                            radius: radius
                        })
                    })
                } else if (currentFeature.getGeometry().getType() == 'LineString') {

                    var featureWidth = styleDefinition.split('width')[1].split(' ')[1].replace('})','');

                    highlightStyle = new ol.style.Style({
                        stroke: new ol.style.Stroke({
                            color: '#ffff00',
                            lineDash: null,
                            width: featureWidth
                        })
                    });

                } else {
                    highlightStyle = new ol.style.Style({
                        fill: new ol.style.Fill({
                            color: '#ffff00'
                        })
                    })
                }
                featureOverlay.getSource().addFeature(currentFeature);
                featureOverlay.setStyle(highlightStyle);
            }
            highlight = currentFeature;
        }
    }

    if (doHover) {
        if (popupText) {
            overlayPopup.setPosition(coord);
            content.innerHTML = popupText;
            container.style.display = 'block';        
        } else {
            container.style.display = 'none';
            closer.blur();
        }
    }
};

var onSingleClick = function(evt) {
    if (doHover) {
        return;
    }
    if (sketch) {
        return;
    }
    var pixel = map.getEventPixel(evt.originalEvent);
    var coord = evt.coordinate;
    var popupField;
    var currentFeature;
    var currentFeatureKeys;
    var clusteredFeatures;
    var popupText = '<ul>';
    map.forEachFeatureAtPixel(pixel, function(feature, layer) {
        if (feature instanceof ol.Feature && (layer.get("interactive") || layer.get("interactive") == undefined)) {
            var doPopup = false;
            for (k in layer.get('fieldImages')) {
                if (layer.get('fieldImages')[k] != "Hidden") {
                    doPopup = true;
                }
            }
            currentFeature = feature;
            clusteredFeatures = feature.get("features");
            var clusterFeature;
            if (typeof clusteredFeatures !== "undefined") {
                if (doPopup) {
                    for(var n=0; n<clusteredFeatures.length; n++) {
                        clusterFeature = clusteredFeatures[n];
                        currentFeatureKeys = clusterFeature.getKeys();
                        popupText += '<li><table>'
                        for (var i=0; i<currentFeatureKeys.length; i++) {
                            if (currentFeatureKeys[i] != 'geometry') {
                                popupField = '';
                                if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
                                popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
                                } else {
                                    popupField += '<td colspan="2">';
                                }
                                if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "header label") {
                                    popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
                                }
                                if (layer.get('fieldImages')[currentFeatureKeys[i]] != "ExternalResource") {
                                    popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? autolinker.link(clusterFeature.get(currentFeatureKeys[i]).toLocaleString()) + '</td>' : '');
                                } else {
                                    popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + clusterFeature.get(currentFeatureKeys[i]).replace(/[\\/:]/g, '_').trim()  + '" /></td>' : '');
                                }
                                popupText += '<tr>' + popupField + '</tr>';
                            }
                        } 
                        popupText += '</table></li>';    
                    }
                }
            } else {
                currentFeatureKeys = currentFeature.getKeys();
                if (doPopup) {
                    popupText += '<li><table>';
                    for (var i=0; i<currentFeatureKeys.length; i++) {
                        if (currentFeatureKeys[i] != 'geometry') {
                            popupField = '';
                            if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
                                popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
                            } else {
                                popupField += '<td colspan="2">';
                            }
                            if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "header label") {
                                popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
                            }
                            if (layer.get('fieldImages')[currentFeatureKeys[i]] != "ExternalResource") {
                                popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? autolinker.link(currentFeature.get(currentFeatureKeys[i]).toLocaleString()) + '</td>' : '');
                            } else {
                                popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + currentFeature.get(currentFeatureKeys[i]).replace(/[\\/:]/g, '_').trim()  + '" /></td>' : '');
                            }
                            popupText += '<tr>' + popupField + '</tr>';
                        }
                    }
                    popupText += '</table>';
                }
            }
        }
    });
    if (popupText == '<ul>') {
        popupText = '';
    } else {
        popupText += '</ul>';
    }
    
    var viewProjection = map.getView().getProjection();
    var viewResolution = map.getView().getResolution();
    for (i = 0; i < wms_layers.length; i++) {
        if (wms_layers[i][1]) {
            var url = wms_layers[i][0].getSource().getGetFeatureInfoUrl(
                evt.coordinate, viewResolution, viewProjection,
                {
                    'INFO_FORMAT': 'text/html',
                });
            if (url) {
                popupText = popupText + '<iframe style="width:100%;height:110px;border:0px;" id="iframe" seamless src="' + url + '"></iframe>';
            }
        }
    }

    if (popupText) {
        overlayPopup.setPosition(coord);
        content.innerHTML = popupText;
        container.style.display = 'block';        
    } else {
        container.style.display = 'none';
        closer.blur();
    }
};


    map.on('pointermove', function(evt) {
        if (evt.dragging) {
            return;
        }
        if (measuring) {
            /** @type {string} */
            var helpMsg = 'Click to start drawing';
            if (sketch) {
                var geom = (sketch.getGeometry());
                if (geom instanceof ol.geom.Polygon) {
                    helpMsg = continuePolygonMsg;
                } else if (geom instanceof ol.geom.LineString) {
                    helpMsg = continueLineMsg;
                }
            }
            helpTooltipElement.innerHTML = helpMsg;
            helpTooltip.setPosition(evt.coordinate);
        }
    });
    

map.on('pointermove', function(evt) {
    onPointerMove(evt);
});
map.on('singleclick', function(evt) {
    onSingleClick(evt);
});

/**
 * Currently drawn feature.
 * @type {ol.Feature}
 */

/**
 * The help tooltip element.
 * @type {Element}
 */
var helpTooltipElement;


/**
 * Overlay to show the help messages.
 * @type {ol.Overlay}
 */
var helpTooltip;


/**
 * The measure tooltip element.
 * @type {Element}
 */
var measureTooltipElement;


/**
 * Overlay to show the measurement.
 * @type {ol.Overlay}
 */
var measureTooltip;


/**
* Message to show when the user is drawing a polygon.
* @type {string}
*/
var continuePolygonMsg = 'Click to continue drawing the polygon';




var source = new ol.source.Vector();

var measureLayer = new ol.layer.Vector({
    source: source,
    style: new ol.style.Style({
        fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 0.2)'
        }),
        stroke: new ol.style.Stroke({
            color: '#ffcc33',
            width: 3
        }),
        image: new ol.style.Circle({
            radius: 7,
            fill: new ol.style.Fill({
                color: '#ffcc33'
            })
        })
    })
});

map.addLayer(measureLayer);

var draw; // global so we can remove it later
function addInteraction() {
  var type = 'Polygon';
  draw = new ol.interaction.Draw({
    source: source,
    type: /** @type {ol.geom.GeometryType} */ (type),
    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.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)'
        }),
        fill: new ol.style.Fill({
          color: 'rgba(255, 255, 255, 0.2)'
        })
      })
    })
  });

  var listener;
  draw.on('drawstart',
      function(evt) {
        // set sketch
        sketch = evt.feature;

        /** @type {ol.Coordinate|undefined} */
        var tooltipCoord = evt.coordinate;

        listener = sketch.getGeometry().on('change', function(evt) {
          var geom = evt.target;
          var output;
            output = formatArea(geom);
                  tooltipCoord = geom.getInteriorPoint().getCoordinates();
          measureTooltipElement.innerHTML = output;
          measureTooltip.setPosition(tooltipCoord);
        });
      }, this);

  draw.on('drawend',
      function(evt) {
        measureTooltipElement.className = 'tooltip tooltip-static';
        measureTooltip.setOffset([0, -7]);
        // unset sketch
        sketch = null;
        // unset tooltip so that a new one can be created
        measureTooltipElement = null;
        createMeasureTooltip();
        ol.Observable.unByKey(listener);
      }, this);
}


/**
 * Creates a new help tooltip
 */
function createHelpTooltip() {
  if (helpTooltipElement) {
    helpTooltipElement.parentNode.removeChild(helpTooltipElement);
  }
  helpTooltipElement = document.createElement('div');
  helpTooltipElement.className = 'tooltip hidden';
  helpTooltip = new ol.Overlay({
    element: helpTooltipElement,
    offset: [15, 0],
    positioning: 'center-left'
  });
  map.addOverlay(helpTooltip);
}


/**
 * Creates a new measure tooltip
 */
function createMeasureTooltip() {
  if (measureTooltipElement) {
    measureTooltipElement.parentNode.removeChild(measureTooltipElement);
  }
  measureTooltipElement = document.createElement('div');
  measureTooltipElement.className = 'tooltip tooltip-measure';
  measureTooltip = new ol.Overlay({
    element: measureTooltipElement,
    offset: [0, -15],
    positioning: 'bottom-center'
  });
  map.addOverlay(measureTooltip);
}

/**
 * Format area output.
 * @param {ol.geom.Polygon} polygon The polygon.
 * @return {string} Formatted area.
 */
      var formatArea = function(polygon) {
        var area = polygon.getArea();       
        var output;
        if (area > 10000) {
          output = (Math.round(area / 1000000 * 100) / 100) +
              ' ' + 'km<sup>2</sup>';
        } else {
          output = (Math.round(area * 100) / 100) +
              ' ' + 'm<sup>2</sup>';
        }
        return output;
      };
      
addInteraction();


var geocoder = new Geocoder('nominatim', {
  provider: 'osm',
  lang: 'en-US',
  placeholder: 'Search for ...',
  limit: 5,
  keepOpen: true
});
map.addControl(geocoder);

document.getElementsByClassName('gcd-gl-btn')[0].className += ' fa fa-search';

var attributionComplete ......ecc...

看看这个 OpenLayers 6 示例 https://openlayers.org/en/latest/examples/measure-style.html 它可能与 OpenLayers 4 一起使用。OL4 不支持规则形状的位移,但您可以使用规则形状图像作为图标,改为设置锚点:

image: new ol.style.Icon({
  src: new ol.style.RegularShape({
    radius: 6,
    points: 3,
    angle: Math.PI,
    fill: new ol.style.Fill({
      color: 'rgba(0, 0, 0, 0.4)'
    })
  }).getImage(1).toDataURL(),
  anchor: [0.5, 1]
})

更新它将在 OL4 中工作 - 克隆文本样式时存在错误,无法访问交互使用的叠加层,但可能有解决方法:

<!DOCTYPE html>
<html>
  <head>
    <title>Measure</title>
    <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
    <script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>
    <style>
      html, body, .map {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
      }
      #map {
        position: relative;
      }
      #form {
        z-index: 1;
        opacity: 1;
        position: absolute;
        bottom: 0;
        left: 0;
        margin: 0;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <form id="form">
      <label for="type">Measurement type &nbsp;</label>
      <select id="type">
        <option value="None">None</option>
        <option value="LineString">Length (LineString)</option>
        <option value="Polygon">Area (Polygon)</option>
      </select>
      <br>
      <label for="segments">Show segment lengths:&nbsp;</label>
      <input type="checkbox" id="segments" checked />
      <br>
      <label for="clear">Clear previous measure:&nbsp;</label>
      <input type="checkbox" id="clear" checked />
    </form>
    <script>

      const typeSelect = document.getElementById('type');
      const showSegments = document.getElementById('segments');
      const clearPrevious = document.getElementById('clear');

      const 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.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)',
          }),
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 0.2)',
          }),
        }),
      });

      const labelStyle = new ol.style.Style({
        text: new ol.style.Text({
          font: '14px Calibri,sans-serif',
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 1)',
          }),
          backgroundFill: new ol.style.Fill({
            color: 'rgba(0, 0, 0, 0.7)',
          }),
          padding: [3, 3, 3, 3],
          textBaseline: 'bottom',
          offsetY: -15,
        }),
        image: new ol.style.Icon({
          src: new ol.style.RegularShape({
            radius: 8,
            points: 3,
            angle: Math.PI,
            fill: new ol.style.Fill({
              color: 'rgba(0, 0, 0, 0.7)',
            }),
          }).getImage(1).toDataURL(),
          anchor: [0.5, 1],
        }),
      });

      const tipStyle = new ol.style.Style({
        text: new ol.style.Text({
          font: '12px Calibri,sans-serif',
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 1)',
          }),
          backgroundFill: new ol.style.Fill({
            color: 'rgba(0, 0, 0, 0.4)',
          }),
          padding: [2, 2, 2, 2],
          textAlign: 'left',
          offsetX: 15,
        }),
      });

      const modifyStyle = new ol.style.Style({
        image: new ol.style.Circle({
          radius: 5,
          stroke: new ol.style.Stroke({
            color: 'rgba(0, 0, 0, 0.7)',
          }),
          fill: new ol.style.Fill({
            color: 'rgba(0, 0, 0, 0.4)',
          }),
        }),
        text: new ol.style.Text({
          text: 'Drag to modify',
          font: '12px Calibri,sans-serif',
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 1)',
          }),
          backgroundFill: new ol.style.Fill({
            color: 'rgba(0, 0, 0, 0.7)',
          }),
          padding: [2, 2, 2, 2],
          textAlign: 'left',
          offsetX: 15,
        }),
      });

      const segmentStyles = [];

      const formatLength = function (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;
      };

      const formatArea = function (polygon) {
        const area = ol.Sphere.getArea(polygon);
        let output;
        if (area > 10000) {
          output = Math.round((area / 1000000) * 100) / 100 + ' km\xB2';
        } else {
          output = Math.round(area * 100) / 100 + ' m\xB2';
        }
        return output;
      };

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

      const source = new ol.source.Vector();

      let modifying = false;

      const modify = new ol.interaction.Modify({
        source: source,
        style: function() {
          modifying = true;
          return modifyStyle;
        }
      });

      let tipPoint;

      function styleFunction(feature, segments, drawType, tip) {
        const styles = [style];
        const geometry = feature.getGeometry();
        const type = geometry.getType();
        let point, label, line;
        if (!drawType || drawType === type) {
          if (type === 'Polygon') {
            point = geometry.getInteriorPoint();
            label = formatArea(geometry);
            line = new ol.geom.LineString(geometry.getCoordinates()[0]);
          } else if (type === 'LineString') {
            point = new ol.geom.Point(geometry.getLastCoordinate());
            label = formatLength(geometry);
            line = geometry;
          }
        }
        if (segments && line) {
          let count = 0;
          line.forEachSegment(function (a, b) {
            const segment = new ol.geom.LineString([a, b]);
            const label = formatLength(segment);
            if (segmentStyles.length - 1 < count) {
              segmentStyles.push(
                new ol.style.Style({
                  text: new ol.style.Text({
                    font: '12px Calibri,sans-serif',
                    fill: new ol.style.Fill({
                      color: 'rgba(255, 255, 255, 1)',
                    }),
                    backgroundFill: new ol.style.Fill({
                      color: 'rgba(0, 0, 0, 0.4)',
                    }),
                    padding: [2, 2, 2, 2],
                    textBaseline: 'bottom',
                    offsetY: -12,
                  }),
                  image: new ol.style.Icon({
                    src: new ol.style.RegularShape({
                      radius: 6,
                      points: 3,
                      angle: Math.PI,
                      fill: new ol.style.Fill({
                        color: 'rgba(0, 0, 0, 0.4)',
                      }),
                    }).getImage(1).toDataURL(),
                    anchor: [0.5, 1],
                  }),
                })
              );
            }
            const segmentPoint = new ol.geom.Point(segment.getCoordinateAt(0.5));
            segmentStyles[count].setGeometry(segmentPoint);
            segmentStyles[count].getText().setText(label);
            styles.push(segmentStyles[count]);
            count++;
          });
        }
        if (label) {
          labelStyle.setGeometry(point);
          labelStyle.getText().setText(label);
          styles.push(labelStyle);
        }
        if (
          tip &&
          type === 'Point' &&
          !modifying
        ) {
          tipPoint = geometry;
          tipStyle.getText().setText(tip);
          styles.push(tipStyle);
        }
        modifying = false;
        return styles;
      }

      const vector = new ol.layer.Vector({
        source: source,
        style: function (feature) {
          return styleFunction(feature, showSegments.checked);
        },
      });

      const map = new ol.Map({
        layers: [raster, vector],
        target: 'map',
        view: new ol.View({
          center: [-11000000, 4600000],
          zoom: 15,
        }),
      });

      map.addInteraction(modify);

      let draw; // global so we can remove it later

      function addInteraction() {
        const drawType = typeSelect.value;
        if (drawType == 'None') {
          modify.setActive(false);
          return;
        }
        const activeTip =
          'Click to continue drawing the ' +
          (drawType === 'Polygon' ? 'polygon' : 'line');
        const idleTip = 'Click to start measuring';
        let tip = idleTip;
        draw = new ol.interaction.Draw({
          source: source,
          type: drawType,
          style: function (feature) {
            return styleFunction(feature, showSegments.checked, drawType, tip);
          },
        });
        draw.on('drawstart', function () {
          if (clearPrevious.checked) {
            source.clear();
          }
          modify.setActive(false);
          tip = activeTip;
        });
        draw.on('drawend', function () {
          modifyStyle.setGeometry(tipPoint);
          modify.setActive(true);
          map.once('pointermove', function () {
            modifyStyle.setGeometry();
          });
          tip = idleTip;
        });
        modify.setActive(true);
        map.addInteraction(draw);
      }

      typeSelect.onchange = function () {
        map.removeInteraction(draw);
        addInteraction();
      };

      addInteraction();

      showSegments.onchange = function () {
        vector.changed();
      };

    </script>
  </body>
</html>

要用作控件,您需要从控件的点击处理函数调用 addInteraction 函数(我用隐藏和显示选项表单替换了 None 选项)。

<!DOCTYPE html>
<html>
  <head>
    <title>Measure</title>
    <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
    <script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>
    <style>
      html, body, .map {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
      }
      #map {
        position: relative;
      }
      #form {
        z-index: 1;
        opacity: 1;
        position: absolute;
        bottom: 0;
        left: 0;
        margin: 0;
      }
      .measure-control {
        top: 65px;
        left: .5em;
      }
      .ol-touch .measure-control {
        top: 80px;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <form id="form" style="display: none">
      <label for="type">Measurement type &nbsp;</label>
      <select id="type">
        <option value="LineString">Length (LineString)</option>
        <option value="Polygon">Area (Polygon)</option>
      </select>
      <br>
      <label for="segments">Show segment lengths:&nbsp;</label>
      <input type="checkbox" id="segments" checked />
      <br>
      <label for="clear">Clear previous measure:&nbsp;</label>
      <input type="checkbox" id="clear" checked />
    </form>
    <script>

      const typeSelectForm = document.getElementById('form');
      const typeSelect = document.getElementById('type');
      const showSegments = document.getElementById('segments');
      const clearPrevious = document.getElementById('clear');

      const 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.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)',
          }),
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 0.2)',
          }),
        }),
      });

      const labelStyle = new ol.style.Style({
        text: new ol.style.Text({
          font: '14px Calibri,sans-serif',
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 1)',
          }),
          backgroundFill: new ol.style.Fill({
            color: 'rgba(0, 0, 0, 0.7)',
          }),
          padding: [3, 3, 3, 3],
          textBaseline: 'bottom',
          offsetY: -15,
        }),
        image: new ol.style.Icon({
          src: new ol.style.RegularShape({
            radius: 8,
            points: 3,
            angle: Math.PI,
            fill: new ol.style.Fill({
              color: 'rgba(0, 0, 0, 0.7)',
            }),
          }).getImage(1).toDataURL(),
          anchor: [0.5, 1],
        }),
      });

      const tipStyle = new ol.style.Style({
        text: new ol.style.Text({
          font: '12px Calibri,sans-serif',
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 1)',
          }),
          backgroundFill: new ol.style.Fill({
            color: 'rgba(0, 0, 0, 0.4)',
          }),
          padding: [2, 2, 2, 2],
          textAlign: 'left',
          offsetX: 15,
        }),
      });

      const modifyStyle = new ol.style.Style({
        image: new ol.style.Circle({
          radius: 5,
          stroke: new ol.style.Stroke({
            color: 'rgba(0, 0, 0, 0.7)',
          }),
          fill: new ol.style.Fill({
            color: 'rgba(0, 0, 0, 0.4)',
          }),
        }),
        text: new ol.style.Text({
          text: 'Drag to modify',
          font: '12px Calibri,sans-serif',
          fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 1)',
          }),
          backgroundFill: new ol.style.Fill({
            color: 'rgba(0, 0, 0, 0.7)',
          }),
          padding: [2, 2, 2, 2],
          textAlign: 'left',
          offsetX: 15,
        }),
      });

      const segmentStyles = [];

      const formatLength = function (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;
      };

      const formatArea = function (polygon) {
        const area = ol.Sphere.getArea(polygon);
        let output;
        if (area > 10000) {
          output = Math.round((area / 1000000) * 100) / 100 + ' km\xB2';
        } else {
          output = Math.round(area * 100) / 100 + ' m\xB2';
        }
        return output;
      };

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

      const source = new ol.source.Vector();

      let modifying = false;

      const modify = new ol.interaction.Modify({
        source: source,
        style: function() {
          modifying = true;
          return modifyStyle;
        }
      });

      let tipPoint;

      function styleFunction(feature, segments, drawType, tip) {
        const styles = [style];
        const geometry = feature.getGeometry();
        const type = geometry.getType();
        let point, label, line;
        if (!drawType || drawType === type) {
          if (type === 'Polygon') {
            point = geometry.getInteriorPoint();
            label = formatArea(geometry);
            line = new ol.geom.LineString(geometry.getCoordinates()[0]);
          } else if (type === 'LineString') {
            point = new ol.geom.Point(geometry.getLastCoordinate());
            label = formatLength(geometry);
            line = geometry;
          }
        }
        if (segments && line) {
          let count = 0;
          line.forEachSegment(function (a, b) {
            const segment = new ol.geom.LineString([a, b]);
            const label = formatLength(segment);
            if (segmentStyles.length - 1 < count) {
              segmentStyles.push(
                new ol.style.Style({
                  text: new ol.style.Text({
                    font: '12px Calibri,sans-serif',
                    fill: new ol.style.Fill({
                      color: 'rgba(255, 255, 255, 1)',
                    }),
                    backgroundFill: new ol.style.Fill({
                      color: 'rgba(0, 0, 0, 0.4)',
                    }),
                    padding: [2, 2, 2, 2],
                    textBaseline: 'bottom',
                    offsetY: -12,
                  }),
                  image: new ol.style.Icon({
                    src: new ol.style.RegularShape({
                      radius: 6,
                      points: 3,
                      angle: Math.PI,
                      fill: new ol.style.Fill({
                        color: 'rgba(0, 0, 0, 0.4)',
                      }),
                    }).getImage(1).toDataURL(),
                    anchor: [0.5, 1],
                  }),
                })
              );
            }
            const segmentPoint = new ol.geom.Point(segment.getCoordinateAt(0.5));
            segmentStyles[count].setGeometry(segmentPoint);
            segmentStyles[count].getText().setText(label);
            styles.push(segmentStyles[count]);
            count++;
          });
        }
        if (label) {
          labelStyle.setGeometry(point);
          labelStyle.getText().setText(label);
          styles.push(labelStyle);
        }
        if (
          tip &&
          type === 'Point' &&
          !modifying
        ) {
          tipPoint = geometry;
          tipStyle.getText().setText(tip);
          styles.push(tipStyle);
        }
        modifying = false;
        return styles;
      }

      const vector = new ol.layer.Vector({
        source: source,
        style: function (feature) {
          return styleFunction(feature, showSegments.checked);
        },
      });

      const map = new ol.Map({
        layers: [raster, vector],
        target: 'map',
        view: new ol.View({
          center: [-11000000, 4600000],
          zoom: 15,
        }),
      });

      map.addInteraction(modify);

      let draw; // global so we can remove it later

      function addInteraction() {
        map.removeInteraction(draw);
        const drawType = typeSelect.value;
        if (typeSelectForm.style.display == 'none') {
          modify.setActive(false);
          return;
        }
        const activeTip =
          'Click to continue drawing the ' +
          (drawType === 'Polygon' ? 'polygon' : 'line');
        const idleTip = 'Click to start measuring';
        let tip = idleTip;
        draw = new ol.interaction.Draw({
          source: source,
          type: drawType,
          style: function (feature) {
            return styleFunction(feature, showSegments.checked, drawType, tip);
          },
        });
        draw.on('drawstart', function () {
          if (clearPrevious.checked) {
            source.clear();
          }
          modify.setActive(false);
          tip = activeTip;
        });
        draw.on('drawend', function () {
          modifyStyle.setGeometry(tipPoint);
          modify.setActive(true);
          map.once('pointermove', function () {
            modifyStyle.setGeometry();
          });
          tip = idleTip;
        });
        modify.setActive(true);
        map.addInteraction(draw);
      }

      typeSelect.onchange = function () {
        addInteraction();
      };

      addInteraction();

      showSegments.onchange = function () {
        vector.changed();
      };


var measuring = false;
var measureControl = (function (Control) {
    measureControl = function(opt_options) {

      var options = opt_options || {};

      var button = document.createElement('button');
      button.className += ' fas fa-ruler ';

      var this_ = this;
      var handleMeasure = function(e) {
        if (!measuring) {
            typeSelectForm.style.display = '';
            addInteraction();
            measuring = true;
        } else {
            typeSelectForm.style.display = 'none';
            addInteraction();
            measuring = false;
        }
      };

      button.addEventListener('click', handleMeasure, false);
      button.addEventListener('touchstart', handleMeasure, false);

      var element = document.createElement('div');
      element.className = 'measure-control ol-unselectable ol-control';
      element.appendChild(button);

      ol.control.Control.call(this, {
        element: element,
        target: options.target
      });

    };
    if (Control) measureControl.__proto__ = Control;
    measureControl.prototype = Object.create(Control && Control.prototype);
    measureControl.prototype.constructor = measureControl;
    return measureControl;
}(ol.control.Control));


      map.addControl(new measureControl());

    </script>
  </body>
</html>

似乎 qgis2web 代码使用变量 sketch 来确定它自己的度量是否处于活动状态(如果是,onSingleClick 函数什么都不做),所以你可以不使用新变量使用那个

sketch = false;
var measureControl = (function (Control) {
    measureControl = function(opt_options) {

      var options = opt_options || {};

      var button = document.createElement('button');
      button.className += ' fas fa-ruler ';

      var this_ = this;
      var handleMeasure = function(e) {
        if (!sketch) {
            typeSelectForm.style.display = '';
            addInteraction();
            sketch = true;
        } else {
            typeSelectForm.style.display = 'none';
            addInteraction();
            sketch = false;
        }
      };

感谢@Mike 的回答,但是我无法实现您随 qgis2web 代码一起提供的代码。然而,由于我的编程经验很少(我花了 3 个月),尽管遇到了很多困难,我还是设法实现了目标。这是结果:

  1. 单击该按钮会打开一个表格,其中包含对几何形状、长度或面积的请求
  2. 图中是一条虚线,除了总的测量外还有分段的测量
  3. 双击关闭几何图形,总计气球为黄色,可以进行进一步测量
  4. 区域呈现气球,总数居中,保持分段的度量
  5. 当您将几何类型从长度更改为面积时,屏幕上的测量值将被重置。要完全停用测量,请再次单击测量按钮,屏幕上的所有元素都将被删除。

我描述一下qgis2web修改后的代码: index.html

<style>
...etc... 

.ol-touch .measure-control {
  top: 80px;
}   
#form_measure {
  background-color: rgba(255, 255, 255, 0.75);
  border: 3px solid #f8f8f8 !important;
  z-index: 1;
  opacity: 1;
  position: absolute;
  top: 65px;
  left: 2.7em;
  margin: 0;
  width: 147px;
  height: 19px;
      }
</style>

<body>
        <div id="map">
            <div id="popup" class="ol-popup">
                <a href="#" id="popup-closer" class="ol-popup-closer"></a>
                <div id="popup-content"></div>
            </div>
        </div>
        <form id="form_measure" style="display: none">
            <label>&nbsp;Misura:&nbsp;</label>
                <select id="type">
                    <option value="length">Lunghezza</option>   
                    <option value="area">Area</option>
                </select>
        </form>
...etc...
</body>

qgis2web.js

...etc...

var handleMeasure = function(e) {
        if (!measuring) {
            typeSelectForm.style.display = '';
            this_.getMap().addInteraction(draw);
            createHelpTooltip();
            createMeasureTooltip();
            measuring = true;
        } else {
            typeSelectForm.style.display = 'none';
            this_.getMap().removeInteraction(draw);
            measuring = false;
            this_.getMap().removeOverlay(helpTooltip);
            this_.getMap().removeOverlay(measureTooltip);

            var staticTooltip = document.getElementsByClassName('tooltip-static');
        while(staticTooltip.length > 0){
            staticTooltip[0].parentNode.removeChild(staticTooltip[0]);
        }
        measureLayer.getSource().clear();
        }
      };

...etc...

/**
 * Message to show when the user is drawing a polygon.
 * @type {string}
 */
var continuePolygonMsg = '1click continua, 2click chiudi';

    var typeSelect = document.getElementById('type');
    var typeSelectForm = document.getElementById('form_measure');

    /**
     * Let user change the geometry type.
     * @param {Event} e Change event.
     */
    typeSelect.onchange = function(e) {
        
      typeSelectForm.style.display = 'none';
     map.removeInteraction(draw);
     measuring = false;
     map.removeOverlay(helpTooltip);
     map.removeOverlay(measureTooltip);
     var staticTooltip = document.getElementsByClassName('tooltip-static');
        while(staticTooltip.length > 0){
            staticTooltip[0].parentNode.removeChild(staticTooltip[0]);
        }
        measureLayer.getSource().clear();
    
     addInteraction();
  
     typeSelectForm.style.display = '';
     map.addInteraction(draw);
     createHelpTooltip();
     createMeasureTooltip();
     measuring = true;
    };

    var style = new ol.style.Style({
    fill: new ol.style.Fill({
    color: 'rgba(255, 255, 255, 0.2)'
    }),
    stroke: new ol.style.Stroke({
    color: 'rgba(255, 204, 51)',
    lineDash: [10, 10],
    width: 3
    }),
    image: new ol.style.Circle({
    radius: 5,
    stroke: new ol.style.Stroke({
    color: 'rgba(0, 0, 0, 0.7)'
    }),
    fill: new ol.style.Fill({
   color: 'rgba(255, 255, 255, 0.2)'
    })
    })
    });

    var labelStyle = new ol.style.Style({
    text: new ol.style.Text({
    font: '14px Calibri,sans-serif',
    fill: new ol.style.Fill({
    color: 'rgba(0, 0, 0, 1)'
     }),
    stroke: new ol.style.Stroke({
    color: 'rgba(255, 255, 255, 1)',
    width: 3
     })
    })
     });

    var labelStyleCache = [];

          
    var styleFunction = function (feature, type) {
    var styles = [style];
    var geometry = feature.getGeometry();
    var type = geometry.getType();
    var lineString;
    if (!type || type === type) {
    if (type === 'Polygon') {
    lineString = new ol.geom.LineString(geometry.getCoordinates()[0]);
    } else if (type === 'LineString') {
    lineString = geometry;
    }
    }
    if (lineString) {
     var count = 0;
     lineString.forEachSegment(function(a, b) {
    var segment = new ol.geom.LineString([a, b]);
    var label = formatLength(segment);
    if (labelStyleCache.length - 1 < count) {
                          labelStyleCache.push(labelStyle.clone());
                        }
                        labelStyleCache[count].setGeometry(segment);
                        labelStyleCache[count].getText().setText(label);
                        styles.push(labelStyleCache[count]);
                        count++;
                      });
                    }
                    return styles;
                    
                  };
                  
var source = new ol.source.Vector();

var measureLayer = new ol.layer.Vector({
    source: source,
    style: function(feature) {
          return styleFunction(feature);
        }
});

map.addLayer(measureLayer);

var draw; // global so we can remove it later
function addInteraction() {
    

  var type = (typeSelect.value == 'area' ? 'Polygon' : 'LineString');       
  draw = new ol.interaction.Draw({
    source: source,
    type: /** @type {ol.geom.GeometryType} */ (type),
    style: function (feature) {
            return styleFunction(feature, type)
          }
  });

  var listener;
  draw.on('drawstart',
      function(evt) {
        // set sketch
        sketch = evt.feature;

        /** @type {ol.Coordinate|undefined} */
        var tooltipCoord = evt.coordinate;

        listener = sketch.getGeometry().on('change', function(evt) {
          var geom = evt.target;
          var output;
            if (geom instanceof ol.geom.Polygon) {
            output = formatArea(/** @type {ol.geom.Polygon} */ (geom));
            tooltipCoord = geom.getInteriorPoint().getCoordinates();
          } else if (geom instanceof ol.geom.LineString) {
            output = formatLength( /** @type {ol.geom.LineString} */ (geom));
            tooltipCoord = geom.getLastCoordinate();
          }
          measureTooltipElement.innerHTML = output;
          measureTooltip.setPosition(tooltipCoord);
        });
      }, this);

  draw.on('drawend',
      function(evt) {
        measureTooltipElement.className = 'tooltip tooltip-static';
        measureTooltip.setOffset([0, -7]);
        // unset sketch
        sketch = null;
        // unset tooltip so that a new one can be created
        measureTooltipElement = null;
        createMeasureTooltip();
        ol.Observable.unByKey(listener);
      }, this);
}

...etc...

/**
 * Format area output.
 * @param {ol.geom.Polygon} polygon The polygon.
 * @return {string} Formatted area.
 */
      var formatArea = function(polygon) {
        var area = polygon.getArea();       
        var output;
        if (area > 10000) {
          output = (Math.round(area / 1000000 * 100) / 100) +
              ' ' + 'km<sup>2</sup>';
        } else {
          output = (Math.round(area * 100) / 100) +
              ' ' + 'm<sup>2</sup>';
        }
        return output;
      };

addInteraction();