基于“vue3+leaflet+dwg格式的CAD图片”实现图片预览及点标记功能

2,162 阅读4分钟

基本思路

1、要将dwg格式的CAD图片在项目中展示并且可放大缩小不失真。

2、要在图片上添加坐标点,需要一个像素坐标到经纬度坐标的转换,并且点位坐标要可点击弹框展示基本信息。

方案1(财大气粗的建议使用这个)

使用唯杰地图 vjmap.com/ 具体使用方法可查看官网应用案例及源码。

注意!注意!注意!:唯杰地图是收费的哦!

方案2(想白嫖看这个)

1、将dwg格式的CAD图片通过插件转换为svg格式的图片。

2、使用leaflet轻松实现以上需求。

上代码

A.Leaflet插件安装

官网:leafletjs.com/ 中文文档:geekdaxue.co/read/leafle…

上述地址是Leaflet的官方网站,如果想查看详细的资料,可以细品。

npm安装库

npm install leaflet --save

B. 在Vue项目中引入Leaflet

可以分为全局及局部两种方式引入,按需使用

import L from "leaflet";
import 'leaflet/dist/leaflet.css'

C. 创建地图容器

仅需要创建一个Div容器,确定唯一id。

<template>
    <!-- 地图容器 -->
    <div id="image-map"></div>
</template>

D. leaflet基本功能

<script setup>
import { ref, onMounted, reactive } from 'vue';
import L from "leaflet";
import 'leaflet/dist/leaflet.css';
import { blueIcon, goldIcon, redIcon, greenIcon, orangeIcon, yellowIcon, violetIcon, greyIcon, blackIcon } from '../icon/leaflet-color-markers'

const map = ref(null);
const markers = reactive([
  {name:'标记点1',type:'1',content:'这是标记点1的信息',position:[-100,300]},
  {name:'标记点2',type:'1',content:'这是标记点2的信息',position:[-150,300]},
  {name:'标记点3',type:'2',content:'这是标记点3的信息',position:[-200,300]},
  {name:'标记点4',type:'2',content:'这是标记点4的信息',position:[-250,300]},
  {name:'标记点5',type:'3',content:'这是标记点5的信息',position:[-300,300]},
])
const markerEnable = ref([]);

function initmap() {
  map.value = L.map('image-map', {
    minZoom: 1,
    maxZoom: 4,
    center: [0, 0],
    zoom: 1,
    crs: L.CRS.Simple
  });
 
  const w = 4000
  const h = 3000
  const url = 'https://xxx.com/loto/default/3.svg';
  
  const southWest = map.value.unproject([0, h], map.value.getMaxZoom() - 1);
  const northEast = map.value.unproject([w, 0], map.value.getMaxZoom() - 1);
  const bounds = new L.LatLngBounds(southWest, northEast);
  
  L.imageOverlay(url, bounds).addTo(map.value);
  
  map.value.setMaxBounds(bounds);

  markerEnable.value = markers.map(element => {
    if(element.type === '1'){
      element.icon = redIcon
    }else{
      element.icon = greenIcon
    }
    return {
      ...element,
      markerItem:L.marker(element.position,{icon: element.icon}).addTo(map.value)
    }
  });
  markerEnable.value.forEach((item)=>{
    item.markerItem.bindPopup(item.content)

    item.markerItem.on('move', (e) => {
      // 当标记移动时,更新位置
      const newLocation = item.markerItem.getLatLng();
      console.log(`Marker moved to: ${newLocation.lat}, ${newLocation.lng}`);
    });

    function updateMarkerPosition(newPosition) {
      item.markerItem.setLatLng(newPosition);
      map.value.panTo(newPosition); // 平移地图到新位置
    }
  
    // 使标记可以被拖动
    item.markerItem.dragging.enable();
    item.markerItem.on('drag', (e) => {
      const newPosition = item.markerItem.getLatLng();
      updateMarkerPosition(newPosition);
    });
  })
}
onMounted(()=>{
  initmap()
})
</script>

工作原理

minZoom: 1,
maxZoom: 4,
center: [0, 0],
zoom: 1,

设置能够缩放的级别,1-4级。初始级别值是1。

crs: L.CRS.Simple

这表明leaflet使用1:1映射,在屏幕像素和经纬度坐标系统之间。换句话说,我们的图片是平的,不是全球的,我们在投影一张平面图片。

const w = 4000
const h = 3000
const url = 'https://xxx.com/loto/default/3.svg';

这一段定义了图片尺寸和它的路径,路径可以引用网络链接。

const southWest = map.value.unproject([0, h], map.value.getMaxZoom() - 1);
const northEast = map.value.unproject([w, 0], map.value.getMaxZoom() - 1);
const bounds = new L.LatLngBounds(southWest, northEast);

这一段相当于告诉我们,如何把图片通过地图的方式放出来,所以需要一个像素坐标到经纬度坐标(系统)的转换。 把西南,东北角的像素坐标逆映射为经纬度坐标网。

在像素中,leaflet默认左上角的为坐标原点(0,0) 。所以,左下角的点有一个h作为y坐标值和0的x坐标值,右上角的点有一个0作为y坐标值和w作为x坐标值。

通过这些,即可知该在地图的何处放置这张照片。

L.imageOverlay(url, bounds).addTo(map.value);

把图像作为一个overlay覆盖,同时规定了图片的total size 只是我们限定的像素大小,所以用户不能把图片拖拽出边界。

const markers = reactive([
  {name:'标记点1',type:'1',content:'这是标记点1的信息',position:[-100,300]},
  {name:'标记点2',type:'1',content:'这是标记点2的信息',position:[-150,300]},
  {name:'标记点3',type:'2',content:'这是标记点3的信息',position:[-200,300]},
  {name:'标记点4',type:'2',content:'这是标记点4的信息',position:[-250,300]},
  {name:'标记点5',type:'3',content:'这是标记点5的信息',position:[-300,300]},
])

这一段是mock的点位信息,name是点名称,type是类型根据类型可显示不同颜色的标记点,content是弹框内容,position是可移动的标记点

markerEnable.value = markers.map(element => {
    if(element.type === '1'){
      element.icon = redIcon
    }else{
      element.icon = greenIcon
    }
    return {
      ...element,
      markerItem:L.marker(element.position,{icon: element.icon}).addTo(map.value)
    }
  });

循环创建点位

markerEnable.value.forEach((item)=>{
    item.markerItem.bindPopup(item.content)

    item.markerItem.on('move', (e) => {
      // 当标记移动时,更新位置
      const newLocation = item.markerItem.getLatLng();
      console.log(`Marker moved to: ${newLocation.lat}, ${newLocation.lng}`);
    });

    function updateMarkerPosition(newPosition) {
      item.markerItem.setLatLng(newPosition);
      map.value.panTo(newPosition); // 平移地图到新位置
    }
  
    // 使标记可以被拖动
    item.markerItem.dragging.enable();
    item.markerItem.on('drag', (e) => {
      const newPosition = item.markerItem.getLatLng();
      updateMarkerPosition(newPosition);
    });
  })

给点位添加弹窗并且设置点位可拖动更新点位坐标功能