福州婚庆网站建设哪个公司比较专业腾讯网qq网站
摘要
在 WebGIS 开发中,OpenLayers 是一个非常强大的开源地图库,它可以在 Web 应用中渲染高效的地图。本篇文章将介绍如何在 Vue 3 中使用 OpenLayers,并封装一个自定义地图控件组件,实现地图的放大、缩小、长度测量和面积测量功能。
1. 项目介绍
在 WebGIS 相关的前端开发中,OpenLayers 是一个流行的选择。结合 Vue 3,我们可以利用 Composition API 更好地封装和管理地图逻辑,使代码更加清晰和可维护。
本篇文章将介绍如何在 Vue 3 项目中集成 OpenLayers,并基于此封装一个自定义地图组件,提供以下功能:
- 放大(Zoom In)
- 缩小(Zoom Out)
- 测量长度(Measure Length)
- 测量面积(Measure Area)
2. 安装 OpenLayers
首先,我们需要在 Vue 3 项目中安装 OpenLayers。
(1)创建 Vue 3 项目
如果你还没有 Vue 3 项目,可以使用以下命令创建一个新的 Vue 3 项目:
npm create vite@latest vue3-openlayers --template vue
cd vue3-openlayers
npm install
(2)安装 OpenLayers
运行以下命令安装 OpenLayers:
npm install ol
3. 编写 OpenLayers 自定义组件
在 components
目录下创建 OpenLayersMap.vue
组件,该组件负责加载地图并提供交互功能。
完整代码
<!--* @Author: 彭麒* @Date: 2025/2/14* @Email: 1062470959@qq.com* @Description: 此源码版权归吉檀迦俐所有,可供学习和借鉴或商用。-->
<template><div class="container"><div class="w-full flex justify-center flex-wrap"><div class="font-bold text-[24px]">在Vue3中使用OpenLayers自定义组件(放大、缩小、长度测量、面积测量)</div></div><div class="controlbox"><div class="getlength0" @click="getLength('length')"></div><div class="getarea0" @click="getArea('area')"></div><div class="zoomIn" @click="zoomIn"></div><div class="zoomOut" @click="zoomOut"></div></div><div id="vue-openlayers"></div></div>
</template><script setup>
import { onMounted, ref } from "vue";
import "ol/ol.css";
import { Map, View } from "ol";
import Tile from "ol/layer/Tile";
import OSM from "ol/source/OSM";
import MeasureTool from "@/utils/OpenLayersMeasure.ts";
import * as control from "ol/control";const map = ref(null);const zoomIn = () => {if (map.value) {let czoom = map.value.getView().getZoom();map.value.getView().setZoom(czoom + 1);}
};const zoomOut = () => {if (map.value) {let czoom = map.value.getView().getZoom();map.value.getView().setZoom(czoom - 1);}
};const getLength = (type) => {clearMeasure();MeasureTool.measure(map.value, type, true);
};const getArea = (type) => {clearMeasure();MeasureTool.measure(map.value, type, true);
};const clearMeasure = () => {MeasureTool.measure(map.value, "", false);
};const initMap = () => {let raster = new Tile({source: new OSM(),name: "OSM",});map.value = new Map({target: "vue-openlayers",layers: [raster],view: new View({center: [-12000000, 4700000],zoom: 2,}),controls: control.defaults({zoom: false,rotate: false,attribution: false,}).extend([]),});
};onMounted(() => {initMap();
});
</script><style scoped>
.container {width: 840px;height: 590px;margin: 50px auto;border: 1px solid #42B983;position: relative;
}#vue-openlayers {width: 800px;height: 470px;margin: 0 auto;border: 1px solid #42B983;position: relative;
}.controlbox {position: absolute;z-index: 200;bottom: 50px;width: 30px;padding: 5px 7px;height: 120px;right: 30px;border: 1px solid #ccc;border-radius: 4px;cursor: pointer;display: flex;align-content: space-between;flex-direction: column;background-color: #fff;
}.getlength0 {width: 18px;height: 30px;background: url(@/assets/OpenLayers/getlength.png) center center no-repeat;background-size: 16px 16px;
}.getarea0 {width: 18px;height: 30px;background: url(@/assets/OpenLayers/getarea.png) center center no-repeat;background-size: 16px 16px;
}.zoomIn {width: 18px;height: 30px;background: url(@/assets/OpenLayers/zoomin.png) center center no-repeat;background-size: 16px 16px;
}.zoomOut {width: 18px;height: 30px;background: url(@/assets/OpenLayers/zoomout.png) center center no-repeat;background-size: 16px 16px;
}
</style>
4. 测量工具 OpenLayersMeasure.ts
创建 utils/OpenLayersMeasure.ts
文件,封装 OpenLayers 的测量功能:
import Draw from 'ol/interaction/Draw';
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import Point from 'ol/geom/Point';
import { unByKey } from 'ol/Observable';
import Overlay from 'ol/Overlay';
import { Feature } from 'ol';
import { getArea, getLength } from 'ol/sphere';
import LineString from 'ol/geom/LineString';
import Polygon from 'ol/geom/Polygon';
import { Circle as CircleStyle, Fill, Stroke, Style } from 'ol/style';export default {measure(map, measureType, show) {let source = new VectorSource(); // 创建一个新的矢量源let sketch; // 当前绘制的要素let helpTooltipElement; // 帮助提示元素let helpTooltip; // 显示帮助消息的覆盖层let measureTooltipElement; // 测量提示元素let measureTooltip; // 显示测量结果的覆盖层const continuePolygonMsg = ''; // 绘制多边形时显示的消息const continueLineMsg = ''; // 绘制线条时显示的消息createMeasureTooltip(); // 创建测量提示createHelpTooltip(); // 创建帮助提示const pointerMoveHandler = function (evt) {if (evt.dragging) {return;}let helpMsg = '请点击开始绘制'; // 默认帮助消息if (sketch) {const geom = sketch.getGeometry();if (geom instanceof Polygon) {helpMsg = continuePolygonMsg; // 如果是多边形,显示相应消息} else if (geom instanceof LineString) {helpMsg = continueLineMsg; // 如果是线条,显示相应消息}}helpTooltipElement.innerHTML = helpMsg; // 更新帮助提示内容helpTooltip.setPosition(evt.coordinate); // 设置帮助提示位置helpTooltipElement.classList.remove('hidden'); // 显示帮助提示};map.on('pointermove', pointerMoveHandler); // 监听指针移动事件map.getViewport().addEventListener('mouseout', function () {helpTooltipElement.classList.add('hidden'); // 鼠标移出视口时隐藏帮助提示});let draw; // 绘制交互const formatLength = function (line) {const sourceProj = map.getView().getProjection(); // 获取投影坐标系const length = getLength(line, { projection: sourceProj }); // 计算长度let output;if (length > 100) {output = (Math.round(length / 1000 * 100) / 100) + ' km'; // 如果长度大于100米,显示为公里} else {output = (Math.round(length * 100) / 100) + ' m'; // 否则显示为米}return output;};const formatArea = function (polygon) {const sourceProj = map.getView().getProjection(); // 获取投影坐标系const area = getArea(polygon, { projection: sourceProj }); // 计算面积let output;if (area > 10000) {output = (Math.round(area / 1000000 * 100) / 100) + ' km<sup>2</sup>'; // 如果面积大于10000平方米,显示为平方公里} else {output = (Math.round(area * 100) / 100) + ' m<sup>2</sup>'; // 否则显示为平方米}return output;};for (const layerTmp of map.getLayers().getArray()) {if (layerTmp.get('name') == 'feature') {source = layerTmp.getSource(); // 获取存放要素的矢量层}}function addInteraction() {const type = (measureType == 'area' ? 'Polygon' : 'LineString'); // 根据测量类型设置绘制类型draw = new Draw({source: source,type: type,style: new Style({fill: new Fill({color: 'rgba(255, 255, 255, 0.2)', // 填充颜色}),stroke: new Stroke({color: 'rgba(255, 0, 0, 0.5)', // 线条颜色lineDash: [10, 10], // 虚线样式width: 2, // 线条宽度}),image: new CircleStyle({radius: 5, // 圆点半径stroke: new Stroke({color: 'rgba(0, 0, 0, 0.7)', // 圆点边框颜色}),fill: new Fill({color: 'rgba(255, 255, 255, 0.2)', // 圆点填充颜色}),}),}),});map.addInteraction(draw); // 添加绘制交互let listener;draw.on('drawstart', function (evt) {sketch = evt.feature; // 设置当前绘制的要素let tooltipCoord = evt.coordinate; // 提示坐标listener = sketch.getGeometry().on('change', function (evt) {const geom = evt.target;let output;if (geom instanceof Polygon) {output = formatArea(geom); // 格式化面积tooltipCoord = geom.getInteriorPoint().getCoordinates(); // 获取多边形内部点坐标} else if (geom instanceof LineString) {output = formatLength(geom); // 格式化长度tooltipCoord = geom.getLastCoordinate(); // 获取线条最后一个点的坐���}measureTooltipElement.innerHTML = output; // 更新测量提示内容measureTooltip.setPosition(tooltipCoord); // 设置测量提示位置});map.on('dblclick', function (evt) {const point = new Point(evt.coordinate);source.addFeature(new Feature(point)); // 添加双击点要素});});draw.on('drawend', function () {measureTooltipElement.className = 'tooltip tooltip-static'; // 设置测量提示样式measureTooltip.setOffset([0, -7]); // 设置测量提示偏移sketch = null; // 清空当前绘制的要素measureTooltipElement = null; // 清空测量提示元素createMeasureTooltip(); // 创建新的测量提示unByKey(listener); // 移除监听器map.un('pointermove', pointerMoveHandler); // 移除指针移动事件监听map.removeInteraction(draw); // 移除绘制交互helpTooltipElement.classList.add('hidden'); // 隐藏帮助提示});}function 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', // 设置定位方式});map.addOverlay(helpTooltip); // 添加帮助提示覆盖层}function 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', // 设置定位方式});map.addOverlay(measureTooltip); // 添加测量提示覆盖层}addInteraction(); // 添加绘制交互const vector = new VectorLayer({name: 'lineAndArea',source: source,style: new Style({fill: new Fill({color: 'rgba(255, 255, 255, 0.2)', // 填充颜色}),stroke: new Stroke({color: 'red', // 线条颜色width: 2, // 线条宽度}),image: new CircleStyle({radius: 7, // 圆点半径fill: new Fill({color: '#ffcc33', // 圆点填充颜色}),}),}),zIndex: 16, // 设置图层顺序});if (show && measureType) {map.addLayer(vector); // 显示测量图层} else {map.getOverlays().clear(); // 清除所有覆盖层map.getLayers().getArray().forEach((layer, index, array) => {if (layer.get('name') == 'lineAndArea') {map.removeLayer(layer); // 移除测量图层}});}},
};
5. 在 Vue 项目中使用组件
在 App.vue
中引入 OpenLayersMap.vue
组件:
<template> <div> <OpenLayersMap /> </div>
</template>
<script setup>
import OpenLayersMap from "@/components/OpenLayersMap.vue";
</script>
6. 运行项目
在项目根目录运行以下命令,启动 Vue 开发服务器:
npm run dev
然后在浏览器中访问 http://localhost:5173/
,你就可以看到 OpenLayers 地图,并且可以使用放大、缩小、长度测量和面积测量功能了。
7. 结语
本篇文章介绍了如何在 Vue 3 项目中集成 OpenLayers,并封装了一个自定义地图控件组件,实现了放大、缩小、长度测量和面积测量功能。
你可以根据自己的需求扩展更多功能,比如:
- 添加更多测量单位
- 显示测量结果的弹窗
- 增加图层切换等功能
希望这篇文章能帮助到你!如果你觉得有用,请点赞支持!🚀🚀🚀
💬 交流与讨论
如果你有任何问题或建议,欢迎在评论区留言交流!💡