修改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 </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: </label>
<input type="checkbox" id="segments" checked />
<br>
<label for="clear">Clear previous measure: </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 </label>
<select id="type">
<option value="LineString">Length (LineString)</option>
<option value="Polygon">Area (Polygon)</option>
</select>
<br>
<label for="segments">Show segment lengths: </label>
<input type="checkbox" id="segments" checked />
<br>
<label for="clear">Clear previous measure: </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 个月),尽管遇到了很多困难,我还是设法实现了目标。这是结果:
- 单击该按钮会打开一个表格,其中包含对几何形状、长度或面积的请求
- 图中是一条虚线,除了总的测量外还有分段的测量
- 双击关闭几何图形,总计气球为黄色,可以进行进一步测量
- 区域呈现气球,总数居中,保持分段的度量
- 当您将几何类型从长度更改为面积时,屏幕上的测量值将被重置。要完全停用测量,请再次单击测量按钮,屏幕上的所有元素都将被删除。
我描述一下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> Misura: </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();
我在同一个网站上找到了这篇文章
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 </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: </label>
<input type="checkbox" id="segments" checked />
<br>
<label for="clear">Clear previous measure: </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 </label>
<select id="type">
<option value="LineString">Length (LineString)</option>
<option value="Polygon">Area (Polygon)</option>
</select>
<br>
<label for="segments">Show segment lengths: </label>
<input type="checkbox" id="segments" checked />
<br>
<label for="clear">Clear previous measure: </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 个月),尽管遇到了很多困难,我还是设法实现了目标。这是结果:
- 单击该按钮会打开一个表格,其中包含对几何形状、长度或面积的请求
- 图中是一条虚线,除了总的测量外还有分段的测量
- 双击关闭几何图形,总计气球为黄色,可以进行进一步测量
- 区域呈现气球,总数居中,保持分段的度量
- 当您将几何类型从长度更改为面积时,屏幕上的测量值将被重置。要完全停用测量,请再次单击测量按钮,屏幕上的所有元素都将被删除。
我描述一下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> Misura: </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();