地图组件联动开发

地图组件联动开发

开发目标简介:

  • 1、点击按钮切换到场景2。
  • 2、在该场景的div中创建两个地图组件,分别用于展示原始地图和综合后地图数据。
  • 3、使两个地图组件互相影响,缩放或拖动其中一个子组件中的地图,另外一个也跟着变化

创建地图组件

开发目标:

  • 实现瓦片地图加载啊
  • 地图缩放控件
  • 地图图层切换控件

使用leaflet.chinatmsproviders新建多种图层

基本语法:L.tileLayer.chinaProvider('Geoq.Normal.PurplishBlue',{})

该方法的第一个参数定义了图层类型

首先使用该方法返回并接受图层

接着定义baseLayer对象,其中保存了图层名及其值也就是上述函数返回的图层

然后这是地图初始化需要的值mapValue,是一个对象,其中包括坐标系、中心点经纬度、缩放等级、图层类型、是否添加缩放空间等属性。

定义初始化函数,在挂载时调用。添加控件

1
2
3
4
5
6
7
8
9
10
11
12
13
// map是在初始化函数外定义的响应式数据
map.value = L.map('mapid', mapValue) //第一个参数是放置地图的容器的id名,第二个参数是上述的初始化值
L.control.layers(baseLayers, null).addTo(map.value)

// L.control.layers(baseLayers, null).addTo(map.value) 这行代码做了以下几件事:

// L.control.layers(baseLayers, null) 创建了一个新的图层控制器。baseLayers 是一个对象,
// 它的每个键值对定义了一个基础图层。在这个例子中,null 表示没有叠加层。

// .addTo(map.value) 将这个图层控制器添加到了地图上。map.value 应该是一个 L.Map 实例。

//返回L.Map构造的实例
return map.value

完整的代码,此时还未实现动图组件互动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<template>
<div id="mapid" style="height: 100%"></div>
</template>

<script setup lang="ts">
import {ref, inject, onUnmounted, watch, nextTick, onMounted} from 'vue'
import L from 'leaflet'
let pscene: any = inject('pscene')
import 'leaflet.chinatmsproviders'

let map = ref<any>(null);
const normalm3 = L.tileLayer.chinaProvider(
'Geoq.Normal.PurplishBlue',
{}
)
// Google map layers
const normalMap = L.tileLayer.chinaProvider('Google.Normal.Map', {})
const satelliteMap = L.tileLayer.chinaProvider(
'Google.Satellite.Map',
{}
)
// 高德地图
const Gaode = L.tileLayer.chinaProvider('GaoDe.Normal.Map', {})
const Gaodimage = L.tileLayer.chinaProvider('GaoDe.Satellite.Map', { })


// Create base layers with different tile layers
const baseLayers = {
智图午夜蓝: normalm3,
谷歌地图: normalMap,
谷歌影像: satelliteMap,
高德地图: Gaode,
高德影像: Gaodimage,
}
//初始化地图
const mapValue = {
crs: L.CRS.EPSG3857,
center: [32.0603, 118.7969],
zoom: 12,
maxZoom: 18,
minZoom: 5,
layers: [normalMap],
zoomControl: true
}
const initMap = () => {
map.value = L.map('mapid', mapValue)

// Add control layers and zoom control to the map

L.control.layers(baseLayers, null).addTo(map.value)
return map.value
}

onMounted(() => {
const mapInstance = initMap()
//等初始化完成后,强制地图重新计算大小和位置
nextTick(() => {
// 强制地图重新计算大小和位置
if (mapInstance){
mapInstance.invalidateSize();
}
});
mapInstance.on('zoomend dragend', () => {
emit('map-moved', { center: mapInstance.getCenter(), zoom: mapInstance.getZoom() })
})

})

onUnmounted(() => {
if (map.value) {
map.value.remove();
map.value = null;
}
})

</script>

