当前位置: 首页 > news >正文

OpenLayers常用控件 -- 章节七:测量工具控件教程

前言

地图测量功能是GIS应用中最实用的功能之一,它能够帮助用户精确测量地图上的距离和面积。在城市规划、土地管理、工程测绘等领域有着广泛的应用。本文将详细介绍如何使用OpenLayers实现一个功能完整的测量工具,包括距离测量和面积测量,并提供实时的测量结果显示和友好的用户交互体验。

项目结构分析

<template><div id="map"><div class="MapTool"><el-row><el-button type="primary" @click.stop.prevent="measureCtl('Polygon')">面积</el-button><el-button type="success" @click.stop.prevent="measureCtl('LineString')">距离</el-button></el-row></div></div>
</template>

模板结构详解:

  • 地图容器: id="map" 作为地图挂载点
  • 工具栏: .MapTool 包含测量功能按钮
  • 测量按钮: 分别触发面积测量和距离测量功能
  • 事件修饰符: @click.stop.prevent 阻止事件冒泡和默认行为

依赖引入详解

//引入依赖
import {Map, View, Overlay} from 'ol'
import * as ol from 'ol'
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import Style from 'ol/style/Style';
import {Fill, Stroke, Circle} from 'ol/style';
import {OSM} from 'ol/source'
import TileLayer from 'ol/layer/Tile'
import {defaults as defaultControls} from 'ol/control.js';
import Draw from 'ol/interaction/Draw';
import {Polygon,LineString} from 'ol/geom';
import * as sphere from 'ol/sphere';
import {unByKey} from 'ol/Observable';

核心依赖说明:

基础组件
  • Map, View: 地图核心组件
  • Overlay: 覆盖层,用于显示测量结果提示框
  • VectorSource, VectorLayer: 矢量数据源和图层,存储绘制的测量图形
样式组件
  • Style, Fill, Stroke, Circle: 图形样式配置组件
  • 用途: 定制测量图形的显示效果
交互组件
  • Draw: 绘制交互工具,实现用户绘制测量图形
  • Polygon, LineString: 几何图形类型,用于类型判断
测量工具
  • sphere: 球面测量模块,提供精确的地理测量算法
  • unByKey: 事件监听器管理工具

全局变量定义

/*** 当前绘制的要素(Currently drawn feature.)* @type {ol.Feature}*/
var sketch;
/*** 帮助提示框对象(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;
/***  当用户正在绘制多边形时的提示信息文本* @type {string}*/
var continuePolygonMsg = 'Click to continue drawing the polygon';
/*** 当用户正在绘制线时的提示信息文本* @type {string}*/
var continueLineMsg = 'Click to continue drawing the line';
/*** 事件监听*/
var listener;
/*** 绘制交互*/
let draw;

全局变量功能分析:

绘制相关变量
  • sketch: 当前正在绘制的要素对象
  • draw: 绘制交互实例
  • listener: 几何图形变化事件监听器
提示框相关变量
  • helpTooltipElement: 帮助提示框DOM元素
  • helpTooltip: 帮助提示框覆盖层对象
  • measureTooltipElement: 测量结果提示框DOM元素
  • measureTooltip: 测量结果覆盖层对象
提示信息
  • continuePolygonMsg: 绘制多边形时的提示文本
  • continueLineMsg: 绘制线条时的提示文本

数据属性初始化

