测量工具
本章将介绍如何在 OpenLayers 中实现测量工具功能,包括距离测量和面积测量。
示例
代码实现
vue
<template>
<div class="h-screen">
<div id="map" class="w-full h-full"></div>
</div>
</template>
<script setup>
import { onMounted } from "vue";
import "ol/ol.css";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import XYZ from "ol/source/XYZ";
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import { Draw } from "ol/interaction";
import { fromLonLat } from "ol/proj";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import { Control } from "ol/control";
import { getLength, getArea } from "ol/sphere";
import Overlay from "ol/Overlay";
onMounted(() => {
// 创建地图
const map = new Map({
target: "map",
view: new View({
center: fromLonLat([117.33083, 39.094899]), // 天津
zoom: 12,
}),
});
// 添加天地图图层
const tdtLayer = new TileLayer({
source: new XYZ({
url: "http://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=d32c6748c80f81a44acd8633cea41dfd",
}),
});
map.addLayer(tdtLayer);
// 创建绘图图层
const source = new VectorSource();
const vector = new VectorLayer({
source: source,
style: new Style({
fill: new Fill({
color: "rgba(24, 144, 255, 0.2)",
}),
stroke: new Stroke({
color: "#1890ff",
width: 2,
}),
image: new CircleStyle({
radius: 7,
fill: new Fill({
color: "#1890ff",
}),
}),
}),
});
map.addLayer(vector);
// 创建测量控件
const measureControls = document.createElement("div");
measureControls.className = "ol-control measure-controls";
const lengthButton = document.createElement("button");
lengthButton.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 12h20M6 8v8M12 6v12M18 8v8"/></svg>`;
lengthButton.title = "测距离";
lengthButton.addEventListener("click", function () {
areaButton.classList.remove("active");
lengthButton.classList.add("active");
addInteraction("LineString");
});
const areaButton = document.createElement("button");
areaButton.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 3h18v18H3z"/><path d="M3 9h18M3 15h18M9 3v18M15 3v18"/></svg>`;
areaButton.title = "测面积";
areaButton.addEventListener("click", function () {
lengthButton.classList.remove("active");
areaButton.classList.add("active");
addInteraction("Polygon");
});
measureControls.appendChild(lengthButton);
measureControls.appendChild(areaButton);
map.addControl(
new Control({
element: measureControls,
})
);
let draw;
let sketch;
let helpTooltipElement;
let helpTooltip;
let measureTooltipElement;
let measureTooltip;
const formatLength = function (line) {
const length = 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 = getArea(polygon);
let output;
if (area > 10000) {
output = Math.round((area / 1000000) * 100) / 100 + " km²";
} else {
output = Math.round(area * 100) / 100 + " m²";
}
return output;
};
function addInteraction(type) {
if (draw) {
map.removeInteraction(draw);
}
draw = new Draw({
source: source,
type: type,
style: new Style({
fill: new Fill({
color: "rgba(24, 144, 255, 0.2)",
}),
stroke: new Stroke({
color: "#1890ff",
lineDash: [6, 6],
width: 2,
}),
image: new CircleStyle({
radius: 5,
stroke: new Stroke({
color: "#1890ff",
}),
fill: new Fill({
color: "#fff",
}),
}),
}),
});
map.addInteraction(draw);
createMeasureTooltip();
createHelpTooltip();
draw.on("drawstart", function (evt) {
sketch = evt.feature;
let tooltipCoord = evt.coordinate;
sketch.getGeometry().on("change", function (evt) {
const geom = evt.target;
let output;
if (geom instanceof ol.geom.Polygon) {
output = formatArea(geom);
tooltipCoord = geom.getInteriorPoint().getCoordinates();
} else if (geom instanceof ol.geom.LineString) {
output = formatLength(geom);
tooltipCoord = geom.getLastCoordinate();
}
measureTooltipElement.innerHTML = output;
measureTooltip.setPosition(tooltipCoord);
});
});
draw.on("drawend", function () {
measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
measureTooltip.setOffset([0, -7]);
sketch = null;
measureTooltipElement = null;
createMeasureTooltip();
});
}
function createHelpTooltip() {
if (helpTooltipElement) {
helpTooltipElement.parentNode.removeChild(helpTooltipElement);
}
helpTooltipElement = document.createElement("div");
helpTooltipElement.className = "ol-tooltip";
helpTooltip = new Overlay({
element: helpTooltipElement,
offset: [15, 0],
positioning: "center-left",
});
map.addOverlay(helpTooltip);
}
function createMeasureTooltip() {
if (measureTooltipElement) {
measureTooltipElement.parentNode.removeChild(measureTooltipElement);
}
measureTooltipElement = document.createElement("div");
measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";
measureTooltip = new Overlay({
element: measureTooltipElement,
offset: [0, -15],
positioning: "bottom-center",
});
map.addOverlay(measureTooltip);
}
});
</script>
<style scoped>
.h-screen {
height: 24rem;
}
.w-full {
width: 100%;
}
.h-full {
height: 100%;
}
:deep(.ol-tooltip) {
position: relative;
background: rgba(0, 0, 0, 0.75);
border-radius: 6px;
color: #fff;
padding: 6px 12px;
opacity: 0.9;
white-space: nowrap;
font-size: 13px;
font-weight: 500;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(4px);
}
:deep(.measure-controls) {
top: 65px;
left: 0.5em;
background: white;
padding: 8px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
:deep(.measure-controls button) {
background-color: #fff;
border: 1px solid #e8e8e8;
color: #595959;
padding: 8px;
cursor: pointer;
border-radius: 4px;
margin: 3px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
min-width: 36px;
min-height: 36px;
}
:deep(.measure-controls button:hover) {
background-color: #f5f5f5;
border-color: #1890ff;
color: #1890ff;
}
:deep(.measure-controls button svg) {
width: 22px;
height: 22px;
transition: all 0.3s ease;
}
:deep(.measure-controls button.active) {
background-color: #1890ff;
border-color: #1890ff;
color: white;
}
:deep(.measure-controls button.active svg) {
fill: white;
stroke: white;
}
:deep(.ol-tooltip-measure) {
background: #1890ff;
color: white;
border: none;
}
:deep(.ol-tooltip-static) {
background-color: rgba(0, 0, 0, 0.85);
color: white;
border: 1px solid white;
}
</style>