leaflet使用,图上标绘

<template>
  <div class="leaflet-map">
    <div ref="mapContainer" class="map-container"></div>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, nextTick } from 'vue'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import 'leaflet-minimap/dist/Control.MiniMap.min.css'
import MiniMap from 'leaflet-minimap'
import domDrag from '@/utils/domDrag'
import { ElMessage } from 'element-plus'

// 定义 props
const props = defineProps({
  center: {
    type: Array,
    validator: value => value.length === 2 && typeof value[0] === 'number' && typeof value[1] === 'number',
    default: [39.9042, 116.4]
  },
  zoom: {
    type: Number,
    default: 14
  },
  tileLayerUrl: {
    type: String,
    default: ''
  },
  tileLayerAttribution: {
    type: String,
    default: ''
  },
})

// 引用地图容器
const mapContainer = ref(null)

//底图数据
let basemapLayer0 = L.tileLayer(
  'http://t1.tianditu.com/vec_c/wmts?layer=vec&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=8899fd3e86aa994f71465b1c56a98727',
  {
    maxZoom: 17,
    minZoom: 2,
    tileSize: 256,
    zoomOffset: 1
  }
)
let basemapLayer1 = L.tileLayer(
  'http://t1.tianditu.com/cva_c/wmts?layer=cva&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=8899fd3e86aa994f71465b1c56a98727',
  {
    maxZoom: 17,
    minZoom: 2,
    tileSize: 256,
    zoomOffset: 1
  }
)
let basemapLayer2 = L.tileLayer(
  'http://t1.tianditu.com/img_c/wmts?layer=img&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=8899fd3e86aa994f71465b1c56a98727',
  {
    maxZoom: 17,
    minZoom: 2,
    tileSize: 256,
    zoomOffset: 1
  }
)
let basemapLayer3 = L.tileLayer(
  'http://t1.tianditu.com/cia_c/wmts?layer=cia&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=174705aebfe31b79b3587279e211cb9a',
  {
    maxZoom: 17,
    minZoom: 2,
    tileSize: 256,
    zoomOffset: 1
  }
)
let basemap0, basemap1, basemap2
// 定义地图和瓦片图层变量
let map = null
let miniMap = null
let tileLayer = null
let scale = null

// 初始化地图
onMounted(() => {
  map = L.map(mapContainer.value, {
    zoomControl: false, //去掉左上角缩放图标
    attributionControl: false, //去掉右下角的Logo
    doubleClickZoom: false, //禁止双击放大
    crs: L.CRS.EPSG4326 //设置坐标系
    // layers: [basemap1]
  }).setView(props.center, props.zoom)

  basemap0 = L.layerGroup([basemapLayer0, basemapLayer1])
  basemap1 = L.layerGroup([basemapLayer2, basemapLayer3])
  basemap2 = L.layerGroup([basemapLayer2])
  setTileLayer('3')
  setMiniMapDisplay(true)
  setScale(true)
})

//设置底图
function setTileLayer(val) {
  removeTileLayer()
  switch (val) {
    case '0': //天地图影像
      tileLayer = basemap1
      break
    case '1': //矢量底图
      break
    case '2': //天地图矢量
      tileLayer = basemap0
      break
    case '3': //电子地图
      tileLayer = basemap2
      break
    default:
      break
  }
  tileLayer.addTo(map)
}
//移除底图
function removeTileLayer() {
  if (tileLayer) {
    tileLayer.remove()
    tileLayer = null
  }
}

//添加鹰眼图
function addMiniMap() {
  let base = L.tileLayer(
    'http://t1.tianditu.com/vec_c/wmts?layer=vec&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=8899fd3e86aa994f71465b1c56a98727',
    {
      maxZoom: 17,
      minZoom: 2,
      tileSize: 256,
      zoomOffset: 1
    }
  )
  miniMap = new MiniMap(base, {
    toggleDisplay: true, // 显示切换按钮
    position: 'topright', // 鹰眼图位置
    crs: L.CRS.EPSG4326 // 坐标系
  }).addTo(map)
}
let isShowMiniMap = ref(true) // 是否显示鹰眼图
//设置鹰眼图显示隐藏
function setMiniMapDisplay(bool) {
  isShowMiniMap.value = bool !== undefined ? !!bool : !isShowMiniMap.value
  if (isShowMiniMap.value) {
    if (miniMap == null) {
      addMiniMap()
    }
  } else {
    if (miniMap) {
      miniMap.remove()
      miniMap = null
    }
  }
}

// 监听 center 和 zoom 的变化
watch(
  () => [props.center, props.zoom],
  ([newCenter, newZoom]) => {
    if (map) {
      map.setView(newCenter, newZoom)
    }
  }
)
let isShowScale = ref(true) // 是否显示比例尺
//设置比例尺显隐
function setScale(bool) {
  isShowScale.value = bool !== undefined ? !!bool : !isShowScale.value
  if (isShowScale.value) {
    if (scale == null) {
      // 需显示,且尚未创建比例尺,则添加
      scale = L.control.scale({ imperial: false })
      scale.addTo(map)
    }
  } else {
    // 不显示,则移除
    if (scale) {
      map.removeControl(scale)
      scale = null
    }
  }
}