<style scoped>
#mapid {
height: 100%;
}
</style>

在父组件中导入组件

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div class="mainBox" >
<el-button size="small" style="margin:0px; border-color: aliceblue;" icon="CloseBold" @click="changeScene"></el-button>
<div class="mapOuterBox" >
<!-- 左边的地图组件 -->
<mapBox ref="mapBox1" class="leftMap" @map-moved="handelMapMoved2"></mapBox>
<!-- 中间的地图组件 -->
<mapBoxMid ref="mapBox2" class="midMap" @map-moved="handelMapMoved1"></mapBoxMid>
<div class="rightMap">文字展示</div>
</div>
</div>
</template>

互动效果实现

使用自定义事件emit,当地图发生缩放或者拖拽时,在附件中监听到该自定义事件。在父组件通知另外一个地图组件进行中心点和缩放等级的变化。

因此步骤如下

  • 在左中两个地图组件中,监听地图实例的缩放和拖拽事件
  • 监听到拖拽事件触发自定义事件 map-moved
  • 在父组件中分别获取到两个地图组件的实例,使用ref
  • 在父组件中的监听到map-move的后,分别调用handelMapmove2和handelMapmove1
  • 在父组件中定义handelMapmove2和handelMapmove1函数,前者用于调整中间地图组件的中心和缩放等级,后者用于调整左边地图组件。
  • 分别在两个地图组件中定义setMapView函数,并暴露出来,这样父组件中才能使用$refs调用该方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//地图组件中代码
//自定义事件
const setMapView = (center: L.LatLngExpression, zoom: number) => {
if (map.value) {
map.value.setView(center, zoom)
}
}
//暴露给父组件的方法
defineExpose({ setMapView })
//注意vue3的写法
const emit = defineEmits(['map-moved'])
onMounted(() => {
const mapInstance = initMap()
//等初始化完成后,强制地图重新计算大小和位置
nextTick(() => {
// 强制地图重新计算大小和位置
if (mapInstance){
mapInstance.invalidateSize();
}
});
mapInstance.on('zoomend dragend', () => {
emit('map-moved', { center: mapInstance.getCenter(), zoom: mapInstance.getZoom() })
})
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//父组件中代码
const mapBox1 = ref(null)
const mapBox2 = ref(null)

//重置第二个子组件的center和zoom
const handelMapMoved2 = (e:any)=>{
if(mapBox2.value){
console.log(mapBox2.value.setMapView(e.center, e.zoom))
}
}
const handelMapMoved1 = (e)=>{
if(mapBox1.value){
console.log(mapBox1.value.setMapView(e.center, e.zoom))
}

}

踩到的坑们

setup

1、在子组件的setup中定义的方法,在父组件中通过子组件实例(在父组件中的子组件标签中使用ref=’ziname’)ziname.的方法无法调用,必须在setup中暴露出该方法才行;暴露的方法也踩了坑,由于使用ts,一直提示不能使用return暴露函数。应该使用defineExpose({ setMapView })

2、地图总是加载左上角,因为组件的大小不是固定的px值,所以会发生变化,因此,需要在初始化完成后强制地图进行大小和位置计算。且在组件卸载后要清空地图的配置值。

3、将地图配置对象mapValue设置成响应式对象,对导致第一个被初始化的地图能加载出地图,而另外一个不能。因此放弃了响应式配置值实现互动的方法。

4、vue3中不能使用this,自定义方法,组件实例调用方法,均和vue2有差异

1
2
3
4
5
6
7
const emit = defineEmits(['map-moved'])
mapInstance.on('zoomend dragend', () => {
emit('map-moved', { center: mapInstance.getCenter(), zoom: mapInstance.getZoom() })
})

const mapBox1 = ref(null)
mapBox1.value.setMapView(e.center, e.zoom)

总结

以为自己掌握了知识,实践中却有很多问题。学好底层逻辑有助于快速发现问题和解决问题