<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>
leaflet使用,图上标绘
于 2025-03-10 16:40:17 首次发布