/**
 * 画点
 * @param {Array} pos 点的坐标数组
 * @return {Object} 绘制的点对象
 * @example drawPoint([39.9042, 116.40])
 */
function drawPoint(pos) {
  let point = L.marker(pos, {})
  point.addTo(map)
  return point
}
/**
 * 画线
 * @param {Array} points 线的坐标点数组
 * @return {Object} 绘制的线对象
 * @example drawLine([
 *   [39.9042, 116.40],
 *   [38.9042, 115.40],
 *   [31.9042, 114.40]
 * ])
 */
function drawLine(points) {
  let polyline = L.polyline(points, {
    color: 'blue'
  }).addTo(map)
  return polyline
}
/**
 * 画面
 * @param {Array} points 多边形的坐标点数组
 * @return {Object} 绘制的多边形对象
 * @example drawPolygon([
 *   [39.9042, 116.40],
 *   [35.9042, 115.40],
 *   [37.9042, 115.40]
 * ])
 */
function drawPolygon(points) {
  let polygon = L.polygon(points, {
    color: 'green',
    fillColor: 'red',
    fillOpacity: 0.5
  }).addTo(map)
  return polygon
}

/**
 * 画矩形
 * @param {Array} points 矩形的坐标点数组
 * @return {Object} 绘制的矩形对象
 * @example drawRect([
 *  [39.9042, 116.40],
 *  [37.5042, 115.40]
 *  ])
 */
function drawRect(points) {
  let rect = L.rectangle(points, {
    color: '#ff7800',
    weight: 1
  }).addTo(map)
  return rect
}

/**
 * @param {Object} vector 要移除的矢量图层对象
 * @example removeVector(vector)
 */
function removeVector(vector) {
  try {
    vector.remove()
  } catch (e) {
    console.error(e)
  }
}

/**
 * @return {Object} 包含中心点和层级等的对象
 */
function getCenterAndZoom() {
  return {
    center: map.getCenter(),
    zoom: map.getZoom()
  }
}

//设置地图中心和层级
function setCenterAndZoom(center, zoom) {
  map.setView(center, zoom)
}

//地图容器大小改变时调用,重新调整地图
function resizeMap() {
  setTimeout(() => {
    map.invalidateSize(true)
  }, 400)
}

/**测试 */
// setTimeout(() => {
//   let point = drawPoint([39.9042, 116.40])
//   setTimeout(() => {
//     removeVector(point)
//     console.log(`output->`,getCenterAndZoom())
//   }, 5000)
// }, 1000)

/**
 * 图上标绘
 * @param type 标绘类型,point/line/polygon/rect/circle
 * @param callback 回调函数,参数为标绘点位数组
 */
function handDrawn(type, callback) {
  if (typeof callback !== 'function') {
    console.log('callback is not a function')
  }
  clearHandDraw()
  switch (type) {
    case 'point':
      creatPoint(callback)
      break
    case 'line':
      creatLine(callback)
      break
    case 'polygon':
      creatPolygon(callback)
      break
    case 'rect':
      creatRectangle(callback)
      break
    case 'circle':
      creatCircle(callback)
      break
    default:
      break
  }
}
//存储图上标绘的图层
let drawVectorList = []
/**
 * 图上画点
 * @param cb 回调函数,参数为点坐标
 */
function creatPoint(cb) {
  let point = []
  map.once('click', e => {
    let vec = L.marker([e.latlng.lat, e.latlng.lng], {}).addTo(map)
    point = [e.latlng.lat, e.latlng.lng]
    drawVectorList.push(vec)
    if (typeof cb === 'function') {
      cb(point)
    }
  })
}

/**
 * 图上画线
 * @param cb 回调函数,参数为线坐标点数组
 */
function creatLine(cb) {
  ElMessage({
    message: '正在绘制线,点击鼠标右键结束绘制',
    type: 'warning'
  })
  let points = []
  let lines = new L.polyline(points)
  map.on('click', onClick)
  map.on('contextmenu', onContextClick)
  function onClick(e) {
    points.push([e.latlng.lat, e.latlng.lng])
    lines.addLatLng(e.latlng)
    map.addLayer(lines)
    let circle = L.circle(e.latlng, { color: '#ff0000', fillColor: 'ff0000', fillOpacity: 1 }).addTo(map)
    map.addLayer(circle)
    drawVectorList.push(lines)
    drawVectorList.push(circle)
  }
  function onContextClick(e) {
    let vec = L.polyline(points).addTo(map)
    lines = new L.polyline(points)
    map.off('click')
    map.off('contextmenu')
    drawVectorList.push(vec)
    if (typeof cb === 'function') {
      cb(points)
    }
  }
}

