vue3.0 + typescript openlayers实现地图标点、移动、画线、显示范围、测量长度、测量面积、画三角形、画正方形、画圆、线选、画笔、清除测量、清除地图所有等功能

由于最近项目中用到了地图,所以今天有时间把代码摘出来,自己写个地图的小demo标记下
话不多说,先看效果:

map1

1、安装ol

 npm i ol@6.14.1

这是我的目录结构

2、下面是OpenlayerMap/index.vue(地图)的代码,记得申请地图得key,记得填到下方的代码中

<template><div class="openlayersMap"><div id="map"></div><!-- 鼠标经纬度指示 --><divid="mouse-position":style="assignMouseIndicatePosition"v-if="needMouseIndicate"></div><!-- 图层上展示的弹窗 --><div id="popup-box"><!-- <span id="popup-closer"></span> --><div id="popup-content"><slot :value="sol"></slot></div></div><div id="popup-box-right" :style="{top:'-90px',left:'5px'}"><hr :style="{border: '2px solid #2af0e1',borderRadius: '40px',width:'2vw',marginTop:'80px'}"/><div id="popup-content"><slot name="right" :value="sol"></slot></div></div></div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, nextTick } from "vue";
import { getTopLeft, getWidth } from "ol/extent";
import { getArea, getLength } from "ol/sphere";
import { Map, View, Feature, Overlay } from "ol";
import { Draw, Modify, Snap } from "ol/interaction";
import { Circle as CircleGeo} from "ol/geom";
import { Circle, Fill, Stroke, Style, Text, Icon } from "ol/style";
import { Tile, Vector as VectorLayer, Image } from "ol/layer";
import { defaults, FullScreen, MousePosition } from "ol/control";
import { get, transform, Projection, fromLonLat } from "ol/proj";
import WMTSTileGrid from "ol/tilegrid/WMTS";
import { createStringXY } from "ol/coordinate";
import {OSM,WMTS,Vector as VectorSource,ImageStatic as ImageStaticSource,Cluster,
} from "ol/source";
import {IMosueIndicatePosition,IVectorLayer,IRouteMap,IRouteInfo,IHistoryRoute,
} from "@/types/thirdModule/olMap";
import { LineString, Point, Polygon } from "ol/geom";
import { createBox, createRegularPolygon } from "ol/interaction/Draw";
import CircleStyle from "ol/style/Circle";
import { unByKey } from "ol/Observable";
const startImg = require("@/image/startPoint.png");//轨迹起点图标
const endImg = require("@/image/endPoint.png");//轨迹终点图标
// 是否显示地图的经纬度指示
const props = withDefaults(defineProps<{ needMouseIndicate: boolean }>(), {needMouseIndicate: true
});
const modelMange: String='';
let sol: any = ref(null); // 插槽回填数据
let assignMouseIndicatePosition: IMosueIndicatePosition;
let drawSource: VectorSource;
let drawVector: any = null;
let exportDrawVector: any = ref(null);
let map: Map | undefined = undefined;
const exportMap =ref<any>(null)
let saveVectorLayer: any | null = null;
let mousePositionControl: MousePosition;
const layerObj: any=ref(null);
const isActivePopup:any=ref(false);
const singleLayer:any=ref(null);
const saveGroupLayer:any=ref(null);  //图形Layerconst draw:any=ref(null);  //画笔
const drawList:any=ref([]);  //
const snap:any=ref(null);  //const state=reactive<any>({shapeType:'',layerIndex:0,drawCache:[],helpTooltip:'',continueLineMsg:'',helpTooltipElement:null,measureTooltipElement:null,measureTooltip:null,sketch:null,listener:null,//路径相关routeMap:[],})/*** 在地图上绘制图层*/
//#region
const initDrawSource = () => {drawSource = new VectorSource();drawVector = new VectorLayer({source: drawSource,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",}),}),}),});exportDrawVector.value=drawVectorconst modify = new Modify({ source: drawSource });// exportMap.value.addInteraction(modify)return modify;
};
// 得到初始化地图控制器
function getIninialControl(mouseInfoTargetId: string = "mouse-position") {if (props.needMouseIndicate) {mousePositionControl = new MousePosition({coordinateFormat: createStringXY(5),projection: "EPSG:4326",className: "custom-mouse-position",target: document.getElementById(mouseInfoTargetId) || undefined,undefinedHTML: " ",});return defaults({ zoom: false, rotate: false }).extend([mousePositionControl,// 全屏插件// new FullScreen(),]);} else {return defaults({ zoom: false, rotate: false });}
}
// 地图初始化
const initMap=()=>{try {let projection: any = get("EPSG:4326");let projectionExtent = projection!.getExtent();let size = getWidth(projectionExtent) / 256;let resolutions = new Array(18);let matrixIds = new Array(18);const modify = initDrawSource();for (let i = 1; i <= 18; i++) {resolutions[i] = size / Math.pow(2, i);matrixIds[i] = i;}map = new Map({controls: getIninialControl(),layers: [// 卫星图层数据new Tile({source: new WMTS({url: "http://t{0-7}.tianditu.gov.cn/img_c/wmts?tk=95152306628d50b66db98577f457ac40",// url: "http://t{0-7}.tianditu.gov.cn/img_c/wmts?tk=c9b843b717f7f1ccd4774ba4a469cad7",layer: "img",style: "default",matrixSet: "c",projection: projection,format: "tiles",tileGrid: new WMTSTileGrid({origin: getTopLeft(projectionExtent),resolutions: resolutions,matrixIds: matrixIds,}),wrapX: true,}),}),// 卫星行政数据new Tile({source: new WMTS({url: "http://t{0-7}.tianditu.gov.cn/cia_c/wmts?tk=95152306628d50b66db98577f457ac40",// url: "http://t{0-7}.tianditu.gov.cn/cia_c/wmts?tk=c9b843b717f7f1ccd4774ba4a469cad7",layer: "cia", //vec/cva img/ciamatrixSet: "c",format: "tiles",style: "default",projection: projection,tileGrid: new WMTSTileGrid({origin: getTopLeft(projectionExtent),resolutions: resolutions,matrixIds: matrixIds,}),wrapX: true,}),}),drawVector,],target: "map",view: new View({center: [108.8821,34.2227], // 初始化中心点 西安软件园maxZoom: 18,minZoom: 8,zoom: 15,//地图层级projection: "EPSG:4326",}),});exportMap.value=mapmap.addInteraction(modify);} catch (err) {console.log(err);}
}// 在页面上展示点
const lastShowPoints= (dataList: any[], imgStr: string,scale: number=1,fn?: any,)=>{   if (dataList.length === 0)  return;try {let features: any[] = []; // 定义坐标simgStr =require(`../../image/${imgStr}`);let vectorLayer = new VectorLayer({renderBuffer: 200,source: new VectorSource(),style: (feature) => [new Style({image: new Icon({src: imgStr,scale: scale,// imgSize: [50, 50],}),}),],});// console.log('points',dataList);dataList.forEach((item) => {item.sol = fn(JSON.parse(JSON.stringify(item)));if ((item.lng || item.longitude) && (item.lat || item.latitude)) {let newObj = Object.assign({}, item);const wz=[+item.lng?item.lng:item.longitude, +item.lat?item.lat:item.latitude];// console.log('wz',wz);newObj.geometry = new Point(wz);features.push(new Feature(newObj));if(item.sol.circle){let circleFeature = new Feature({  //添加面geometry: new CircleGeo([wz[0]*1,wz[1]*1], 0.005)});circleFeature.setStyle(new Style({  // 设置样式fill: new Fill({color: 'rgba(255, 255, 255, 0.1)'}),stroke: new Stroke({color: 'rgba(217, 11, 53, 0.4)',lineDash: [20, 10]})}));features.push(circleFeature);}}});vectorLayer.getSource()!.addFeatures(features);map!.addLayer(vectorLayer);saveVectorLayer=vectorLayerreturn saveVectorLayer} catch (error) {console.log(error);}
}//点数组绘图
const groupPlot=(data:any,color?:string,borderColor?:string)=>{console.log(color);color=color || "rgba(252, 51, 40, 0.2700)"let source = new VectorSource();let style = new Style({text: new Text({// font: '15px Microsoft YaHei',// text:'xxxx',fill: new Fill({color: '#fff'})}),fill: new Fill({color}),stroke: new Stroke({color: borderColor || 'rgba(252, 51, 40, 1)',width:  2}),image: new Circle({radius: 10,fill: new Fill({color: '#ffcc33'})})});//矢量图层let vectorLayer = new VectorLayer({source: source,style: style,});    let features: any[] = [];//多边形要素类data.map((n:any)=>{//声明一个新的数组let coordinatesPolygon = new Array();//循环遍历将经纬度转到"EPSG:4326"投影坐标系下for (let i = 0; i < n.pointList.length; i++) {let pointTransform = fromLonLat([n.pointList[i][0], n.pointList[i][1]], "EPSG:4326");coordinatesPolygon.push(pointTransform);}let feature = new Feature({...n,geometry: new Polygon([coordinatesPolygon]),//多边形此处注意一定要是[坐标数组]});features.push(feature)})source.addFeatures(features);map!.addLayer(vectorLayer);saveGroupLayer.value=vectorLayer;return vectorLayer
}// 点击地图后的交互(主要是图标)
const mapClick=()=>{    map!.on("click", (event) => {console.log(784,event)removeModel();let bool=true;let pixel =map!.getEventPixel(event.originalEvent);map?.forEachFeatureAtPixel(pixel, (feature: any,a) => {     try {let myfeature;if(feature.values_.features){//聚合点击myfeature=feature.values_.features[0];}else{myfeature=feature;}// console.log('myfeature',myfeature);if(!myfeature.values_.sol) returnsol.value=myfeature.values_.sol;console.log('sol',sol.value)// console.log('sol',sol.value || myfeature.values_);let x = myfeature.get("longitude") || myfeature.get("lng"),y = myfeature.get("latitude") || myfeature.get("lat");let coordinate = [x, y]; //来控制弹框样式let popupClass="popup-box";   //popup-box-rightif(sol.value.popupClass) popupClass=sol.value.popupClass//来控制弹框边距样式// this.modelMangebottom=this.sol.bottom || '0';// this.modelMangeleft=this.sol.left || '0';let overlay = new Overlay({ element: document.getElementById(popupClass) || undefined,});map!.addOverlay(overlay);overlay.setPosition(coordinate);layerObj.value = overlay;bool=falseisActivePopup.value=true;} catch (error) {console.log(error);}   });// if((this.$parent as any).isPopup && (this.$parent as any).isPopup.length>0) (this.$parent as any).isPopup=[];if(bool) map!.removeLayer(singleLayer.value);});
}//清除已打开的弹框
const removeModel=()=>{if(isActivePopup.value){layerObj.value.setPosition(undefined);sol.value = {};layerObj.value=null;isActivePopup.value=false}
}// 移动到指定中心点位
const moveToPoint=async (x: number, y: number)=>{// console.log(x,y);map!.getView().animate({center: [x, y],});
}//画线
const drawLine=(bool:boolean)=>{draw.value && map!.removeInteraction(draw.value);draw.value = new Draw({source:drawVector.getSource(),type: "LineString",freehand: bool,stopClick: true,});exportMap.value.addInteraction(draw.value);draw.value && draw.value.on('drawend',() =>drawList.value.push(draw.value))// console.log('drawVector',drawVector);return draw.value;
}// 多边形
const drawPolygon=()=>{draw.value && map!.removeInteraction(draw.value);draw.value = new Draw({source: drawVector.getSource(),type: "Polygon",stopClick: true,});exportMap.value.addInteraction(draw.value);draw.value && draw.value.on('drawend',() =>drawList.value.push(draw.value))
}// 画圆
const drawCircle=(type:string)=>{draw.value && map!.removeInteraction(draw.value);if (type == "triangle") {//正三角draw.value = new Draw({source: drawVector.getSource(),type: "Circle",geometryFunction: createRegularPolygon(3),stopClick: true,});} else if (type == "circle") {draw.value = new Draw({//正圆source: drawVector.getSource(),type: "Circle",stopClick: true,});} else if (type == "square") {draw.value = new Draw({//正方形source: drawVector.getSource(),type: "Circle",geometryFunction: createRegularPolygon(4),stopClick: true,});} else if (type == "rectangle") {draw.value = new Draw({//长方形source: drawVector.getSource(),type: "Circle",geometryFunction: createBox(),stopClick: true,});}snap.value = new Snap({source: drawSource });exportMap.value.addInteraction(snap.value); //鼠标捕捉exportMap.value.addInteraction(draw.value);draw.value && draw.value.on('drawend',() =>drawList.value.push(draw.value))
}/** 测距、测面*/
const measure=(type:string)=>{draw.value && exportMap.value.removeInteraction(draw.value);state.shapeType = type;state.layerIndex++;state.drawCache[state.layerIndex] = {feature: null,measureTooltip: null,};if (draw.value) {exportMap.value.removeInteraction(draw.value);}// 添加map事件addMapEvent();
}const addMapEvent=()=>{exportMap.value.on("pointermove", (evt:any) => {draw.value? pointerMoveHandler(evt): exportMap.value.removeOverlay(state.helpTooltip);});exportMap.value.getViewport().addEventListener("mouseout", () => {state.helpTooltipElement &&state.helpTooltipElement.classList.add("hidden");});addInteraction();
}const addInteraction=()=> {draw.value = new Draw({source: drawVector.getSource(),type: state.shapeType,style: new Style({fill: new Fill({ color: "rgba(255, 255, 255, 0.2)" }),stroke: new Stroke({color: "rgba(255, 255, 255, 1)",lineDash: [10, 10],width: 2,}),image: new CircleStyle({radius: 5,stroke: new Stroke({ color: "rgba(255, 255, 255, 0.8)" }),fill: new Fill({ color: "rgba(255, 255, 255, 0.2)" }),}),}),});exportMap.value.addInteraction(draw.value);createMeasureTooltip();createHelpTooltip();drawHandler();
}const createMeasureTooltip=()=>{if(state.measureTooltipElement) {state.measureTooltipElement.parentNode.removeChild(state.measureTooltipElement);}state.measureTooltipElement = document.createElement("div");state.measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";state.measureTooltip = new Overlay({element: state.measureTooltipElement,offset: [0, -15],positioning: "bottom-center",});exportMap.value.addOverlay(state.measureTooltip);
}const createHelpTooltip=()=>{if (state.helpTooltipElement) {state.helpTooltipElement.parentNode.removeChild(state.helpTooltipElement);}state.helpTooltipElement = document.createElement("div");state.helpTooltipElement.className = "ol-tooltip hidden";state.helpTooltip = new Overlay({element: state.helpTooltipElement,offset: [15, 0],positioning: "center-left",});exportMap.value.addOverlay(state.helpTooltip);
}const drawHandler=()=>{draw.value.on("drawstart", (evt:any) => {state.sketch = evt.feature;let tooltipCoord = evt.coordinate;state.listener = state.sketch.getGeometry().on("change", (evt:any) => {let output;const geom = evt.target;if (geom instanceof LineString) {output = formatLength(geom);tooltipCoord = geom.getLastCoordinate();} else if (geom instanceof Polygon) {output = formatArea(geom);tooltipCoord = geom.getInteriorPoint().getCoordinates();}let closeBtn ="<i class='tooltip-close-btn tooltip-close-btn_" +state.layerIndex +"' data-index='" +state.layerIndex +"'></i>";state.measureTooltipElement.innerHTML = output + closeBtn;state.measureTooltip.setPosition(tooltipCoord);state.drawCache[state.layerIndex].measureTooltip = state.measureTooltip;});});draw.value.on("drawend", (evt:any) => {// console.log('hhhh');// (this.$parent as any).toolsindex ='';state.drawCache[state.layerIndex].feature = evt.feature;state.measureTooltipElement.className = "ol-tooltip ol-tooltip-static";state.measureTooltip.setOffset([0, -7]);state.sketch = null;state.measureTooltipElement = null;createMeasureTooltip();unByKey(state.listener);exportMap.value.removeInteraction(draw.value);draw.value = null;        // 删除图层const self = this;document?.querySelector(".tooltip-close-btn_" + state.layerIndex).addEventListener("click", function (e:any) {// console.log('dataset',e);// state.drawVector//     .getSource()//     .removeFeature(state.drawCache[state.dataset.index].feature);//     state.map.removeOverlay(//       state.drawCache[state.dataset.index].measureTooltip//   );//   delete state.drawCache[state.dataset.index];});});
}const formatLength=(line:any)=>{const sourceProj = exportMap.value.getView().getProjection(); //获取投影坐标系var length = getLength(line, { projection: sourceProj });var output;if (length > 100) {output = Math.round((length / 1000) * 100) / 100 + " " + "km";} else {output = Math.round(length * 100) / 100 + " " + "m";}return output;
}const formatArea=(polygon:any)=>{const sourceProj = exportMap.value.getView().getProjection(); //获取投影坐标系const geom = polygon.clone().transform(sourceProj, "EPSG:3857");const area = getArea(geom);let 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;
}const pointerMoveHandler=(evt:any)=>{if (evt.dragging) {return;}/** @type {string} */var helpMsg = "测量";if (state.sketch) {var geom = state.sketch.getGeometry();if (geom instanceof LineString) {helpMsg = state.continueLineMsg;}}state.helpTooltipElement.innerHTML = helpMsg;state.helpTooltip.setPosition(evt.coordinate);state.helpTooltipElement.classList.remove("hidden");
}
// **************//路径相关
// 开始显示一条轨迹
const  showWay=async(_routeInfo: IRouteInfo)=>{  if (state.routeMap.some((route:any) => route.id === _routeInfo.id)) {// 已经有了路径,直接移动视野到该点moveToPoint(_routeInfo.gis[0][0], _routeInfo.gis[0][1]);return;}// 根据一系列点设置地图视野中点const point = _routeInfo.gis[0];// 将地图设置到最大缩放度const view = map!.getView();const MaxZoom = view.getMaxZoom();const MinZoom = view.getMinZoom();const zoom = Math.ceil((MaxZoom + MinZoom) / 2);view.setZoom(zoom);await moveToPoint(point[0], point[1]);showRouterMap(_routeInfo);
}// 路径显示
const showRouterMap=(_routeInfo: any)=>{let pointsList = _routeInfo.gis;// pointsList.map(n=>{//   n=[1*n[0],1*n[1]]// })let roadLine = new LineString(pointsList);let output=formatLength(roadLine)console.log('output',pointsList,output);let roadLineSource = new VectorSource({features: [new Feature(roadLine)],});const roadLineLayer = new VectorLayer({source: roadLineSource,style: new Style({stroke: new Stroke({color: "#38A0FF",width: 4,}),}),});map!.addLayer(roadLineLayer);let pointLayer1 = addVectorLabel({pointsList: pointsList[0],txt: "起",});let pointLayer2 = addVectorLabel({pointsList: pointsList[pointsList.length - 1],txt: "终",});let label = addVectorLabel({pointsList: [pointsList[0][0], pointsList[0][1]],txt: _routeInfo.name + ",全程" + output,fill: "#111111",stroke: "#ffffff",});// 保存实例state.routeMap.push({id: _routeInfo.id as string,routeLayerExample: roadLineLayer,vectorLabels: [label],points: [pointLayer1, pointLayer2],});
}// 添加矢量标签(例如路径的起终点、描述)
const addVectorLabel=(opt: any)=>{let vectorSource = new VectorSource(); //矢量标注的数据源const layer = new VectorLayer({source: vectorSource,}); //矢量标注图层map!.addLayer(layer);if (opt.txt == "起" || opt.txt == "终") {// 添加起点以及终点的layerlet newFeature = new Feature({geometry: new Point(opt.pointsList || []),});newFeature.setStyle(new Style({image: new Icon({src: opt.txt == "起" ? startImg : endImg,offset: [0, -26],offsetOrigin: "bottom-right",size: [32, 60],}),fill: new Fill({ color: "#ffffff" }),zIndex: 1,}));// 将新要素添加到数据源中vectorSource.addFeature(newFeature);return layer;}let overlayerElement = document.createElement("div");overlayerElement.className = "ol-tooltip-draw-route";overlayerElement.innerHTML = opt.txt;const overlayObj = new Overlay({element: overlayerElement,offset: [0, -30],positioning: "bottom-center",});overlayObj.setPosition(opt.pointsList);map!.addOverlay(overlayObj);return overlayObj;
}// 清除部分路径
const clearRoute=(_routeId: number|string | Array<number|string>)=>{  if (Array.isArray(_routeId)) {for (let routeId of _routeId) {let routeWayIndex = state.routeMap.findIndex((it:any)=>it.id == routeId)let routeItem = state.routeMap[routeWayIndex]// console.log('routeItem',routeItem,state.routeMap,routeId);exportMap.value.removeLayer(routeItem.routeLayerExample);routeItem.points?.forEach((it:any) => exportMap.value.removeLayer(it));routeItem.vectorLabels.forEach((it:any) => exportMap.value.removeOverlay(it));state.routeMap.splice(routeWayIndex, 1);}} else {if (!_routeId) return;let routeWayIndex = state.routeMap.findIndex((it:any)=>it.id == _routeId)let routeItem = state.routeMap[routeWayIndex]exportMap.value.removeLayer(routeItem.routeLayerExample);routeItem.points?.forEach((it:any) => exportMap.value.removeLayer(it));routeItem.vectorLabels.forEach((it:any) => exportMap.value.removeOverlay(it));state.routeMap.splice(routeWayIndex, 1);}
}nextTick(()=>{})
defineExpose({measure,removeModel,lastShowPoints,groupPlot,moveToPoint,exportMap,drawLine,drawPolygon,drawCircle,draw,exportDrawVector,state,showWay,clearRoute})onMounted(() => {initMap();mapClick()
});</script><style lang="scss" scoped>
.openlayersMap {width: 100%;height: 100%;#map {height: 100%;}#mouse-position {position: absolute;top: 10px;right: 20px;color: #fff;font-weight: bold;font-size: 18px;}#popup-box{bottom: 25px;left: 0px;background-color: rgba(0, 0, 1, 0.5);box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);}#popup-box ,#popup-box-right{position: absolute;z-index: -1;box-sizing: border-box;padding: 4px 6px;border-radius: 4px;border: 2px solid #84f199;min-width: 280px;position: absolute;// bottom: 20px;// left: -50px;#popup-closer {font-size: 14px;height: 20px;line-height: 20px;text-align: right;position: absolute;right: 0;top: 0;margin-right: 4px;user-select: none;cursor: pointer;color: #fff;}#popup-content {// margin-top: 24px;display: flex;max-height: 600px;// overflow-y: scroll;position: relative;}&:after,&:before {top: 100%;border: solid transparent;content: " ";height: 0;width: 0;position: absolute;pointer-events: none;}&:after {border-top-color: white;border-width: 10px;left: 48px;margin-left: -10px;}&:before {border-top-color: #cccccc;border-width: 11px;left: 48px;margin-left: -11px;}}#popup-box-right{// bottom: -24vh;// left: 2vh;border: none;display: flex;&:after,&:before {content:none;}> #popup-content{background: url("~@/image/chart.png") no-repeat;background-size: 100% 100%;}}}
</style>

4、这是我地图中用到的三张图片,chart.png是地图上弹窗的背景图。icon.png是个标点的小图标,剩下两个是地图画轨迹时一个起点和终点的图标,如果你粘贴我的代码,没这几张图片会报错

5、全局引入

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
//引入地图
import components from "@/components/OpenlayerMap/index";
let app=createApp(App)
Object.values(components).forEach((com) => {app.use(com);
});
app.use(store).use(router).use(Antd).mount('#app')

6、页面代码

<template><div class="Box"><div class="homeBox"><div class="leftBox"><a-button type="primary" @click="showIcon">在地图上显示点</a-button><br /><br /><a-button type="primary" @click="moveXiAn([108.9423, 34.261])">移动到西安钟楼</a-button><br /><br /><a-button type="primary" @click="moveXiAn([108.8821, 34.2227])">移动到初始点</a-button><br /><br /><a-button type="primary" @click="trajectory">显示一段轨迹</a-button><br /><br /><a-button type="primary" @click="clearTrajectory">清除上面的轨迹</a-button><br /><br /><a-button type="primary" @click="range">显示范围</a-button><br /><br /></div><!-- 地图 --><section class="map-wrap"><OpenlayerMapref="mapRef":assignMouseIndicatePosition="{zIndex: 1,right: '83px',top: '14px',fontSize: '16px',fontWeight: '400',color: '#fff',}"><!-- 地图弹框 --><template #right="sol"><div :style="{ width: '0vh' }"></div><div><div class="map_right" v-if="sol.value?.showLength"><div v-for="(item, l) in sol.value.show" :key="l"><span v-for="(n, i) in item" :key="i">{{ n }}</span></div><button v-if="sol.value.button">{{ sol.value.buttonText ? sol.value.buttonText : "查看" }}</button></div><div class="map_right" v-else><span v-for="(n, i) in sol.value?.show" :key="i">{{ n }}</span><button v-if="sol.value?.button">{{ sol.value.buttonText ? sol.value.buttonText : "查看" }}</button></div></div></template></OpenlayerMap><!-- 工具箱 --><div class="work-box"><div class="tool"><div v-for="(n, i) in toolData" :key="i"><span>{{ n.title }}</span><div><span:style="{ color: toolsindex == `${i}${l}` ? '#409EFF' : '' }"v-for="(e, l) in n.data":key="l"@click="clickTool(e, i, l)">{{ e.title }}</span></div></div></div></div></section></div></div>
</template><script lang="ts" setup>
import { defineComponent, reactive, ref, onMounted, nextTick } from "vue";
const mapRef = ref<null | HTMLElement>(null);const data = ref<any>([{id: 14,name: "张三",longitude: "108.8821",latitude: "34.2227",pointList: [[108.8646, 34.2136],[108.8695, 34.2134],[108.8647, 34.2187],],},{id: 14,name: "李四",longitude: "108.8867",latitude: "34.2283",pointList: [[108.8943, 34.2149],[108.8951, 34.2127],[108.8909, 34.2135],[108.8973, 34.2122],],},
]);
const toolData = ref<any>([{title: "基础",key: 0,data: [{ title: "测量距离" },{ title: "测量面积" },{ title: "清除测量" },],},{title: "消防",key: 1,data: [{ title: "画笔" },{ title: "隔离带" },{ title: "线选" },{ title: "框选" },{ title: "多边形选" },{ title: "圈选" },{ title: "清除" },],},
]);
const toolsindex = ref<string>("");
//地图跳转
function moveXiAn(coordinate: any) {(mapRef.value as any).moveToPoint(...coordinate);
}
//显示轨迹
function trajectory() {let gis = [[108.9423, 34.261],[108.9424, 34.2568],[108.9478, 34.2566],[108.9538, 34.2496],];(mapRef.value as any).showWay({id: 11, //idgis: gis,name: "轨迹", //});
}
function clearTrajectory() {(mapRef.value as any).clearRoute(11);
}
//地图上显示图标
function showIcon() {data.value.forEach((item: any) => {nextTick(() => {(mapRef.value as any).lastShowPoints(data.value,"icon.png",1,(item: any) => {return {show: [`${item.name}`,`纬度:${item.latitude},经度:${item.longitude}`,],data: item,popupClass: "popup-box-right",bottom: "-13vh",left: "15px",button: true, //地图弹窗是否显示按钮buttonText: "点击", //地图弹窗按钮文字circle: true, //是否展示范围};});});});
}//地图上显示范围
function range() {nextTick(() => {(mapRef.value as any).groupPlot(data.value);});
}
//点击工具
function clickTool(n:any,i:number,l:number){if(toolsindex.value=='') toolsindex.value=`${i}${l}`; else {toolsindex.value='';nextTick(()=>{(mapRef.value as any).exportMap.removeInteraction((mapRef.value as any).draw);(mapRef.value as any).draw = null;});}switch (`${i}${l}`) {case '00': //测量距离if(toolsindex.value==`00`) (mapRef.value as any).measure("LineString");break;case '01': //测量面积if(toolsindex.value==`01`) (mapRef.value as any).measure("Polygon");break;case '02': //清除测量if(toolsindex.value==`02`) {for (const key in (mapRef.value as any).state.drawCache) {(mapRef.value as any).exportMap.removeOverlay((mapRef.value as any).state.drawCache[key].measureTooltip);}(mapRef.value as any).exportDrawVector.getSource().clear();toolsindex.value =''}break;case "01": //测量距离if (toolsindex.value == `01`) (mapRef.value as any).measure("LineString");break;case "02": //测量面积if (toolsindex.value == `02`) (mapRef.value as any).measure("Polygon");break;case "03": //清除测量if (toolsindex.value == `03`) {for (const key in (mapRef.value as any).drawCache) {(mapRef.value as any).map.removeOverlay((mapRef.value as any).drawCache[key].measureTooltip);}(mapRef.value as any).drawVector.getSource().clear();toolsindex.value = "";}break;case "10": //点if (toolsindex.value == `10`) (mapRef.value as any).drawLine(true);break;case "11": //正三角 隔离带if (toolsindex.value == `11`) (mapRef.value as any).drawCircle("triangle");break;case "12": //线if (toolsindex.value == `12`) (mapRef.value as any).drawLine(false);break;case "13": //正方形if (toolsindex.value == `13`) (mapRef.value as any).drawCircle("square");break;case "14": //多边形if (toolsindex.value == `14`) (mapRef.value as any).drawPolygon();break;case "15": //圆形if (toolsindex.value == `15`) (mapRef.value as any).drawCircle("circle");break;case '16': //清除if(toolsindex.value==`16`){     (mapRef.value as any).exportDrawVector.getSource().clear();toolsindex.value =''} break;default:break;}(mapRef.value as any).draw && (mapRef.value as any).draw.on("drawend", () => {(mapRef.value as any).exportMap.removeInteraction((mapRef.value as any).draw);(mapRef.value as any).draw = null;toolsindex.value = '';});
}</script>
<style lang="scss" scoped>
.Box {width: 100%;height: 100vh;position: relative;.homeBox {width: 100%;height: 100%;border: 1px solid gray;display: flex;justify-content: space-between;align-items: flex-start;.leftBox {width: 20%;}.map-wrap {flex: 1;height: 100%;position: relative;}}
}//地图弹窗样式
.map_right {color: #fff;font-size: 14px;padding: 15px 20px;display: flex;flex-direction: column;span:nth-child(n) {display: flex;flex-direction: column;margin-bottom: 10px;white-space: nowrap;}span:nth-child(1) {font-size: 16px;font-weight: 500;margin-top: 10px;}> button {// color: #409EFF;background: #409eff;text-align: center !important;padding: 6px 0;border: none;margin: 10px 16px 20px 16px;cursor: pointer;}
}
.work-box {display: flex;align-items: center;color: #fff;position: absolute;bottom: 60px;left: 30px;.tool {position: absolute;// bottom: 100%;display: flex;flex-direction: column;justify-content: center;border: 1px solid;margin: 0 20px;background: gray;border-radius: 5px;> * {padding: 0 10px;display: flex;width: 510px;align-items: center;> span {font-size: 16px;font-weight: 700;}> div:first-child {border-bottom: 1px solid;}> div {margin: 3px 10px;display: flex;flex: 1;justify-content: space-around;span {display: inline-block;padding: 8px;border: 1px solid;cursor: pointer;margin: 3px 5px;font-size: 14px;border-radius: 5px;}}}}
}
</style>

vue3.0 + typescript openlayers实现地图标点、移动、画线、显示范围、测量长度、测量面积、画三角形、画正方形、画圆、线选、画笔、清除测量、清除、地图上展示弹窗等功能相关推荐

  1. Vue3.0 + typescript 高仿网易云音乐 WebApp

    Vue3.0 + typescript 高仿网易云音乐 WebApp 前言 Vue3.0 的正式发布,让我心动不已,于是尝试用 vue3 实现一个完整的项目,整个项目全部使用了 composition ...

  2. Vue3.0+TypeScript+Vite

    Vue3.0 + TypeScript + Vite Vue3.0+TypeScript+Vite 项目创建(推荐使用yarn包管理器) 项目结构 main.ts App.vue: Compositi ...

  3. 来自一枚rookie的项目开发——vue3.0+typescript+element-plus+vue-router4+Pinia之动态路由(上)

    动态路由(上) 配合动态路由的菜单(理论上可无限递归) <!-- 父组件 --> <template><div class="menu">< ...

  4. 用Vue3.0+typescript写vuex的模块化拆分

    用Vue3.0+typescript写vuex的模块化拆分 最近自己学习了下vue3.0+ts,在vuex这一块遇到些问题记录一下 以下代码中的写法请参考官方vuex文档: https://next. ...

  5. 来自一枚rookie的项目开发——vue3.0+typescript+element-plus+vue-router4+Pinia之动态路由(下)

    路由守卫结合JWT token鉴权控制路由跳转 Pinia使用 import { defineStore } from 'pinia' import { Entity } from '../globa ...

  6. 【VUE】vue3.0后台常用模板

    vue3.0后台常用模板: 1.vue-admin-perfect 在线预览 gitee国内访问地址:https://yuanzbz.gitee.io/vue-admin-perfect/#/home ...

  7. vue3.0实战项目

    vue3.0+typescript+vite2实战:后台管理系统 一.技术栈 二.功能架构 三.框架搭建 四.安装插件 1.路由插件vue-router4安装使用 2.vuex4的安装 3.安装sas ...

  8. vue3.0 引入Ueditor(百度编辑器)

    原本的集团里的老系统要重构,打算使用vue3.0开发(内心慌的一批),因为是一个涉及七八百个页面的业务系统,担心会翻车.先剥离里面的功能单独调研开发.以后有其他功能迭代也会陆续更新. Ueditor的 ...

  9. 第一章:Vue3.0+Openlayers+Cesium创建二三维联动项目

    Vue3.0+Openlayers+Cesium创建二三维联动项目 简介 Vue项目创建 安装依赖 框架结构 地图加载 显示效果 结语 简介 大家好!从今天开始,我将分享我在GIS开发的过程中如何利用 ...

最新文章

  1. maven 的依赖传递
  2. mybatis动态sql中where标签的使用
  3. PHP: to use scptask, you need to install the SSH extension.
  4. 开发文档模板_需求文档模板一堆什么样的适合你呢?
  5. mysql 参数调整_mysql需要调整的参数-阿里云开发者社区
  6. python3 namedtuple_去年发布的Python 3.8 稳定版,帮你们来一波特性全面解读
  7. KVM的概念和云计算
  8. 快速使用js验证输入的数字类型
  9. dbstart $oracle_home,使用dbstart和dbshut命令启动和关闭数据库
  10. 把自己的电脑作为网络代理服务器
  11. team viewer如何解绑设备
  12. 集合之比较接口器+Map家族的HashMap+LinkedHashMap+Hashtable+ConcurrentHashMap
  13. css background-image 属性
  14. 90句美丽的英文及翻译~~
  15. 使用leaflet仿原神提瓦特大地图制作日记
  16. 【ffmpeg】音频重采样
  17. C语言初阶:字符串,字符串搭配函数与指针的多重奥秘
  18. 什么是Java语言?java语言简介
  19. SSL-TLS 双向认证(一) -- SSL-TLS工作原理
  20. Java操作MongoDB

热门文章

  1. 点对点网络与广播式网络的区别
  2. 计算机教师面试题模板,初中信息技术|《了解Word界面》教师资格证面试真题模板...
  3. 计算机科学导论第二版(佛罗赞)-读书笔记
  4. 如何在ubuntu14.04上安装轻量级的Budgie桌面(v8)
  5. 音频变速变调原理及 soundtouch 代码分析
  6. 简单20行python代码_用 20 行 python 代码实现人脸识别!Python实现就是这么简单!...
  7. 5G注册流程分级详解(鉴权)Step9
  8. tp剩余未验证内容-4
  9. 火绒安全软件-免费且最干净的杀毒软件
  10. WindowBlinds破解全书