data() {return {map: null,  // 地图实例}
}

地图初始化

mounted() {//初始化地图this.map = new Map({target: 'map',//指定挂载dom,注意必须是idlayers: [new TileLayer({source: new OSM()//加载OpenStreetMap})],//配置视图view: new View({center: [113.24981689453125, 23.126468438108688], //视图中心位置projection: "EPSG:4326", //指定投影zoom: 12,  //缩放到的级别})});
}

初始化说明:

  • 使用OSM作为底图
  • 设置广州为地图中心
  • 使用WGS84坐标系
  • 不添加默认控件,保持界面简洁

核心功能实现

测量控制方法 (measureCtl)

measureCtl(measureType){if(draw){//清除掉之前绘制的交互this.map.removeInteraction(draw);}//定义一个数据源this.source = new VectorSource();this.vectorLayer = new VectorLayer({source:this.source,style: new Style({fill: new Fill({color: 'rgba(255, 255, 255, 0.2)'}),stroke: new Stroke({color: '#ffcc33',width: 2}),image: new Circle({radius: 7,fill: new Fill({color: '#ffcc33'})})})});this.map.addLayer(this.vectorLayer);//地图容器绑定鼠标移动事件,动态显示帮助提示框内容this.map.on('pointermove', this.pointerMoveHandler);//根据不同类型添加绘制的交互this.addInteraction(measureType)
}

方法详细解析:

1. 清理前一次交互
if(draw){//清除掉之前绘制的交互this.map.removeInteraction(draw);
}

功能说明:

  • 检查是否存在之前的绘制交互
  • 清除旧的交互,避免冲突
  • 确保每次只有一个测量工具激活
2. 创建矢量数据源和图层
//定义一个数据源
this.source = new VectorSource();
this.vectorLayer = new VectorLayer({source:this.source,style: new Style({fill: new Fill({color: 'rgba(255, 255, 255, 0.2)'}),stroke: new Stroke({color: '#ffcc33',width: 2}),image: new Circle({radius: 7,fill: new Fill({color: '#ffcc33'})})})
});

样式配置详解:

  • fill: 填充样式,半透明白色
  • stroke: 边框样式,黄色2像素宽度
  • image: 点样式,黄色圆圈,半径7像素
3. 绑定鼠标移动事件
//地图容器绑定鼠标移动事件,动态显示帮助提示框内容
this.map.on('pointermove', this.pointerMoveHandler);

鼠标移动处理方法 (pointerMoveHandler)

pointerMoveHandler(event) {if (event.dragging) {return;}//当前默认提示信息var helpMsg = 'Click to start drawing';//判断绘制几何类型设置相应的帮助提示信息if (sketch) {var geom = (sketch.getGeometry());if (geom instanceof Polygon) {helpMsg = continuePolygonMsg; //绘制多边形时提示相应内容} else if (geom instanceof LineString) {helpMsg = continueLineMsg; //绘制线时提示相应内容}}helpTooltipElement.innerHTML = helpMsg; //将提示信息设置到对话框中显示helpTooltip.setPosition(event.coordinate);//设置帮助提示框的位置
}

功能分析:

  • 拖拽检测: 拖拽时不显示提示
  • 动态提示: 根据绘制状态显示不同提示信息
  • 位置跟随: 提示框跟随鼠标位置移动

创建帮助提示框 (createHelpTooltip)

createHelpTooltip() {if (helpTooltipElement) {helpTooltipElement.parentNode.removeChild(helpTooltipElement);}helpTooltipElement = document.createElement('div');helpTooltipElement.className = 'tooltip hidden';helpTooltip = new Overlay({element: helpTooltipElement,offset: [15, 0],positioning: 'center-left'});//提示框的覆盖层this.map.addOverlay(helpTooltip);
}

实现步骤:

  1. 清理旧元素: 移除之前的帮助提示框
  2. 创建DOM元素: 创建新的div元素
  3. 设置样式类: 应用tooltip样式
  4. 创建覆盖层: 配置位置偏移和定位方式
  5. 添加到地图: 将覆盖层添加到地图

创建测量提示框 (createMeasureTooltip)

createMeasureTooltip() {//重置提示框if (measureTooltipElement) {measureTooltipElement.parentNode.removeChild(measureTooltipElement);}measureTooltipElement = document.createElement('div');measureTooltipElement.className = 'tooltip tooltip-measure';//提示框的覆盖层measureTooltip = new Overlay({element: measureTooltipElement,offset: [0, -15],positioning: 'bottom-center'});this.map.addOverlay(measureTooltip);
}

配置说明:

  • offset: [0, -15]: 向上偏移15像素
  • positioning: 'bottom-center': 底部居中定位
  • tooltip-measure: 专用样式类

添加绘制交互 (addInteraction)

addInteraction(measureType) {//绘制交互draw = new Draw({source: this.source,//测量绘制层数据源type: measureType,  //几何图形类型style: new Style({//绘制几何图形的样式fill: new Fill({color: 'rgba(255, 255, 255, 0.8)'}),stroke: new Stroke({color: 'rgba(0, 0, 0, 0.5)',lineDash: [10, 10],width: 2}),image: new Circle({radius: 5,stroke: new Stroke({color: 'rgba(0, 0, 0, 0.7)'}),fill: new Fill({color: 'rgba(255, 255, 255, 0.5)'})})})});//将绘制交互事件添加到地图中this.map.addInteraction(draw);//创建测量工具提示框this.createMeasureTooltip();//创建帮助提示框this.createHelpTooltip();// ...事件绑定代码
}

绘制样式特点:

  • lineDash: [10, 10]: 虚线效果,绘制时的临时样式
  • 半透明填充: 便于查看底图内容
  • 对比色边框: 黑色边框便于识别

绘制事件处理

开始绘制事件 (drawstart)
//绑定交互绘制工具开始绘制的事件
draw.on('drawstart', (event) => {sketch = event.feature; //绘制的要素var tooltipCoord = event.coordinate;// 绘制的坐标//绑定change事件,根据绘制几何类型得到测量长度值或面积值listener = sketch.getGeometry().on('change', (event) => {var geom = event.target;//绘制几何要素var output;if (geom instanceof Polygon) {output = _this.formatArea(geom);//面积值//获取一个在几何体中内部的坐标点tooltipCoord = geom.getInteriorPoint().getCoordinates();//坐标} else if (geom instanceof LineString) {output = _this.formatLength(geom);//长度值//测量长度获取到线的最后一个坐标tooltipCoord = geom.getLastCoordinate();//坐标}measureTooltipElement.innerHTML = output;//将测量值设置到测量工具提示框中显示measureTooltip.setPosition(tooltipCoord);//设置测量工具提示框的显示位置});
});

事件处理流程:

  1. 保存要素引用: 将当前绘制要素存储到sketch变量
  2. 绑定几何变化事件: 监听几何图形的实时变化
  3. 实时计算: 根据几何类型调用相应的测量方法
  4. 更新显示: 实时更新测量结果和提示框位置
结束绘制事件 (drawend)
//绑定交互绘制工具结束绘制的事件
draw.on('drawend', (event) => {measureTooltipElement.className = 'tooltip tooltip-static'; //设置测量提示框的样式measureTooltip.setOffset([0, -7]);sketch = null; //置空当前绘制的要素对象measureTooltipElement = null; //置空测量工具提示框对象_this.createMeasureTooltip();//重新创建一个测试工具提示框显示结果unByKey(listener);
});

结束处理步骤:

  1. 切换样式: 将提示框样式改为静态样式
  2. 调整位置: 微调提示框偏移
  3. 清理状态: 重置全局变量
  4. 创建新提示框: 为下次测量准备
  5. 清理监听器: 移除几何变化监听

测量算法实现

距离测量 (formatLength)

formatLength(line) {var length;//使用测地学方法测量var sourceProj = this.map.getView().getProjection(); //地图数据源投影坐标系length = sphere.getLength(line, {"projection": sourceProj, "radius": 6378137});var output;if (length > 100) {output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km'; //换算成KM单位} else {output = (Math.round(length * 100) / 100) + ' ' + 'm'; //m为单位}return output;//返回线的长度
}

测量原理详解:

  • sphere.getLength(): 使用球面几何算法计算真实地理距离
  • projection参数: 指定当前地图的投影坐标系
  • radius: 地球半径,6378137米(WGS84椭球体长半轴)
  • 单位转换: 自动在米和千米之间切换

面积测量 (formatArea)

formatArea(polygon) {var area;//使用测地学方法测量var sourceProj = this.map.getView().getProjection();//地图数据源投影坐标系var geom = (polygon.clone().transform(sourceProj, 'EPSG:4326')); //将多边形要素坐标系投影为EPSG:4326area = Math.abs(sphere.getArea(geom, {"projection": sourceProj, "radius": 6378137})); //获取面积var output;if (area > 10000) {output = (Math.round(area / 1000000 * 100) / 100) + ' ' + 'km<sup>2</sup>'; //换算成KM单位} else {output = (Math.round(area * 100) / 100) + ' ' + 'm<sup>2</sup>';//m为单位}return output; //返回多边形的面积
}

面积计算步骤:

  1. 坐标转换: 将多边形转换为WGS84坐标系
  2. 球面面积: 使用sphere.getArea()计算球面面积
  3. 绝对值: 确保面积为正值
  4. 单位转换: 平方米和平方千米之间自动切换
  5. HTML格式: 使用上标显示平方符号

样式设计详解

工具栏样式

.MapTool {position: absolute;top: .5em;right: .5em;z-index: 9999;
}

提示框基础样式

#map >>> .tooltip {position: relative;background: rgba(0, 0, 0, 0.5);border-radius: 4px;color: white;padding: 4px 8px;opacity: 0.7;white-space: nowrap;
}

样式特点:

  • 半透明背景: 不遮挡地图内容
  • 圆角边框: 现代化视觉效果
  • 不换行: 保持提示信息简洁

测量提示框样式

#map >>> .tooltip-measure {opacity: 1;font-weight: bold;
}

