1. 封装加载地图资源组件
loadMap 方法用于初始化地图, 配置地图图层, 再调用添加数据源和图层的方法
addSource 方法用于添加数据源
addLayer 方法用于添加图层
addMarker 用于将echart 和柱状图定位到地图
import jingzhou_county from './geojson-county.json'
import jingzhou_city from './geojson-city.json'
declare const mapboxgl: any;
const useMap = () => {
let _mapbox: any;
// 资源集合
const allsources: string[] = [];
// 图层集合
const allLayers: string[] = [];
// 打点集合
const allMarkers: string[] = [];
// 弹窗集合
const allPopup: string[] = [];
/**
* 加载地图要素
* @param {Function} callback 回调函数
*/
const loadMap = (map, callback: Function = () => { }) => {
_mapbox = map._mapboxLayer._mapbox
const sources = { jingzhou_county, jingzhou_city }
// 1.加载地图资源
// 1.1遍历资源
for (const key in sources) {
// 1.2加载地图资源
addSource(key, sources[key]);
}
// 图层配置
const LayerData = [
// 区县填充
{
id: 'jingzhou_county_fill',
type: 'fill',
source: 'jingzhou_county',
layout: {
visibility: 'visible',
},
paint: {
'fill-color': '#165DFF', //#EEBF15
'fill-opacity': 0.2,
},
},
// 区县轮廓
{
id: 'jingzhou_county',
type: 'line',
source: 'jingzhou_county',
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#fff',
'line-width': ['interpolate', ['linear'], ['zoom'], 1, 1, 22, 5],
'line-opacity': 0.8,
'line-dasharray': [2, 4]
},
},
// 高亮区县轮廓
{
id: 'jingzhou_county_highLight',
type: 'line',
source: 'jingzhou_county',
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#0d99ff',
'line-width': ['interpolate', ['linear'], ['zoom'], 1, 1, 22, 5],
'line-opacity': 1,
},
filter: ['in', 'name', ''],
},
// 市区轮廓
{
id: 'jingzhou_city',
type: 'line',
source: 'jingzhou_city',
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#fff',
'line-width': ['interpolate', ['linear'], ['zoom'], 1, 1, 22, 5],
'line-opacity': 0.8,
},
},
// 市区轮廓偏移
{
id: 'jingzhou_city_offset',
type: 'line',
source: 'jingzhou_city',
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#165DFF',
'line-width': ['interpolate', ['linear'], ['zoom'], 1, 1, 22, 20],
'line-opacity': 1,
'line-offset': ['interpolate', ['linear'], ['zoom'], 1, -0.5, 22, -10],
},
},
];
// 2.遍历图层配置添加图层
LayerData.forEach((item) => {
addLayer(item.id, item, callback);
});
};
/**
* 加载资源
* @param {any} featuresData feature数据
* @param {string} sourceName 资源名称
*/
const addSource = (sourceName: string, featuresData: any) => {
// 1. 记录资源添加
if (!allsources.includes(sourceName)) {
allsources.push(sourceName);
}
// 2. 判断_mapbox是否已有资源
if (!_mapbox.getSource(sourceName)) {
_mapbox.addSource(sourceName, {
type: 'geojson',
data: featuresData,
});
} else {
// 3. 替换资源
(_mapbox.getSource(sourceName) as any).setData(featuresData);
}
};
/**
* 加载图层
* @param {string} layerName 图层名称
* @param {mapboxgl.AnyLayer} layerData 图层参数
* @param {Function} callback 回调函数
*/
const addLayer = (layerName: string, layerData: any, callback: Function) => {
// 判断是否有数据源
if (!_mapbox.getSource(layerData.source)) {
return;
}
// 0. 记录图层添加
if (!allLayers.includes(layerName)) {
allLayers.push(layerName);
}
// 1.判断是否已有图层,没有才添加
if (!_mapbox.getLayer(layerName)) {
// 2.添加图层
_mapbox.addLayer(layerData);
if (layerName.includes('_fill')) {
const filterLayerName = layerName.replace('_fill', '_highLight')
// 3.添加鼠标移入事件
_mapbox.on('mouseenter', layerName, (e) => {
_mapbox.getCanvas().style.cursor = 'pointer';
if (e.features && e.features[0]) {
// 获取区县名称
const name = e.features[0].properties.name;
_mapbox.setFilter(filterLayerName, ['in', 'name', name]);
}
});
// 4.添加鼠标移除事件
_mapbox.on('mouseleave', layerName, () => {
_mapbox.getCanvas().style.cursor = '';
_mapbox.setFilter(filterLayerName, ['in', 'name', '']);
});
}
}
};
/**
* 加载marker点
* @param {any} el
* @param {number[]} coordinate 坐标
*/
const addMarker = (el, coordinate: number[]) => {
// 实例化marker, marker打环形图跟柱状图会偏
const marker = new mapboxgl.Marker({
anchor: 'center',
element: el,
scale: 1
}).setLngLat(coordinate).addTo(_mapbox);
if (!allMarkers.includes(marker)) {
allMarkers.push(marker);
}
};
/**
* 弹窗popup
* @param {any} el
* @param {number[]} coordinate 坐标
*/
const addPopup = (el, coordinate: number[]) => {
const popup = new mapboxgl.Popup({
anchor: 'center',
className: 'popup-container',
closeButton: false,
closeOnClick: false,
}).setLngLat(coordinate)
.setDOMContent(el)
.addTo(_mapbox);
if (!allPopup.includes(popup)) {
allPopup.push(popup);
}
};
/**
* 设置图层显示隐藏
* @param {type} 参数
* @returns {type} 返回值
*/
const setLayerVisible = (show: boolean) => {
//_mapbox && _mapbox.setPitch(show ? 45 : 0) // 设置地图倾角
allLayers.forEach((layer: any) => {
_mapbox.setLayoutProperty(layer, 'visibility', show ? 'visible' : 'none');
})
allPopup.forEach((popup: any) => {
show ? popup._setOpacity(1) : popup._setOpacity(0)
})
}
return { loadMap, addMarker, addPopup }
}
export default useMap
2. 封装echart加载方法
initCharts 方法用于初始化echarts容器
addCharts 方法用于添加配置生成echarts
import { ref, markRaw } from 'vue';
import * as echarts from 'echarts';
import county from './geojson-county.json'
const useEcharts = () => {
// echart 实例
const myChart = ref<any>(null);
/**
* 初始化echarts
*/
const initCharts = (el) => {
// 移除之前创建的实例并且重新创建一个Echarts实例达到刷新效果
el.removeAttribute('_echarts_instance_');
myChart.value = markRaw(echarts.init(el));
};
/**
* 添加charts
*/
const addCharts = (el, option) => {
initCharts(el)
myChart.value.setOption(option);
};
return {
myChart,
initCharts,
addCharts,
}
}
export default useEcharts
3. 调用地图和echarts 的vue组件
加载地图比onMounted还慢, 用了setInterval来监听地图加载完
环形图用的 echarts 来生成
柱状图直接用html + css 来生成比较简单
<template>
<div class="map-echarts">
<template v-for="item in jingzhou_county.features" :key="item">
<div v-if="level === 'fibrilUsageRate'" :style="{ transform: scaleValue }"
:class="[item.properties.className, 'echarts-circle']"></div>
<div v-else-if="level !== '网架概览'" v-show="showColumn" :style="{ transform: scaleValue }"
:class="[item.properties.className, 'echarts-column']">
<div class="column-box"
:style="{ 'height': (Number((item.properties as any)[level]) * percent[level] + 5) + 'px' }">
</div>
<div class="column-text">{{ item.properties.name.slice(0, 2) + '供电所' }}</div>
<div class="column-num">{{ (item.properties as any)[level] }}</div>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, watch, nextTick } from 'vue';
import useEcharts from './useEcharts'
import useMap from './useMap'
import jingzhou_county from './geojson-county.json'
import { mapListStore } from '@/store/mapList'
import 'echarts-liquidfill'
const mapList = mapListStore()
const { loadMap, addMarker, setLayerVisible } = useMap()
const { addCharts } = useEcharts()
const props = defineProps<{ level: string, tableData: any }>()
// 显示柱状图
const showColumn = ref(false)
// 缩放比例
const scaleValue = ref('scale(0.8)')
// 柱状图比例
const percent = { branchLength: 0.1, poleTotal: 0.05, fiberSwitchTotal: 1, }
watch(() => props.level, (newVal) => {
if (newVal === '' || newVal === '网架概览') {
setLayerVisible(false)
return
}
nextTick(() => {
showColumn.value ? setLayerVisible(true) : initMap()
// 加载marker
updateMarkers()
})
}, { immediate: true })
watch(() => props.tableData, (newVal) => {
jingzhou_county.features.forEach((item: any) => {
const properties = item.properties
let obj = newVal.find(v => v.name.includes(properties.name.slice(0, 2))) || {}
item.properties = {
...properties,
branchLength: obj.branchLength || 0, // 线路总长度
fiberSwitchTotal: obj.fiberSwitchTotal || 0, // 接头盒总数
poleTotal: obj.poleTotal || 0,// 杆塔总数
fibrilUsageRate: obj.fibrilUsageRate || 0,// 纤芯使用率
}
})
})
/**
* 初始化地图
* @param {type} 参数
* @returns {type} 返回值
*/
const initMap = () => {
const map = mapList.allMapData['decisionAnalysis']
loadMap(map)
// 地图加载完, 柱状图才显示
showColumn.value = true
// 监测地图缩放移动
map.on('zoom-complete', () => {
if (props.level === '网架概览') return
const zoom = map.getZoom()
scaleValue.value = `scale(${1 + (zoom - 10) * 0.2})`
updateMarkers()
}, 'decisionAnalysis')
}
/**
* 地图缩放更新marker
* @param {type} 参数
* @returns {type} 返回值
*/
const updateMarkers = () => {
// 遍历区县
jingzhou_county.features.forEach(item => {
const properties: any = item.properties
let el = document.querySelector('.' + properties.className)
if (props.level === 'fibrilUsageRate') {
// 添加环形图echarts
handleAddCharts(el, parseFloat(properties.fibrilUsageRate) / 100)
}
addMarker(el, properties.centroid)
})
}
/**
* 添加环形图
* @param {type} 参数
* @returns {type} 返回值
*/
const handleAddCharts = (el, percent) => {
const option = {
series: [{
type: 'liquidFill',
radius: '90%',
data: [percent],
label: {
normal: {
color: '#1D2129',
insideColor: '#fff',
textStyle: {
fontSize: 16,
}
}
},
color: [{
type: 'linear',
x: 0,
y: 1,
x2: 0,
y2: 0,
colorStops: [{
offset: 1,
color: ['#165DFF'], // 0% 处的颜色
}, {
offset: 0,
color: ['#9FF9FF'], // 100% 处的颜色
}],
global: false // 缺省为 false
}],
outline: {
show: true,
borderDistance: 2,
itemStyle: {
borderColor: '#165DFF',
borderWidth: 2
}
}
}]
};
addCharts(el, option)
}
</script>
<style lang="scss" scoped>
.echarts-circle {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #fff;
}
.echarts-column {
@include flex(3);
flex-direction: column;
.column-box {
width: 8px;
height: 50px;
background: linear-gradient(180deg, #165DFF 0%, #9FF9FF 100%);
box-shadow: inset 0px 4px 4px rgba(0, 0, 0, 0.25);
border-radius: 2px;
}
.column-text {
font-size: 14px;
color: #4E5969;
}
.column-num {
font-size: 16px;
color: #1D2129;
}
}
</style>
<style lang="scss">
.popup-container {
.mapboxgl-popup-content {
box-shadow: unset !important;
background: transparent;
}
}
</style>