/**
 * 图上画面
 * @param cb 回调函数,参数为面坐标点数组
 */
function creatPolygon(cb) {
  ElMessage({
    message: '正在绘制面,点击鼠标右键结束绘制',
    type: 'warning'
  })
  let points = []
  let lines = new L.polyline([])
  map.on('click', onClick)
  map.on('contextmenu', onContextClick)
  function onClick(e) {
    points.push([e.latlng.lat, e.latlng.lng])
    lines.addLatLng(e.latlng)
    map.addLayer(lines)
    let circle = L.circle(e.latlng, { color: '#ff0000', fillColor: 'ff0000', fillOpacity: 1 }).addTo(map)
    map.addLayer(circle)
    drawVectorList.push(lines)
    drawVectorList.push(circle)
  }
  function onContextClick(e) {
    let vec = L.polygon([points]).addTo(map)
    lines = new L.polyline([])
    drawVectorList.push(vec)
    map.off('click')
    map.off('contextmenu')
    if (typeof cb === 'function') {
      cb(points)
    }
  }
}

/**
 * 图上画矩形
 * @param cb 回调函数,参数为矩形坐标点数组
 */
function creatRectangle(cb) {
  let tmplist = []
  let vec = null
  map.on('click', e => {
    tmplist.push([e.latlng.lat, e.latlng.lng])
    map.on('mousemove', ev => {
      if (tmplist.length > 1) {
        tmplist.splice(1)
      }
      tmplist.push([ev.latlng.lat, ev.latlng.lng])
      vec && vec.remove()
      vec = L.rectangle(tmplist, {
        color: '#ff7800',
        weight: 1
      }).addTo(map)
    })
    if (tmplist.length > 1) {
      map.off('mousemove')
      map.off('click')
      tmplist.pop()
      drawVectorList.push(vec)
      if (typeof cb === 'function') {
        cb(tmplist)
      }
    }
  })
}

/**
 * 图上画圆
 * @param cb 回调函数,参数为圆坐标点数组
 */
function creatCircle(cb) {
  let r, center, tempCircle
  tempCircle = new L.circle()
  map.dragging.disable()
  map.on('click', onMouseClick)
  function onMouseClick(e) {
    center = e.latlng
    map.on('mousemove', onMove)
    map.once('click', onMouseUp)
  }
  function onMove(e) {
    r = L.latLng(e.latlng).distanceTo(center)
    if (center) {
      tempCircle.setLatLng(center)
      tempCircle.setRadius(r)
      tempCircle.setStyle({
        color: '#ff7800',
        weight: 1,
        fillOpacity: 0.5
      })
      map.addLayer(tempCircle)
    }
  }
  function onMouseUp(e) {
    r = L.latLng(e.latlng).distanceTo(center)
    L.circle(center, { radius: r, color: '#ff7800', fillopacity: 0.5, weight: 1 })
    map.addLayer(tempCircle)
    map.dragging.enable()
    //动画滑动居中圆
    // map.flyToBounds(tempCircle.getBounds())
    map.off('click')
    map.off('mousemove')
    drawVectorList.push(tempCircle)
    if (typeof cb === 'function') {
      cb({ r, center: [center.lat, center.lng] })
    }
  }
}
//清除标绘图层事件
function clearHandDraw() {
  map.off('mousemove')
  map.off('click')
  map.off('contextmenu')
  drawVectorList.forEach(vec => {
    vec.remove()
  })
  drawVectorList = []
}

/**
 * @param url wms瓦片图层地址
 * @param options 瓦片图层选项
 * @returns {Object} 瓦片图层对象
 */
function loadTileLayer(url, options = {}) {
  let tileLayer = L.tileLayer.wms(url, { crs: L.CRS.EPSG4326, format: 'image/png', transparent: true, ...options })
  tileLayer.addTo(map)
  // map.flyToBounds(tileLayer.getBounds())
  return tileLayer
}
/**
 * @param url GeoJson图层地址
 * @param options 图层选项
 * @returns {Promise} Promise
 */
function loadGeoJsonLayer(url, options = {}) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        let tileLayer = L.geoJSON(data, {
          style: function (feature) {
            return { color: '#ff7800', weight: 1 } // 自定义样式
          },
          ...options
        }).addTo(map)
        resolve(tileLayer)
      })
      .catch(error => console.error('Error loading GeoJSON:', error))
  })
}

defineExpose({
  loadTileLayer,
  loadGeoJsonLayer,
  setScale,
  setMiniMapDisplay,
  setTileLayer,
  drawPoint,
  drawLine,
  drawPolygon,
  drawRect,
  removeVector,
  getCenterAndZoom,
  setCenterAndZoom,
  resizeMap,
  handDrawn,
  clearHandDraw
})
</script>

<style scoped>
.leaflet-map {
  height: 100%;
  width: 100%;
  position: relative;
  overflow: hidden;
}
.map-container {
  height: 100%;
  width: 100%;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值