静态提示框样式

#map >>> .tooltip-static {background-color: #ffcc33;color: black;border: 1px solid white;
}

功能说明:

  • 黄色背景: 突出显示最终测量结果
  • 黑色文字: 提高可读性
  • 白色边框: 增强视觉层次

提示框箭头样式

#map >>> .tooltip-measure:before, .tooltip-static:before {border-top: 6px solid rgba(0, 0, 0, 0.5);border-right: 6px solid transparent;border-left: 6px solid transparent;content: "";position: absolute;bottom: -6px;margin-left: -7px;left: 50%;
}#map >>> .tooltip-static:before {border-top-color: #ffcc33;
}

箭头实现原理:

  • CSS三角形: 使用border属性创建三角形箭头
  • 绝对定位: 精确定位箭头位置
  • 颜色匹配: 箭头颜色与提示框背景一致

实际应用扩展

1. 测量结果存储

data() {return {map: null,measureResults: []  // 存储测量结果}
},methods: {saveMeasureResult(type, value, geometry) {const result = {id: Date.now(),type: type,value: value,geometry: geometry.clone(),timestamp: new Date()};this.measureResults.push(result);}
}

2. 清除测量结果

methods: {clearMeasurements() {// 清除图层if (this.vectorLayer) {this.map.removeLayer(this.vectorLayer);}// 清除覆盖层this.map.getOverlays().clear();// 清除交互if (draw) {this.map.removeInteraction(draw);draw = null;}// 重置变量sketch = null;listener = null;measureTooltipElement = null;helpTooltipElement = null;}
}

3. 测量单位切换

data() {return {map: null,unit: 'metric'  // 'metric' 或 'imperial'}
},methods: {formatLength(line) {var length;var sourceProj = this.map.getView().getProjection();length = sphere.getLength(line, {"projection": sourceProj, "radius": 6378137});var output;if (this.unit === 'imperial') {// 英制单位var feet = length * 3.28084;if (feet > 5280) {output = (Math.round(feet / 5280 * 100) / 100) + ' miles';} else {output = (Math.round(feet * 100) / 100) + ' ft';}} else {// 公制单位if (length > 100) {output = (Math.round(length / 1000 * 100) / 100) + ' km';} else {output = (Math.round(length * 100) / 100) + ' m';}}return output;}
}

4. 测量精度控制

methods: {formatLength(line, precision = 2) {var length;var sourceProj = this.map.getView().getProjection();length = sphere.getLength(line, {"projection": sourceProj, "radius": 6378137});var output;if (length > 100) {output = (Math.round(length / 1000 * Math.pow(10, precision)) / Math.pow(10, precision)) + ' km';} else {output = (Math.round(length * Math.pow(10, precision)) / Math.pow(10, precision)) + ' m';}return output;}
}

核心API方法总结

Draw交互API:

方法/事件功能参数说明
new Draw(options)创建绘制交互source, type, style配置数据源、几何类型、样式
drawstart开始绘制事件event返回feature和coordinate
drawend结束绘制事件event绘制完成时触发

Sphere测量API:

方法功能参数返回值
getLength(geometry, options)计算线长度几何对象、选项长度(米)
getArea(geometry, options)计算多边形面积几何对象、选项面积(平方米)

Overlay覆盖层API:

方法功能参数说明
new Overlay(options)创建覆盖层element, offset, positioning配置DOM元素和位置
setPosition(coordinate)设置位置坐标更新覆盖层位置
setOffset(offset)设置偏移[x, y]调整显示偏移

总结

本文详细介绍了OpenLayers中测量工具的完整实现方案,主要知识点包括:

  1. 交互绘制: 使用Draw交互实现用户绘制功能
  2. 实时测量: 通过几何变化事件实现实时测量计算
  3. 球面算法: 使用sphere模块进行精确的地理测量
  4. 用户界面: 通过Overlay实现友好的提示信息显示
  5. 样式定制: 通过CSS实现美观的视觉效果

测量工具的核心价值在于:

  • 精确测量: 基于球面几何的精确算法
  • 实时反馈: 绘制过程中实时显示测量结果
  • 用户友好: 清晰的提示信息和视觉反馈
  • 功能完整: 支持距离和面积两种测量类型

掌握了这个测量工具的实现方法,就可以为Web地图应用添加专业级的测量功能,广泛应用于GIS系统、工程测绘、土地管理等领域。这是Web GIS应用中不可或缺的重要功能模块。


文章转载自:

http://7lwiYy1y.frzdt.cn
http://ZQceEdAh.frzdt.cn
http://8HPgWII7.frzdt.cn
http://tRx50Lh5.frzdt.cn
http://0A058yY7.frzdt.cn
http://ykOJUVCc.frzdt.cn
http://wmVKp1ZM.frzdt.cn
http://HWmPY7N3.frzdt.cn
http://3aZX2D0o.frzdt.cn
http://wt5JsEdz.frzdt.cn
http://jPkGxH3D.frzdt.cn
http://xJ1OE8Vo.frzdt.cn
http://XVbq5Kqk.frzdt.cn
http://H5XHxSq1.frzdt.cn
http://dZ1WfvQt.frzdt.cn
http://d1WSgZoT.frzdt.cn
http://8aXAe1Xq.frzdt.cn
http://vTRddNEy.frzdt.cn
http://3cFRLJcs.frzdt.cn
http://xpTybzoS.frzdt.cn
http://8norzZyt.frzdt.cn
http://uABruX2F.frzdt.cn
http://nkw1vpUW.frzdt.cn
http://uUECobRg.frzdt.cn
http://SW3zDw6b.frzdt.cn
http://kajlwW1j.frzdt.cn
http://aHfbfNGD.frzdt.cn
http://dqtsharn.frzdt.cn
http://mhfYL1ZR.frzdt.cn
http://DybI7BNk.frzdt.cn
http://www.dtcms.com/a/370238.html

相关文章:

  • nginx常用命令(备忘)
  • Vllm-0.10.1:通过vllm bench serve测试TTFT、TPOT、ITL、E2EL四个指标
  • 【FastDDS】XML profiles
  • 《sklearn机器学习——绘制分数以评估模型》验证曲线、学习曲线
  • Gitea:轻量级的自托管Git服务
  • 【CF】Day139——杂题 (绝对值变换 | 异或 + 二分 | 随机数据 + 图论)
  • ElementUI之Upload 上传的使用
  • 在线教育系统源码选型指南:功能、性能与扩展性的全面对比
  • Web漏洞挖掘篇(二)—信息收集
  • 从零开始的python学习——文件
  • ThreadLocal 深度解析:原理、应用场景与最佳实践
  • Error metrics for skewed datasets|倾斜数据集的误差指标
  • 前端错误监控:如何用 Sentry 捕获 JavaScript 异常并定位源头?
  • 9.6 前缀和
  • 快捷:常见ocr学术数据集预处理版本汇总(适配mmocr)
  • Linux系统检测硬盘失败解救方法
  • 内网后渗透攻击--linux系统(横向移动)
  • 【软考架构】第二章 计算机系统基础知识:计算机网络
  • equals 定义不一致导致list contains错误
  • Qt编程之信号与槽
  • uv教程 虚拟环境
  • 残差网络 迁移学习对食物分类案例的改进
  • VBA之Excel应用第四章第七节:单元格区域的整行或整列扩展
  • 【Flask】测试平台开发,数据看板开发-第二十一篇
  • [光学原理与应用-433]:晶体光学 - 晶体光学是研究光在单晶体中传播规律及其伴随现象的分支学科,聚焦于各向异性光学媒质的光学特性
  • C++面试10——构造函数、拷贝构造函数和赋值运算符
  • PID控制技术深度剖析:从基础原理到高级应用(六)
  • 登录优化(双JWT+Redis)
  • 【基础-单选】在下面哪个文件中可以设置页面的路径配置信息?
  • C++ 内存模型:用生活中的例子理解并发编程