上一篇文章我们学习了如何使用leaflet实现地图功能,具体的详情可以参考juejin.cn/post/745187…
此篇文章主要介绍一下我们利用leaflet本身带的Marker、LayerGroup、Icon来构建一个在地图上展示的多组图层图标。后续也会介绍插件来生成性能更好的canvas图层。
首先我们先画一个没有标识的地图
<template>
<div class="portal">
<div class="map">
<div class="map-content" id="mapId"></div>
</div>
</div>
</template>
<script setup>
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
const { push, currentRoute, go, replace } = useRouter()
import { onBeforeMount, onMounted, reactive, ref, watch } from 'vue'
let map = null
onBeforeMount(() => {})
onMounted(() => {
initMap()
})
// 地图相关数据
const state = reactive({
vesselList: [],
})
const initMap = async () => {
const osm = L.tileLayer(
'http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
{
preferCanvas: true,
maxZoom: 18,
minZoom: 3,
name: 'osm',
subdomains: ['1', '2', '3', '4'],
}
)
map = L.map('mapId', {
preferCanvas: true,
attributionControl: false,
renderer: L.canvas(),
center: [34.22088, 121.14048],
zoom: 6,
layers: [osm],
zoomControl: false,
worldCopyJump: true,
zoomSnap: 1,
zoomDelta: 1,
wheelPxPerZoomLevel: 120,
crs: L.CRS.EPSG3857,
})
}
</script>
<style lang="scss" scoped>
.portal {
width: 100%;
height: calc(100vh - 91px);
box-sizing: border-box;
position: relative;
}
.map-content {
width: 100%;
height: 100%;
}
:deep(.matou) {
font-size: 16px;
color: #3475f2;
}
:deep(.triangle) {
font-size: 16px;
color: #ffa500;
}
:deep(.port-area-icon) {
padding-top: 3px;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border: 2px dashed transparent;
}
:deep(.select-div) {
padding-top: 3px;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border: 2px dashed #f42448;
z-index: 100;
}
:deep(.triangles) {
font-size: 16px;
color: #f42448;
z-index: 100;
}
:deep(.yellow) {
font-size: 16px;
color: #ffa500;
}
:deep(.origin) {
font-size: 16px;
color: #ffff00;
}
:deep(.red) {
font-size: 16px;
color: #ec0730;
}
:deep(.blue) {
font-size: 16px;
color: #019dd8;
}
:deep(.pttbtexts) {
font-size: 8px;
width: max-content;
// background-color: rgba(0, 0, 0, 0.3);
background-color: #fff;
text-align: center;
color: #000;
// border: 1px solid #979797;
padding: 0px 1px;
transform: translate(-10%, 25%);
}
</style>
生成的地图是这样的,然后我们在地图上添加标识点
通过接口获取船的经纬度,然后画到地图上
onBeforeMount(() => {
getShipListHandle()
})
onMounted(() => {
initMap()
})
const getShipListHandle = () => {
getShipListApi().then(res => {
if (res.code === 200) {
state.vesselList = res.data
renderShip()
}
})
}
// 地图相关数据
const state = reactive({
// 暂存港口marker集合,用作地图渲染
vesselList: [],
vesselGroup: null,
})
const initMap = async () => {
const osm = L.tileLayer(
'http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
{
preferCanvas: true,
maxZoom: 18,
minZoom: 3,
name: 'osm',
subdomains: ['1', '2', '3', '4'],
}
)
map = L.map('mapId', {
preferCanvas: true,
attributionControl: false,
renderer: L.canvas(),
center: [34.22088, 121.14048],
zoom: 6,
layers: [osm],
zoomControl: false,
worldCopyJump: true,
zoomSnap: 1,
zoomDelta: 1,
wheelPxPerZoomLevel: 120,
crs: L.CRS.EPSG3857,
})
state.vesselGroup = new L.layerGroup()
map.addLayer(state.vesselGroup)
}
const renderShip = () => {
if (state.vesselList.length === 0) {
return
}
const data = state.vesselList
for (let i = 0; i < data.length; i++) {
if (data[i].lon && data[i].lat) {
let Icon = L.divIcon({
//图标地址
className: '__openstreet__',
html: `<div class="port-area-icon">
<div style="transform: rotate(${data[i].hdg}deg)">
<i class="triangle iconfont icon-chuanbotuli"></i>
</div>
</div>
`,
})
let ic = L.marker([data[i].lat, data[i].lon], {
icon: Icon,
})
ic.addTo(state.vesselGroup)
}
}
}
这里渲染船列表的时候,我选择了自定义icon船图标,使用L.divIcon
来生成船的图标,这里生成的都是一个一个的dom点,然后通过ic.addTo(state.vesselGroup)
的方式一个一个添加到图层当中,我们使用vesselGroup
一个图层管理有一个好处就是可以方便后期我们整体的操作整个图层。
可以看到,船的图标是一个一个三角号,而且还根据hdg
字段指明了船的方向