切换echarts 问题1 原先的打标点产生冲突 -- 解决中
parent
ca28c0851e
commit
b0c004eab8
|
@ -13,13 +13,17 @@
|
||||||
"@types/leaflet-draw": "^1.0.7",
|
"@types/leaflet-draw": "^1.0.7",
|
||||||
"@types/proj4leaflet": "^1.0.7",
|
"@types/proj4leaflet": "^1.0.7",
|
||||||
"@vueuse/core": "^10.2.1",
|
"@vueuse/core": "^10.2.1",
|
||||||
|
"echarts": "^5.4.3",
|
||||||
"geojson": "^0.5.0",
|
"geojson": "^0.5.0",
|
||||||
|
"heatmapjs": "^2.0.2",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet-draw": "^1.0.4",
|
"leaflet-draw": "^1.0.4",
|
||||||
|
"pinia": "^2.1.6",
|
||||||
"proj4": "^2.9.0",
|
"proj4": "^2.9.0",
|
||||||
"proj4leaflet": "^1.0.2",
|
"proj4leaflet": "^1.0.2",
|
||||||
"quasar": "^2.12.3",
|
"quasar": "^2.12.3",
|
||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4",
|
||||||
|
"vue-router": "4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@quasar/vite-plugin": "^1.4.1",
|
"@quasar/vite-plugin": "^1.4.1",
|
||||||
|
|
|
@ -17,15 +17,24 @@ dependencies:
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.2.1
|
specifier: ^10.2.1
|
||||||
version: 10.2.1(vue@3.3.4)
|
version: 10.2.1(vue@3.3.4)
|
||||||
|
echarts:
|
||||||
|
specifier: ^5.4.3
|
||||||
|
version: 5.4.3
|
||||||
geojson:
|
geojson:
|
||||||
specifier: ^0.5.0
|
specifier: ^0.5.0
|
||||||
version: 0.5.0
|
version: 0.5.0
|
||||||
|
heatmapjs:
|
||||||
|
specifier: ^2.0.2
|
||||||
|
version: 2.0.2
|
||||||
leaflet:
|
leaflet:
|
||||||
specifier: ^1.9.4
|
specifier: ^1.9.4
|
||||||
version: 1.9.4
|
version: 1.9.4
|
||||||
leaflet-draw:
|
leaflet-draw:
|
||||||
specifier: ^1.0.4
|
specifier: ^1.0.4
|
||||||
version: 1.0.4
|
version: 1.0.4
|
||||||
|
pinia:
|
||||||
|
specifier: ^2.1.6
|
||||||
|
version: 2.1.6(typescript@5.1.6)(vue@3.3.4)
|
||||||
proj4:
|
proj4:
|
||||||
specifier: ^2.9.0
|
specifier: ^2.9.0
|
||||||
version: 2.9.0
|
version: 2.9.0
|
||||||
|
@ -38,6 +47,9 @@ dependencies:
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.3.4
|
specifier: ^3.3.4
|
||||||
version: 3.3.4
|
version: 3.3.4
|
||||||
|
vue-router:
|
||||||
|
specifier: '4'
|
||||||
|
version: 4.2.4(vue@3.3.4)
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@quasar/vite-plugin':
|
'@quasar/vite-plugin':
|
||||||
|
@ -405,6 +417,10 @@ packages:
|
||||||
'@vue/compiler-dom': 3.3.4
|
'@vue/compiler-dom': 3.3.4
|
||||||
'@vue/shared': 3.3.4
|
'@vue/shared': 3.3.4
|
||||||
|
|
||||||
|
/@vue/devtools-api@6.5.0:
|
||||||
|
resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@vue/language-core@1.8.5(typescript@5.1.6):
|
/@vue/language-core@1.8.5(typescript@5.1.6):
|
||||||
resolution: {integrity: sha512-DKQNiNQzNV7nrkZQujvjfX73zqKdj2+KoM4YeKl+ft3f+crO3JB4ycPnmgaRMNX/ULJootdQPGHKFRl5cXxwaw==}
|
resolution: {integrity: sha512-DKQNiNQzNV7nrkZQujvjfX73zqKdj2+KoM4YeKl+ft3f+crO3JB4ycPnmgaRMNX/ULJootdQPGHKFRl5cXxwaw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -549,6 +565,13 @@ packages:
|
||||||
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/echarts@5.4.3:
|
||||||
|
resolution: {integrity: sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==}
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
zrender: 5.4.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/esbuild@0.18.13:
|
/esbuild@0.18.13:
|
||||||
resolution: {integrity: sha512-vhg/WR/Oiu4oUIkVhmfcc23G6/zWuEQKFS+yiosSHe4aN6+DQRXIfeloYGibIfVhkr4wyfuVsGNLr+sQU1rWWw==}
|
resolution: {integrity: sha512-vhg/WR/Oiu4oUIkVhmfcc23G6/zWuEQKFS+yiosSHe4aN6+DQRXIfeloYGibIfVhkr4wyfuVsGNLr+sQU1rWWw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -614,6 +637,10 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/heatmapjs@2.0.2:
|
||||||
|
resolution: {integrity: sha512-1pO/bbn9G1NYhndvjnzLVAQMGBOCk8abMM7QnLOPlIMoxDfG9Uylb68PlZe/0MCT9GuwGGtchXVPLWfoT6HrCw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-binary-path@2.1.0:
|
/is-binary-path@2.1.0:
|
||||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -692,6 +719,24 @@ packages:
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pinia@2.1.6(typescript@5.1.6)(vue@3.3.4):
|
||||||
|
resolution: {integrity: sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@vue/composition-api': ^1.4.0
|
||||||
|
typescript: '>=4.4.4'
|
||||||
|
vue: ^2.6.14 || ^3.3.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@vue/composition-api':
|
||||||
|
optional: true
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@vue/devtools-api': 6.5.0
|
||||||
|
typescript: 5.1.6
|
||||||
|
vue: 3.3.4
|
||||||
|
vue-demi: 0.14.5(vue@3.3.4)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/postcss@8.4.26:
|
/postcss@8.4.26:
|
||||||
resolution: {integrity: sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==}
|
resolution: {integrity: sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
@ -763,11 +808,14 @@ packages:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tslib@2.3.0:
|
||||||
|
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/typescript@5.1.6:
|
/typescript@5.1.6:
|
||||||
resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
|
resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
|
||||||
|
|
||||||
/vite@4.4.4(sass@1.32.12):
|
/vite@4.4.4(sass@1.32.12):
|
||||||
resolution: {integrity: sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==}
|
resolution: {integrity: sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==}
|
||||||
|
@ -820,6 +868,15 @@ packages:
|
||||||
vue: 3.3.4
|
vue: 3.3.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/vue-router@4.2.4(vue@3.3.4):
|
||||||
|
resolution: {integrity: sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.2.0
|
||||||
|
dependencies:
|
||||||
|
'@vue/devtools-api': 6.5.0
|
||||||
|
vue: 3.3.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vue-template-compiler@2.7.14:
|
/vue-template-compiler@2.7.14:
|
||||||
resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
|
resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -855,3 +912,9 @@ packages:
|
||||||
/yallist@4.0.0:
|
/yallist@4.0.0:
|
||||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/zrender@5.4.4:
|
||||||
|
resolution: {integrity: sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==}
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
dev: false
|
||||||
|
|
204
src/App.vue
204
src/App.vue
|
@ -1,129 +1,115 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeMount, ref, watch } from 'vue';
|
import { onBeforeMount, ref, watch } from 'vue';
|
||||||
import Map from './components/Map.vue'
|
import ThemeSwitch from './components/ThemeSwitch.vue';
|
||||||
import Mark from './components/mapChild/Mark.vue'
|
import MarkMenu from './components/left/MarkMenu.vue';
|
||||||
import useMarks, { markType } from './hook/useMarks.ts'
|
import CtrlMenu from './components/left/CtrlMenu.vue';
|
||||||
import ThemeSwitch from './components/ThemeSwitch.vue'
|
import { useWindowSize } from '@vueuse/core';
|
||||||
import MarkMenu from './components/left/MarkMenu.vue'
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import CtrlMenu from './components/left/CtrlMenu.vue'
|
import { useMarkStore } from './store/useMarkStore';
|
||||||
import { mdiLanConnect } from '@quasar/extras/mdi-v7'
|
|
||||||
import { useWindowSize } from '@vueuse/core';
|
|
||||||
import { Config } from './hook/useConfig';
|
|
||||||
|
|
||||||
|
const {marks} = useMarkStore();
|
||||||
|
const tab = ref('mark');
|
||||||
|
const sideStyle = ref('20em,logo');
|
||||||
|
const { width } = useWindowSize();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const nowRouter = ref(route.path);
|
||||||
|
|
||||||
const map = ref<InstanceType<typeof Map>>(); // 获取地图的实例 纂取实例方法
|
watch(nowRouter, (path) => router.push(path));
|
||||||
const marks = useMarks([{ latlng: [39.92647400, 116.40328300], title: '北京故宫', opacity: 1 }])
|
|
||||||
const tab = ref('mark')
|
|
||||||
const sideStyle = ref('20em,logo');
|
|
||||||
const { width } = useWindowSize()
|
|
||||||
const mapConfig = ref()
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
sideStyle.value = width.value > 600 ? '20em,logo' : '0em,bar'
|
sideStyle.value = width.value > 600 ? '20em,logo' : '0em,bar';
|
||||||
})
|
});
|
||||||
|
|
||||||
function changeSide(data: string) {
|
function changeSide(data: string) {
|
||||||
const _data = data.split(',')
|
const _data = data.split(',');
|
||||||
document.body.style.setProperty('--side', _data[0])
|
document.body.style.setProperty('--side', _data[0]);
|
||||||
document.body.style.setProperty('--logoOrBar', _data[1])
|
document.body.style.setProperty('--logoOrBar', _data[1]);
|
||||||
}
|
}
|
||||||
watch(sideStyle, changeSide)
|
|
||||||
|
|
||||||
function pushMark(params: any, type: markType) {
|
watch(sideStyle, changeSide);
|
||||||
const { latlng } = params;
|
|
||||||
marks.value.push({ latlng: [latlng.lat, latlng.lng], title: '新标点', opacity: 1, type })
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeMark(index: number) {
|
|
||||||
marks.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
function flytoMark(index: number) {
|
|
||||||
mapConfig.value.center = marks.value[index].latlng
|
|
||||||
}
|
|
||||||
|
|
||||||
function ctrlMap(e: Config) {
|
|
||||||
mapConfig.value = e
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h5 class="logo">离线地图</h5>
|
<h5 class="logo">离线地图</h5>
|
||||||
<Map ref="map" v-slot="{ contextmenu }" v-model="mapConfig">
|
<div class="main" ><router-view></router-view></div>
|
||||||
<q-menu touch-position context-menu>
|
<div class="bar">
|
||||||
<q-list dense style="min-width: 100px">
|
<q-toggle class="leftctl" v-model="sideStyle" true-value="20em,logo" false-value="0em,bar" />
|
||||||
<q-item clickable v-close-popup>
|
<ThemeSwitch></ThemeSwitch>
|
||||||
<q-item-section @click="pushMark(contextmenu, 'localtion')">添加标点</q-item-section>
|
</div>
|
||||||
</q-item>
|
<div class="left">
|
||||||
<q-item clickable v-close-popup>
|
<q-btn-toggle
|
||||||
<q-item-section @click="pushMark(contextmenu, 'equipment')">添加设备</q-item-section>
|
v-model="nowRouter"
|
||||||
</q-item>
|
spread
|
||||||
</q-list>
|
class="my-custom-toggle"
|
||||||
</q-menu>
|
no-caps
|
||||||
<Mark v-for="(mark, i) in marks" :key="i" :latlng="mark.latlng" :title="mark.title" :opacity="mark.opacity"
|
rounded
|
||||||
:ref="`mark-${i}`">
|
unelevated
|
||||||
<template v-if="mark.type === 'equipment'">
|
toggle-color="primary"
|
||||||
<q-icon :name="mdiLanConnect" size="md"></q-icon>
|
color="white"
|
||||||
</template>
|
text-color="primary"
|
||||||
</Mark>
|
:options="[
|
||||||
</Map>
|
{ label: '百度', value: '/baidu' },
|
||||||
<div class="bar">
|
{ label: '高德', value: '/gaode' },
|
||||||
|
{ label: '热力图', value: '/echart' },
|
||||||
<ThemeSwitch></ThemeSwitch>
|
]" />
|
||||||
</div>
|
<q-tabs v-model="tab" glossy>
|
||||||
<div class="left ">
|
<q-tab name="mark" label="标点" />
|
||||||
<q-toggle class="leftctl" v-model="sideStyle" true-value="20em,logo" false-value="0em,bar" />
|
<q-tab name="ctrl" label="控制" />
|
||||||
<q-tabs v-model="tab" dense active-color="primary" indicator-color="primary" align="justify" narrow-indicator>
|
</q-tabs>
|
||||||
<q-tab name="mark" label="标点" />
|
<q-tab-panels v-model="tab" animated keep-alive>
|
||||||
<q-tab name="ctrl" label="控制" />
|
<!-- <q-tab-panel name="mark">
|
||||||
</q-tabs>
|
<MarkMenu :marks="marks" @flyTo="flytoMark" @remove="removeMark"></MarkMenu>
|
||||||
<q-tab-panels v-model="tab" animated keep-alive>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="mark">
|
<q-tab-panel name="ctrl">
|
||||||
<MarkMenu :marks="marks" @flyTo="flytoMark" @remove="removeMark"></MarkMenu>
|
<CtrlMenu @change="ctrlMap"></CtrlMenu>
|
||||||
</q-tab-panel>
|
</q-tab-panel> -->
|
||||||
<q-tab-panel name="ctrl">
|
</q-tab-panels>
|
||||||
<CtrlMenu @change="ctrlMap"></CtrlMenu>
|
</div>
|
||||||
</q-tab-panel>
|
|
||||||
</q-tab-panels>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.logo {
|
.logo {
|
||||||
margin: unset;
|
margin: unset;
|
||||||
grid-area: var(--logoOrBar, logo);
|
grid-area: var(--logoOrBar, logo);
|
||||||
will-change: filter;
|
will-change: filter;
|
||||||
transition: filter 300ms;
|
transition: filter 300ms;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo:hover {
|
.logo:hover {
|
||||||
filter: drop-shadow(0 0 2em #646cffaa);
|
filter: drop-shadow(0 0 2em #646cffaa);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo.vue:hover {
|
.logo.vue:hover {
|
||||||
filter: drop-shadow(0 0 2em #42b883aa);
|
filter: drop-shadow(0 0 2em #42b883aa);
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
grid-area: left;
|
grid-area: left;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
.leftctl {
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar {
|
.leftctl {
|
||||||
grid-area: bar;
|
position: absolute;
|
||||||
display: flex;
|
top: 0px;
|
||||||
align-items: center;
|
left: 10px;
|
||||||
justify-content: flex-end;
|
}
|
||||||
margin-right: 20px;
|
}
|
||||||
}
|
.main {
|
||||||
|
grid-area: main;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
grid-area: bar;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,70 +1,62 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, provide, watch } from 'vue'
|
import { ref, onMounted, provide, watch } from 'vue';
|
||||||
import 'leaflet/dist/leaflet.css'
|
import 'leaflet/dist/leaflet.css';
|
||||||
import useMap from '../hook/useMap.ts'
|
import useMap from '../hook/useMap.ts';
|
||||||
import { useResizeObserver } from '@vueuse/core'
|
import { useResizeObserver } from '@vueuse/core';
|
||||||
import { setOptions } from 'leaflet'
|
import { setOptions } from 'leaflet';
|
||||||
|
|
||||||
const mapContainer = ref()
|
const mapContainer = ref();
|
||||||
const { map, tileLayers, props: mapProps } = useMap(mapContainer, { use: 'gaode', zoom: 13 });
|
const { map, tileLayers, props: mapProps } = useMap(mapContainer, { use: 'gaode', zoom: 13 });
|
||||||
|
|
||||||
// const config = defineModel<Object>()
|
// const config = defineModel<Object>()
|
||||||
|
|
||||||
const slotProps = ref({
|
const slotProps = ref({
|
||||||
contextmenu: {}
|
contextmenu: {},
|
||||||
})
|
});
|
||||||
|
|
||||||
const mapFilter = ref('invert(1) grayscale(.2) saturate(0.8) brightness(1.8) opacity(1) hue-rotate(184deg) sepia(17%)'); // 地图滤镜
|
const mapFilter = ref('invert(1) grayscale(.2) saturate(0.8) brightness(1.8) opacity(1) hue-rotate(184deg) sepia(17%)'); // 地图滤镜
|
||||||
|
|
||||||
|
provide('map', map); // 注入的内容 mapChild 下
|
||||||
|
|
||||||
provide('map', map) // 注入的内容 mapChild 下
|
onMounted(() => {
|
||||||
|
map.value?.addEventListener('contextmenu', (e) => {
|
||||||
|
slotProps.value.contextmenu = e;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
watch({ center: mapProps.value.center }, ({ center }) => center && map.value?.flyTo(center));
|
||||||
map.value?.addEventListener("contextmenu", (e) => {
|
watch({ max: mapProps.value.maxZoom || 0 }, ({ max }) => map.value?.setMaxZoom(max));
|
||||||
slotProps.value.contextmenu = e
|
watch({ min: mapProps.value.minZoom || 0 }, ({ min }) => map.value?.setMaxZoom(min));
|
||||||
})
|
watch({ url: mapProps.value.urlTemplate }, ({ url }) => url && tileLayers.value[0].setUrl(url));
|
||||||
})
|
watch({ zoomSnap: mapProps.value.zoomSnap }, ({ zoomSnap }) => map.value && (map.value.options.zoomSnap = zoomSnap));
|
||||||
|
|
||||||
|
function changeMapFilter(
|
||||||
|
filterInfo: Record<'blur' | 'brightness' | 'contrast' | 'drop-shadow' | 'grayscale' | 'hue-rotate' | 'invert' | 'opacity' | 'saturate' | 'sepia', string | number>
|
||||||
|
) {
|
||||||
|
mapFilter.value = Object.entries(filterInfo)
|
||||||
|
.map(([key, value]) => `${key}(${value})`)
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
useResizeObserver(mapContainer, () => map.value?.invalidateSize(true));
|
||||||
|
|
||||||
watch({ center: mapProps.value.center }, ({ center }) => center && map.value?.flyTo(center))
|
defineExpose({ changeMapFilter, setOptions });
|
||||||
watch({ max: mapProps.value.maxZoom || 0 }, ({ max }) => map.value?.setMaxZoom(max))
|
|
||||||
watch({ min: mapProps.value.minZoom || 0 }, ({ min }) => map.value?.setMaxZoom(min))
|
|
||||||
watch({ url: mapProps.value.urlTemplate }, ({ url }) => url && tileLayers.value[0].setUrl(url))
|
|
||||||
watch({ zoomSnap: mapProps.value.zoomSnap }, ({ zoomSnap }) => map.value && (map.value.options.zoomSnap = zoomSnap))
|
|
||||||
|
|
||||||
function changeMapFilter(filterInfo: Record<'blur' |
|
|
||||||
'brightness' |
|
|
||||||
'contrast' |
|
|
||||||
'drop-shadow' |
|
|
||||||
'grayscale' |
|
|
||||||
'hue-rotate' |
|
|
||||||
'invert' |
|
|
||||||
'opacity' |
|
|
||||||
'saturate' |
|
|
||||||
'sepia', string | number>) {
|
|
||||||
mapFilter.value = Object.entries(filterInfo).map(([key, value]) => `${key}(${value})`).join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
useResizeObserver(mapContainer, () => map.value?.invalidateSize(true))
|
|
||||||
|
|
||||||
defineExpose({ changeMapFilter, setOptions })
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mapContainer" ref="mapContainer" :style="{ '--mapFilter': mapFilter }">
|
<div class="mapContainer" ref="mapContainer" :style="{ '--mapFilter': mapFilter }">
|
||||||
<slot v-if="map" v-bind="slotProps"></slot>
|
<slot v-if="map" v-bind="slotProps"></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.mapContainer {
|
.mapContainer {
|
||||||
grid-area: map;
|
grid-area: map;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.body--dark {
|
body.body--dark {
|
||||||
.mapFilter {
|
.mapFilter {
|
||||||
filter: var(--mapFilter);
|
filter: var(--mapFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<q-menu touch-position context-menu>
|
||||||
|
<q-list dense style="min-width: 100px">
|
||||||
|
<q-item clickable v-close-popup>
|
||||||
|
<q-item-section @click="addPoint">添加标点</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item clickable v-close-popup>
|
||||||
|
<q-item-section @click="addDev">添加设备</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-menu>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, PropType } from 'vue';
|
||||||
|
import { useMarkStore } from '../store/useMarkStore';
|
||||||
|
import { LeafletMouseEventHandlerFn } from 'leaflet';
|
||||||
|
|
||||||
|
const { marks } = useMarkStore();
|
||||||
|
|
||||||
|
const emit = defineEmits(['addPoint', 'addDev']);
|
||||||
|
const props = defineProps({ map: Object as PropType<L.Map> });
|
||||||
|
|
||||||
|
onMounted(() => {});
|
||||||
|
|
||||||
|
function addPoint() {
|
||||||
|
const { map } = props;
|
||||||
|
if (!map) return;
|
||||||
|
const pushMark: LeafletMouseEventHandlerFn = (e) => {
|
||||||
|
marks.push({ latlng: [e.latlng.alt || 0, e.latlng.lng || 0], type: 'localtion', opacity: 1, title: '新地标' });
|
||||||
|
};
|
||||||
|
map.addEventListener('contextmenu', pushMark);
|
||||||
|
return map.removeEventListener('contextmenu', pushMark);
|
||||||
|
}
|
||||||
|
function addDev() {
|
||||||
|
const { map } = props;
|
||||||
|
if (!map) return;
|
||||||
|
const pushMark: LeafletMouseEventHandlerFn = (e) => {
|
||||||
|
marks.push({ latlng: [e.latlng.alt || 0, e.latlng.lng || 0], type: 'equipment', opacity: 1, title: '新设备' });
|
||||||
|
};
|
||||||
|
map.addEventListener('contextmenu', pushMark);
|
||||||
|
return map.removeEventListener('contextmenu', pushMark);
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -5,15 +5,10 @@ export type DataProps = { zoom: { min: number, max: number }, urlTemplate: strin
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import useConfig from '../../hook/useConfig';
|
|
||||||
import { MapType } from '../../lib/mapType';
|
import { MapType } from '../../lib/mapType';
|
||||||
const _configGetter = useConfig()
|
|
||||||
const maptype = ref<MapType>('gaode')
|
const maptype = ref<MapType>('gaode')
|
||||||
const data = ref(_configGetter('gaode'))
|
|
||||||
const emit = defineEmits(['change'])
|
const emit = defineEmits(['change'])
|
||||||
|
|
||||||
watch(maptype, v => data.value = _configGetter(v))
|
|
||||||
watch(data, (v) => emit('change', v), { deep: true })
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -30,11 +25,11 @@ watch(data, (v) => emit('change', v), { deep: true })
|
||||||
<q-item clickable v-close-popup>
|
<q-item clickable v-close-popup>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label>缩放控制</q-item-label>
|
<q-item-label>缩放控制</q-item-label>
|
||||||
<q-item-label caption>
|
<!-- <q-item-label caption>
|
||||||
<q-range :model-value="{ max: data.maxZoom, min: data.minZoom }"
|
<q-range :model-value="{ max: data.maxZoom, min: data.minZoom }"
|
||||||
@update:model-value="({ max, min }) => { data.maxZoom = max; data.minZoom = min; }" :min="3"
|
@update:model-value="({ max, min }) => { data.maxZoom = max; data.minZoom = min; }" :min="3"
|
||||||
:max="19" color="deep-orange" label-always markers switch-label-side />
|
:max="19" color="deep-orange" label-always markers switch-label-side />
|
||||||
</q-item-label>
|
</q-item-label> -->
|
||||||
<!-- TODO 控制多图层 ?? 缩放级别减少 直接控制路线等 -->
|
<!-- TODO 控制多图层 ?? 缩放级别减少 直接控制路线等 -->
|
||||||
<!-- TODO 地图不清楚 -->
|
<!-- TODO 地图不清楚 -->
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
@ -43,7 +38,7 @@ watch(data, (v) => emit('change', v), { deep: true })
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label>访问地址</q-item-label>
|
<q-item-label>访问地址</q-item-label>
|
||||||
<q-item-label caption>
|
<q-item-label caption>
|
||||||
<q-input v-model="data.urlTemplate" />
|
<!-- <q-input v-model="data.urlTemplate" /> -->
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
@ -51,7 +46,7 @@ watch(data, (v) => emit('change', v), { deep: true })
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label>失败图片</q-item-label>
|
<q-item-label>失败图片</q-item-label>
|
||||||
<q-item-label caption>
|
<q-item-label caption>
|
||||||
<q-input v-model="data.errorTileUrl" />
|
<!-- <q-input v-model="data.errorTileUrl" /> -->
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
@ -59,7 +54,7 @@ watch(data, (v) => emit('change', v), { deep: true })
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label>缩放倍数</q-item-label>
|
<q-item-label>缩放倍数</q-item-label>
|
||||||
<q-item-label caption>
|
<q-item-label caption>
|
||||||
<q-input v-model.number="data.zoomSnap" type="number" />
|
<!-- <q-input v-model.number="data.zoomSnap" type="number" /> -->
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,31 +0,0 @@
|
||||||
import { MapType } from "../lib/mapType";
|
|
||||||
|
|
||||||
export type Config = L.TileLayerOptions & { urlTemplate: string, center: L.LatLngExpression, zoomSnap?: number }
|
|
||||||
|
|
||||||
const mapConfig: Record<MapType, Config> = {
|
|
||||||
baidu: {
|
|
||||||
urlTemplate: "http://localhost/baidumaps/roadmap/{z}/{x}/{y}.png",
|
|
||||||
errorTileUrl: 'http://localhost/baidumaps/roadmap/error.png',
|
|
||||||
center: [39.926474, 116.403283],
|
|
||||||
maxZoom: 15, minZoom: 3
|
|
||||||
},
|
|
||||||
gaode: {
|
|
||||||
urlTemplate: "http://localhost/mapabc/roadmap/{z}/{x}/{y}.png",
|
|
||||||
errorTileUrl: 'http://localhost/baidumaps/roadmap/error.png',
|
|
||||||
center: [-97.00238024827533, 210.7725501856634],
|
|
||||||
maxZoom: 15, minZoom: 3
|
|
||||||
},
|
|
||||||
gaodeLine: {
|
|
||||||
urlTemplate: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
|
|
||||||
errorTileUrl: 'http://localhost/baidumaps/roadmap/error.png',
|
|
||||||
center: [-97.00238024827533, 210.7725501856634],
|
|
||||||
maxZoom: 15, minZoom: 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function useConfig(): (name: MapType) => Config {
|
|
||||||
return (name) => mapConfig[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default useConfig
|
|
|
@ -1,32 +1,29 @@
|
||||||
import L from 'leaflet'
|
import L from 'leaflet';
|
||||||
import 'proj4'
|
import 'proj4';
|
||||||
import 'proj4leaflet'
|
import 'proj4leaflet';
|
||||||
import { MapType } from '../lib/mapType';
|
import { MapType } from '../lib/mapType';
|
||||||
const _level = 19;
|
|
||||||
|
|
||||||
const crss: Record<MapType, L.CRS> = {
|
const crss: Record<MapType, L.CRS> = {
|
||||||
baidu: new L.Proj.CRS('EPSG:900913',
|
baidu: new L.Proj.CRS('EPSG:900913', '+proj=merc +a=6378206 +b=6356584.314245179 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', {
|
||||||
'+proj=merc +a=6378206 +b=6356584.314245179 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs',
|
resolutions: (function () {
|
||||||
{
|
let level = 19;
|
||||||
resolutions: function () {
|
var res = [];
|
||||||
let level = 19
|
res[0] = Math.pow(2, 18);
|
||||||
var res = [];
|
for (var i = 1; i < level; i++) {
|
||||||
res[0] = Math.pow(2, 18);
|
res[i] = Math.pow(2, 18 - i);
|
||||||
for (var i = 1; i < level; i++) {
|
}
|
||||||
res[i] = Math.pow(2, (18 - i))
|
return res;
|
||||||
}
|
})(),
|
||||||
return res;
|
origin: [0, 0],
|
||||||
}(),
|
bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244]),
|
||||||
origin: [0, 0],
|
}),
|
||||||
bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244])
|
gaode: L.CRS.Simple,
|
||||||
}),
|
gaodeCharts: L.CRS.Simple,
|
||||||
gaode: L.CRS.Simple,
|
gaodeLine: L.CRS.EPSG4326,
|
||||||
gaodeLine:L.CRS.EPSG4326
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function useCrs(name: MapType) {
|
function useCrs(name: MapType) {
|
||||||
return crss[name]
|
return crss[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default useCrs;
|
||||||
export default useCrs
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
function useDraw(){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default useDraw
|
|
|
@ -1,76 +1,32 @@
|
||||||
import L, { Layer } from "leaflet"
|
import { use, init, ComposeOption } from 'echarts/core';
|
||||||
import { Ref, isRef, onMounted, reactive, ref, shallowRef, toRef, triggerRef, watch } from "vue"
|
|
||||||
import useCrs from "./useCrs"
|
|
||||||
import { MapType } from "../lib/mapType"
|
|
||||||
import useConfig, { Config } from "./useConfig"
|
|
||||||
|
|
||||||
/**
|
import { ScatterChart, ScatterSeriesOption, EffectScatterChart, EffectScatterSeriesOption, HeatmapChart } from 'echarts/charts';
|
||||||
* 地图接口 地图类型
|
|
||||||
*/
|
|
||||||
interface MapController {
|
|
||||||
|
|
||||||
|
import { TooltipComponent, TitleComponentOption, TitleComponent, VisualMapComponent } from 'echarts/components';
|
||||||
|
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
|
||||||
|
import { LeafletComponent, LeafletComponentOption } from '../leafletchart/export';
|
||||||
|
import L, { CRS, MapOptions } from 'leaflet';
|
||||||
|
import { HeatmapSeriesOption } from 'echarts';
|
||||||
|
|
||||||
|
export type EMapOptions = LeafletComponentOption<MapOptions & { getCrs?: () => CRS }>;
|
||||||
|
export type ECOption = ComposeOption<ScatterSeriesOption | EffectScatterSeriesOption | TitleComponentOption | HeatmapSeriesOption> & EMapOptions;
|
||||||
|
|
||||||
|
function useMap(dom: HTMLElement, chartOptions: ECOption) {
|
||||||
|
use([CanvasRenderer, TooltipComponent, LeafletComponent, ScatterChart, EffectScatterChart, TitleComponent, HeatmapChart, VisualMapComponent]);
|
||||||
|
|
||||||
|
const chart = init(dom);
|
||||||
|
chart.setOption(chartOptions);
|
||||||
|
// @ts-ignore
|
||||||
|
const lmapComponent = chart.getModel().getComponent('lmap');
|
||||||
|
// @ts-ignore
|
||||||
|
const lmap: L.Map = lmapComponent.getLeaflet();
|
||||||
|
|
||||||
|
return {
|
||||||
|
map: lmap,
|
||||||
|
chart,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefineProps = L.MapOptions & Partial<Config> & { use: MapType }
|
export default useMap;
|
||||||
|
|
||||||
interface DefineExport {
|
|
||||||
map: Ref<L.Map | undefined>
|
|
||||||
tileLayers: Ref<L.TileLayer[]>
|
|
||||||
gridLayers: Ref<L.GridLayer[]>
|
|
||||||
geoJsons: Ref<L.GeoJSON[]>
|
|
||||||
props: Ref<DefineProps>
|
|
||||||
triggerProps: (options: Config) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function useMap(dom: Ref<string | HTMLElement>, props: DefineProps | Ref<DefineProps>): DefineExport {
|
|
||||||
const _props = isRef(props) ? props : shallowRef(props)
|
|
||||||
const _map = ref<L.Map>()
|
|
||||||
const _tileLayers: Ref<L.TileLayer[]> = ref([])
|
|
||||||
const _gridLayers: Ref<L.GridLayer[]> = ref([])
|
|
||||||
const _geos: Ref<L.GeoJSON[]> = ref([])
|
|
||||||
const _configGetter = useConfig()
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const _options = _props // FIXME 这里必须要强转嘛
|
|
||||||
if (!_options) return
|
|
||||||
const crs = useCrs(_options.value.use)
|
|
||||||
const _config = _configGetter(_options.value.use)
|
|
||||||
_map.value = L.map(dom.value, { ..._options.value, crs, center: _config.center })
|
|
||||||
const layer = L.tileLayer(_config.urlTemplate, _config);
|
|
||||||
layer.addTo(_map.value)
|
|
||||||
_tileLayers.value.push(layer)
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(_props, (options) => {
|
|
||||||
if (!_map.value) return
|
|
||||||
if (!options) return
|
|
||||||
_map.value.options.crs = useCrs(options.use)
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(_tileLayers, (layers) => {
|
|
||||||
if (!_map.value) return
|
|
||||||
if (layers.length < 1) return
|
|
||||||
const { hasLayer: _has, addLayer: _add } = _map.value
|
|
||||||
layers.forEach(l => !_has(l) && _add(l))
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(_geos, (geos) => {
|
|
||||||
const __map = _map.value
|
|
||||||
if (!__map) return
|
|
||||||
if (geos.length < 1) return
|
|
||||||
geos.forEach(geo => {
|
|
||||||
geo.addTo(__map)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改后激发props
|
|
||||||
*/
|
|
||||||
function triggerProps(options: Config) {
|
|
||||||
triggerRef(_props)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { map: _map, props: _props, tileLayers: _tileLayers, gridLayers: _gridLayers, geoJsons: _geos, triggerProps }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useMap
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { useLocalStorage } from "@vueuse/core"
|
|
||||||
|
|
||||||
const key = 'map-marks'
|
|
||||||
export type markType = 'localtion' | 'equipment'
|
|
||||||
export type markInfo = { latlng: [number, number], title: string, opacity?: number, type?: markType }
|
|
||||||
|
|
||||||
function useMarks(initData: markInfo[] = []) {
|
|
||||||
const stroked = useLocalStorage<markInfo[]>(key, initData);
|
|
||||||
return stroked;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useMarks
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
import { data } from './data';
|
||||||
|
import { ECOption, EMapOptions } from './useMap';
|
||||||
|
|
||||||
|
function useOption() {
|
||||||
|
const heatmap: (mapOptions: EMapOptions) => ECOption = (mapOptions) => {
|
||||||
|
var points = [].concat.apply(
|
||||||
|
[],
|
||||||
|
data.map(function (track: any) {
|
||||||
|
return track.map(function (seg: { coord: number[] }) {
|
||||||
|
const [x, y] = seg.coord;
|
||||||
|
return [x - 3.73, y + 9.7, 1];
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
animation: false,
|
||||||
|
lmap: {
|
||||||
|
center: [10, 60], // [lng, lat]
|
||||||
|
zoom: 4,
|
||||||
|
resizeEnable: true, // automatically handles browser window resize.
|
||||||
|
renderOnMoving: true,
|
||||||
|
echartsLayerInteractive: true, // Default: true
|
||||||
|
largeMode: false, // Default: false
|
||||||
|
|
||||||
|
...mapOptions?.leaflet,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: '热力图',
|
||||||
|
},
|
||||||
|
visualMap: {
|
||||||
|
show: false,
|
||||||
|
top: 'top',
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
seriesIndex: 0,
|
||||||
|
calculable: true,
|
||||||
|
inRange: {
|
||||||
|
color: ['blue', 'blue', 'green', 'yellow', 'red'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'heatmap',
|
||||||
|
coordinateSystem: 'lmap',
|
||||||
|
data: points,
|
||||||
|
pointSize: 5,
|
||||||
|
blurSize: 6,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const scatter: (mapOptions?: EMapOptions) => ECOption = (mapOptions) => ({
|
||||||
|
lmap: {
|
||||||
|
center: [10, 60], // [lng, lat]
|
||||||
|
zoom: 4,
|
||||||
|
resizeEnable: true, // automatically handles browser window resize.
|
||||||
|
renderOnMoving: true,
|
||||||
|
echartsLayerInteractive: true, // Default: true
|
||||||
|
largeMode: false, // Default: false
|
||||||
|
|
||||||
|
...mapOptions?.leaflet,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: '散点图',
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'scatter',
|
||||||
|
// use `lmap` as the coordinate system
|
||||||
|
coordinateSystem: 'lmap',
|
||||||
|
// data items [[lng, lat, value], [lng, lat, value], ...]
|
||||||
|
data: [
|
||||||
|
[39.926474, 116.403283, 400],
|
||||||
|
[10, 60, 8],
|
||||||
|
[10.1, 60, 20],
|
||||||
|
],
|
||||||
|
encode: {
|
||||||
|
// encode the third element of data item as the `value` dimension
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'effectScatter',
|
||||||
|
// use `lmap` as the coordinate system
|
||||||
|
coordinateSystem: 'lmap',
|
||||||
|
// data items [[lng, lat, value], [lng, lat, value], ...]
|
||||||
|
data: [
|
||||||
|
[39.926474, 116, 400],
|
||||||
|
[11, 60, 8],
|
||||||
|
[11.1, 60, 20],
|
||||||
|
],
|
||||||
|
encode: {
|
||||||
|
// encode the third element of data item as the `value` dimension
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
scatter,
|
||||||
|
heatmap,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useOption;
|
|
@ -1,69 +0,0 @@
|
||||||
type RGBA = Record<'r' | 'g' | 'b' | 'a', number>;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function imageChange(img: HTMLImageElement, colorize: (pixel: RGBA) => RGBA) {
|
|
||||||
if (img.getAttribute('data-colorized'))
|
|
||||||
return;
|
|
||||||
img.crossOrigin = 'anonymous'
|
|
||||||
var canvas = document.createElement("canvas");
|
|
||||||
canvas.width = img.width;
|
|
||||||
canvas.height = img.height;
|
|
||||||
var ctx = canvas.getContext("2d");
|
|
||||||
if (!ctx) return
|
|
||||||
ctx.drawImage(img, 0, 0);
|
|
||||||
var imgd = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
||||||
var pix = imgd.data;
|
|
||||||
for (var i = 0, n = pix.length; i < n; i += 4) {
|
|
||||||
|
|
||||||
var pixel = colorize({ r: pix[i], g: pix[i + 1], b: pix[i + 2], a: pix[i + 3] });
|
|
||||||
if (!!!pixel || pixel !== Object(pixel) || Object.prototype.toString.call(pixel) === '[object Array]') {
|
|
||||||
|
|
||||||
if (i === 0) {
|
|
||||||
throw 'The colorize option should return an object with at least one of "r", "g", "b", or "a" properties.';
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (pixel.hasOwnProperty('r') && typeof pixel.r === 'number') {
|
|
||||||
pix[i] = pixel.r;
|
|
||||||
}
|
|
||||||
if (pixel.hasOwnProperty('g')) {
|
|
||||||
pix[i + 1] = pixel.g;
|
|
||||||
}
|
|
||||||
if (pixel.hasOwnProperty('b')) {
|
|
||||||
pix[i + 2] = pixel.b;
|
|
||||||
}
|
|
||||||
if (pixel.hasOwnProperty('a')) {
|
|
||||||
pix[i + 3] = pixel.a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.putImageData(imgd, 0, 0);
|
|
||||||
img.setAttribute('data-colorized', 'true');
|
|
||||||
img.src = canvas.toDataURL();
|
|
||||||
return imgd;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param img 图片
|
|
||||||
* @param colorize 图片rgb颜色修改
|
|
||||||
* @example
|
|
||||||
* const colorize = useRgb()
|
|
||||||
* layer.on('tileload', (e) => {
|
|
||||||
* colorize(e.tile, (pixel) => {
|
|
||||||
* pixel.g += 17;
|
|
||||||
* pixel.b += 90;
|
|
||||||
* return pixel;
|
|
||||||
* })
|
|
||||||
* })
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function useRgb() {
|
|
||||||
return imageChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useRgb
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import L from 'leaflet';
|
||||||
|
import { tileLayer as LtileLayer } from 'leaflet';
|
||||||
|
|
||||||
|
function useTileLayer() {
|
||||||
|
const baidu = LtileLayer('http://localhost/baidumaps/roadmap/{z}/{x}/{y}.png', {
|
||||||
|
errorTileUrl: 'http://localhost/baidumaps/roadmap/error.png',
|
||||||
|
maxZoom: 15,
|
||||||
|
tms: true,
|
||||||
|
minZoom: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
const gaode = LtileLayer('http://localhost/mapabc/roadmap/{z}/{x}/{y}.png', {
|
||||||
|
errorTileUrl: 'http://localhost/baidumaps/roadmap/error.png',
|
||||||
|
maxZoom: 15,
|
||||||
|
tms: true,
|
||||||
|
minZoom: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Kitten = L.TileLayer.extend({
|
||||||
|
getTileUrl: function ({ x, y, z }: any) {
|
||||||
|
return `http://localhost/mapabc/roadmap/${z}/${x}/${y}.png`;
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
errorTileUrl: 'http://localhost/baidumaps/roadmap/error.png',
|
||||||
|
maxZoom: 15,
|
||||||
|
minZoom: 3,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const gaodeChart = new Kitten();
|
||||||
|
|
||||||
|
const gaodeLine = LtileLayer('http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
|
||||||
|
errorTileUrl: 'http://localhost/baidumaps/roadmap/error.png',
|
||||||
|
maxZoom: 15,
|
||||||
|
minZoom: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { baidu, gaode, gaodeLine, gaodeChart };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useTileLayer;
|
|
@ -1,85 +0,0 @@
|
||||||
import L from "leaflet"
|
|
||||||
import { ref, Ref } from "vue";
|
|
||||||
|
|
||||||
function close(used: number[], close: number): number { // 接近数字下表
|
|
||||||
const closeUsed = used.map((v, i) => ({ v: close - v, i }))
|
|
||||||
const p = closeUsed.shift();
|
|
||||||
if (!p) throw new Error('传入使用数组不能为空');
|
|
||||||
const a = closeUsed.reduce((pre, cur) => {
|
|
||||||
if (pre.v < 0 && cur.v < 0) { // 双方小数
|
|
||||||
return pre.v < cur.v ? cur : pre // 取大
|
|
||||||
} else if (pre.v < 0 || cur.v < 0) { // 有一方是小数
|
|
||||||
return pre.v < 0 ? pre : cur // 取小数
|
|
||||||
} else { // 都不是
|
|
||||||
|
|
||||||
return pre.v < cur.v ? pre : cur // 取小
|
|
||||||
}
|
|
||||||
}, p)
|
|
||||||
|
|
||||||
return a.i
|
|
||||||
}
|
|
||||||
|
|
||||||
function limitZoom(min: number, max: number, zoom: number): number {
|
|
||||||
return Math.max(min, Math.min(max, zoom))
|
|
||||||
}
|
|
||||||
|
|
||||||
function useZoom(mapContainer: Ref<HTMLElement>, map: Ref<L.Map | undefined>, used: number[]) {
|
|
||||||
const _used = used.sort((a, b) => a - b)
|
|
||||||
let _timer = 0;
|
|
||||||
let _delta = 0;
|
|
||||||
let _currentZoom = ref(0);
|
|
||||||
|
|
||||||
const zoomControl = L.Handler.extend({
|
|
||||||
addHooks: function () {
|
|
||||||
console.log('add')
|
|
||||||
L.DomEvent.on(mapContainer.value, 'wheel', this._onWheelScroll)
|
|
||||||
},
|
|
||||||
removeHooks: function () {
|
|
||||||
console.log('remove')
|
|
||||||
L.DomEvent.off(mapContainer.value, 'wheel', this._onWheelScroll)
|
|
||||||
},
|
|
||||||
_onWheelScroll: function (e: WheelEvent) {
|
|
||||||
const _map = map.value;
|
|
||||||
if (!_map) return;
|
|
||||||
let _lastMousePos = _map.mouseEventToContainerPoint(e);
|
|
||||||
let _debounce = _map.options.wheelDebounceTime
|
|
||||||
|
|
||||||
const delta = L.DomEvent.getWheelDelta(e); // 处理高度
|
|
||||||
_delta += delta
|
|
||||||
|
|
||||||
clearTimeout(_timer)
|
|
||||||
_timer = setTimeout(() => {
|
|
||||||
|
|
||||||
let zoom = _map.getZoom(),
|
|
||||||
snap = _map.options.zoomSnap || 0,
|
|
||||||
d2 = _delta / ((_map.options.wheelPxPerZoomLevel || 60) * 4),
|
|
||||||
d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
|
|
||||||
d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
|
|
||||||
min = _map.getMinZoom(),
|
|
||||||
max = _map.getMaxZoom(),
|
|
||||||
limit = limitZoom(min, max, zoom + (_delta > 0 ? d4 : -d4)),
|
|
||||||
delta = limit - zoom;
|
|
||||||
|
|
||||||
if (!delta) return
|
|
||||||
_currentZoom.value = close(_used, limit) + delta
|
|
||||||
const _to = _used[_currentZoom.value];
|
|
||||||
console.log(_used, '当前', zoom, '放大缩小', delta, '接近', close(_used, limit), '转到', _currentZoom.value, '实际', _to)
|
|
||||||
debugger
|
|
||||||
if (_map.options.scrollWheelZoom === 'center') {
|
|
||||||
_map.setZoom(_to);
|
|
||||||
} else {
|
|
||||||
_map.setZoomAround(_lastMousePos, _to);
|
|
||||||
}
|
|
||||||
_delta = 0
|
|
||||||
}, _debounce);
|
|
||||||
|
|
||||||
|
|
||||||
L.DomEvent.stop(e);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return zoomControl
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default useZoom
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { MapType } from "../lib/mapType";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function useConfig<T>( configName:T ,type: Record<MapType, T>) { // TODO 对config监听??
|
|
||||||
|
|
||||||
|
|
||||||
return // TODO 配置
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useConfig
|
|
|
@ -1,14 +0,0 @@
|
||||||
import L from 'leaflet'
|
|
||||||
import { Ref, onMounted, ref } from 'vue'
|
|
||||||
|
|
||||||
function useMap(mapContainer: Ref<string | HTMLElement>, options: L.MapOptions) {
|
|
||||||
const map = ref<L.Map>();
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
map.value = L.map(mapContainer.value, options)
|
|
||||||
})
|
|
||||||
|
|
||||||
return [map]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useMap
|
|
|
@ -1,26 +0,0 @@
|
||||||
import L from "leaflet";
|
|
||||||
import { ref, Ref, onMounted } from "vue";
|
|
||||||
|
|
||||||
|
|
||||||
function useTileLayer(map: Ref<L.Map>, url: string, options: L.TileLayerOptions) {
|
|
||||||
const tileLayer = ref<L.TileLayer>()
|
|
||||||
onMounted(() => {
|
|
||||||
tileLayer.value = L.tileLayer(url, options)
|
|
||||||
})
|
|
||||||
|
|
||||||
function open() {
|
|
||||||
if (!tileLayer.value) return;
|
|
||||||
if (!map.value) return;
|
|
||||||
tileLayer.value.addTo(map.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function close() {
|
|
||||||
if (!tileLayer.value) return;
|
|
||||||
if (!map.value) return;
|
|
||||||
tileLayer.value.removeFrom(map.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return [tileLayer, open, close]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useTileLayer
|
|
|
@ -0,0 +1,246 @@
|
||||||
|
import { util as zrUtil, graphic, matrix } from 'echarts/core';
|
||||||
|
import { DomUtil, LatLng, Layer, Map as LMap, Projection } from 'leaflet';
|
||||||
|
import 'proj4';
|
||||||
|
import projleaflet from 'proj4leaflet';
|
||||||
|
|
||||||
|
function dataToCoordSize(dataSize, dataItem) {
|
||||||
|
dataItem = dataItem || [0, 0];
|
||||||
|
return zrUtil.map(
|
||||||
|
[0, 1],
|
||||||
|
function (dimIdx) {
|
||||||
|
const val = dataItem[dimIdx];
|
||||||
|
const halfSize = dataSize[dimIdx] / 2;
|
||||||
|
const p1 = [];
|
||||||
|
const p2 = [];
|
||||||
|
p1[dimIdx] = val - halfSize;
|
||||||
|
p2[dimIdx] = val + halfSize;
|
||||||
|
p1[1 - dimIdx] = p2[1 - dimIdx] = dataItem[1 - dimIdx];
|
||||||
|
return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]);
|
||||||
|
},
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// exclude private and unsupported options
|
||||||
|
const excludedOptions = ['echartsLayerInteractive', 'renderOnMoving', 'largeMode', 'layers', 'getCrs'];
|
||||||
|
|
||||||
|
const CustomOverlay = Layer.extend({
|
||||||
|
initialize: function (container) {
|
||||||
|
this._container = container;
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd: function (map) {
|
||||||
|
let pane = map.getPane(this.options.pane);
|
||||||
|
pane.appendChild(this._container);
|
||||||
|
|
||||||
|
// Calculate initial position of container with
|
||||||
|
// `L.Map.latLngToLayerPoint()`, `getPixelOrigin()
|
||||||
|
// and/or `getPixelBounds()`
|
||||||
|
|
||||||
|
// L.DomUtil.setPosition(this._container, point);
|
||||||
|
|
||||||
|
// Add and position children elements if needed
|
||||||
|
|
||||||
|
// map.on('zoomend viewreset', this._update, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRemove: function (map) {
|
||||||
|
DomUtil.remove(this._container);
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function () {
|
||||||
|
// Recalculate position of container
|
||||||
|
// L.DomUtil.setPosition(this._container, point);
|
||||||
|
// Add/remove/reposition children elements if needed
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function LeafletCoordSys(lmap, api) {
|
||||||
|
this._lmap = lmap;
|
||||||
|
this._api = api;
|
||||||
|
this._mapOffset = [0, 0];
|
||||||
|
this._projection = Projection.Mercator;
|
||||||
|
// this.dimensions = ['lat', 'lng'] // Is Leaflet default, but incompatible with echarts heatmap since isGeoCoordSys in HeatMapView.ts, checks for [0] === 'lng', [1] === 'lat'
|
||||||
|
}
|
||||||
|
|
||||||
|
const LeafletCoordSysProto = LeafletCoordSys.prototype;
|
||||||
|
|
||||||
|
LeafletCoordSysProto.setZoom = function (zoom) {
|
||||||
|
this._zoom = zoom;
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.setCenter = function (center) {
|
||||||
|
const latlng = this._projection.project(new LatLng(center[1], center[0])); // lng, lat
|
||||||
|
this._center = [latlng.lng, latlng.lat];
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.setMapOffset = function (mapOffset) {
|
||||||
|
this._mapOffset = mapOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.setLeaflet = function (lmap) {
|
||||||
|
this._lmap = lmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.getLeaflet = function () {
|
||||||
|
return this._lmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.dataToPoint = function (data) {
|
||||||
|
const latlng = new LatLng(data[1], data[0]); // lng, lat
|
||||||
|
const px = this._lmap.latLngToLayerPoint(latlng);
|
||||||
|
const mapOffset = this._mapOffset;
|
||||||
|
return [px.x - mapOffset[0], px.y - mapOffset[1]];
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.pointToData = function (pt) {
|
||||||
|
const mapOffset = this._mapOffset;
|
||||||
|
const coord = this._lmap.layerPointToLatLng({
|
||||||
|
x: pt[0] + mapOffset[0],
|
||||||
|
y: pt[1] + mapOffset[1],
|
||||||
|
});
|
||||||
|
return [coord.lng, coord.lat]; // lng, lat
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.getViewRect = function () {
|
||||||
|
const api = this._api;
|
||||||
|
return new graphic.BoundingRect(0, 0, api.getWidth(), api.getHeight());
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.getRoamTransform = function () {
|
||||||
|
return matrix.create();
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.prepareCustoms = function () {
|
||||||
|
const rect = this.getViewRect();
|
||||||
|
return {
|
||||||
|
coordSys: {
|
||||||
|
// The name exposed to user is always 'cartesian2d' but not 'grid'.
|
||||||
|
type: 'lmap',
|
||||||
|
x: rect.x,
|
||||||
|
y: rect.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
coord: zrUtil.bind(this.dataToPoint, this),
|
||||||
|
size: zrUtil.bind(dataToCoordSize, this),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.convertToPixel = function (ecModel, finder, value) {
|
||||||
|
// here we don't use finder as only one amap component is allowed
|
||||||
|
return this.dataToPoint(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.convertFromPixel = function (ecModel, finder, value) {
|
||||||
|
// here we don't use finder as only one amap component is allowed
|
||||||
|
return this.pointToData(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSys.create = function (ecModel, api) {
|
||||||
|
let lmapCoordSys;
|
||||||
|
|
||||||
|
ecModel.eachComponent('lmap', function (lmapModel) {
|
||||||
|
if (typeof L === 'undefined') {
|
||||||
|
throw new Error('Leaflet api is not loaded');
|
||||||
|
}
|
||||||
|
if (lmapCoordSys) {
|
||||||
|
throw new Error('Only one lmap echarts component is allowed');
|
||||||
|
}
|
||||||
|
|
||||||
|
let lmap = lmapModel.getLeaflet();
|
||||||
|
const echartsLayerInteractive = lmapModel.get('echartsLayerInteractive');
|
||||||
|
if (!lmap) {
|
||||||
|
const root = api.getDom();
|
||||||
|
const painter = api.getZr().painter;
|
||||||
|
let viewportRoot = painter.getViewportRoot();
|
||||||
|
viewportRoot.className = 'lmap-ec-layer';
|
||||||
|
// Not support IE8
|
||||||
|
let lmapRoot = root.querySelector('.ec-extension-leaflet');
|
||||||
|
if (lmapRoot) {
|
||||||
|
// Reset viewport left and top, which will be changed
|
||||||
|
// in moving handler in Leaflet View
|
||||||
|
viewportRoot.style.left = '0px';
|
||||||
|
viewportRoot.style.top = '0px';
|
||||||
|
|
||||||
|
root.removeChild(lmapRoot);
|
||||||
|
}
|
||||||
|
lmapRoot = document.createElement('div');
|
||||||
|
lmapRoot.className = 'ec-extension-leaflet';
|
||||||
|
lmapRoot.style.cssText = 'position:absolute;top:0;left:0;bottom:0;right:0;';
|
||||||
|
root.appendChild(lmapRoot);
|
||||||
|
|
||||||
|
const options = zrUtil.clone(lmapModel.get());
|
||||||
|
|
||||||
|
const crs = options.getCrs && options.getCrs();
|
||||||
|
|
||||||
|
// delete excluded options
|
||||||
|
zrUtil.each(excludedOptions, function (key) {
|
||||||
|
delete options[key];
|
||||||
|
});
|
||||||
|
if (crs) {
|
||||||
|
lmap = new LMap(lmapRoot, { ...options, crs });
|
||||||
|
} else {
|
||||||
|
lmap = new LMap(lmapRoot, { ...options });
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Encapsulate viewportRoot element into
|
||||||
|
the parent element responsible for moving,
|
||||||
|
avoiding direct manipulation of viewportRoot elements
|
||||||
|
affecting related attributes such as offset.
|
||||||
|
*/
|
||||||
|
let moveContainer = document.createElement('div');
|
||||||
|
moveContainer.style = 'position: relative;';
|
||||||
|
moveContainer.appendChild(viewportRoot);
|
||||||
|
|
||||||
|
new CustomOverlay(moveContainer).addTo(lmap);
|
||||||
|
|
||||||
|
lmapModel.setLeaflet(lmap);
|
||||||
|
lmapModel.setEChartsLayer(viewportRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldEChartsLayerInteractive = lmapModel.__echartsLayerInteractive;
|
||||||
|
if (oldEChartsLayerInteractive !== echartsLayerInteractive) {
|
||||||
|
lmapModel.setEChartsLayerInteractive(echartsLayerInteractive);
|
||||||
|
lmapModel.__echartsLayerInteractive = echartsLayerInteractive;
|
||||||
|
}
|
||||||
|
|
||||||
|
const center = lmapModel.get('center');
|
||||||
|
const zoom = lmapModel.get('zoom');
|
||||||
|
if (center && zoom) {
|
||||||
|
const lmapCenter = lmap.getCenter(); // leaflet lat lng
|
||||||
|
const lmapZoom = lmap.getZoom();
|
||||||
|
const centerOrZoomChanged = lmapModel.centerOrZoomChanged(
|
||||||
|
[lmapCenter.lng, lmapCenter.lat], // lng, lat
|
||||||
|
lmapZoom
|
||||||
|
);
|
||||||
|
if (centerOrZoomChanged) {
|
||||||
|
lmap.setView(new LatLng(center[1], center[0]), zoom); // lng, lat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lmapCoordSys = new LeafletCoordSys(lmap, api);
|
||||||
|
lmapCoordSys.setMapOffset(lmapModel.__mapOffset || [0, 0]);
|
||||||
|
lmapCoordSys.setZoom(zoom);
|
||||||
|
lmapCoordSys.setCenter(center);
|
||||||
|
|
||||||
|
lmapModel.coordinateSystem = lmapCoordSys;
|
||||||
|
});
|
||||||
|
|
||||||
|
ecModel.eachSeries(function (seriesModel) {
|
||||||
|
if (seriesModel.get('coordinateSystem') === 'lmap') {
|
||||||
|
seriesModel.coordinateSystem = lmapCoordSys;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return lmapCoordSys && [lmapCoordSys];
|
||||||
|
};
|
||||||
|
|
||||||
|
LeafletCoordSysProto.dimensions = LeafletCoordSys.dimensions = ['lng', 'lat']; // lng, lat
|
||||||
|
|
||||||
|
LeafletCoordSysProto.type = 'lmap';
|
||||||
|
|
||||||
|
export default LeafletCoordSys;
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { ComponentModel } from 'echarts/core';
|
||||||
|
import { v2Equal } from './helper';
|
||||||
|
|
||||||
|
const LeafletModel = {
|
||||||
|
type: 'lmap',
|
||||||
|
|
||||||
|
setLeaflet(lmap) {
|
||||||
|
this.__lmap = lmap;
|
||||||
|
},
|
||||||
|
|
||||||
|
getLeaflet() {
|
||||||
|
return this.__lmap;
|
||||||
|
},
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.__lmap._leaflet_id;
|
||||||
|
},
|
||||||
|
|
||||||
|
setEChartsLayer(layer) {
|
||||||
|
this.__echartsLayer = layer;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEChartsLayer() {
|
||||||
|
return this.__echartsLayer;
|
||||||
|
},
|
||||||
|
|
||||||
|
setEChartsLayerVisiblity(visible) {
|
||||||
|
this.__echartsLayer.style.display = visible ? 'block' : 'none';
|
||||||
|
},
|
||||||
|
|
||||||
|
// FIXME: NOT SUPPORT <= IE 10
|
||||||
|
setEChartsLayerInteractive(interactive) {
|
||||||
|
this.option.echartsLayerInteractive = !!interactive;
|
||||||
|
this.__echartsLayer.style.pointerEvents = interactive ? 'auto' : 'none';
|
||||||
|
},
|
||||||
|
|
||||||
|
setCenterAndZoom(center, zoom) {
|
||||||
|
// center received here is in lat, lng, so swap it
|
||||||
|
this.option.center = [center[1], center[0]];
|
||||||
|
this.option.zoom = zoom;
|
||||||
|
},
|
||||||
|
|
||||||
|
centerOrZoomChanged(center, zoom) {
|
||||||
|
const option = this.option;
|
||||||
|
return !(v2Equal(center, option.center) && zoom === option.zoom);
|
||||||
|
},
|
||||||
|
|
||||||
|
defaultOption: {
|
||||||
|
center: [10.39506, 63.43049], // lng, lat
|
||||||
|
zoom: 6,
|
||||||
|
echartsLayerInteractive: true,
|
||||||
|
renderOnMoving: true,
|
||||||
|
largeMode: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComponentModel.extend(LeafletModel);
|
|
@ -0,0 +1,189 @@
|
||||||
|
import { ComponentView, getInstanceByDom, throttle } from 'echarts/core';
|
||||||
|
import { clearLogMap } from './helper';
|
||||||
|
|
||||||
|
const LeafletView = {
|
||||||
|
type: 'lmap',
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._isFirstRender = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
render(lmapModel, ecModel, api) {
|
||||||
|
let rendering = true;
|
||||||
|
|
||||||
|
const lmap = lmapModel.getLeaflet();
|
||||||
|
const moveContainer = api.getZr().painter.getViewportRoot().parentNode;
|
||||||
|
const coordSys = lmapModel.coordinateSystem;
|
||||||
|
const offsetEl = lmap._mapPane;
|
||||||
|
|
||||||
|
const renderOnMoving = lmapModel.get('renderOnMoving');
|
||||||
|
const resizeEnable = lmapModel.get('resizeEnable');
|
||||||
|
const largeMode = lmapModel.get('largeMode');
|
||||||
|
|
||||||
|
let moveHandler = function(e) {
|
||||||
|
if (rendering) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let transformStyle = offsetEl.style.transform;
|
||||||
|
let dx = 0;
|
||||||
|
let dy = 0;
|
||||||
|
if (transformStyle) {
|
||||||
|
transformStyle = transformStyle.replace('translate3d(', '');
|
||||||
|
let parts = transformStyle.split(',');
|
||||||
|
dx = -parseInt(parts[0], 10);
|
||||||
|
dy = -parseInt(parts[1], 10);
|
||||||
|
} else {
|
||||||
|
// browsers that don't support transform: matrix
|
||||||
|
dx = -parseInt(offsetEl.style.left, 10);
|
||||||
|
dy = -parseInt(offsetEl.style.top, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapOffset = [dx, dy];
|
||||||
|
const offsetLeft = mapOffset[0] + 'px';
|
||||||
|
const offsetTop = mapOffset[1] + 'px';
|
||||||
|
if (moveContainer.style.left !== offsetLeft) {
|
||||||
|
moveContainer.style.left = offsetLeft;
|
||||||
|
}
|
||||||
|
if (moveContainer.style.top !== offsetTop) {
|
||||||
|
moveContainer.style.top = offsetTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
coordSys.setMapOffset(lmapModel.__mapOffset = mapOffset);
|
||||||
|
|
||||||
|
const actionParams = {
|
||||||
|
type: 'lmapRoam',
|
||||||
|
animation: {
|
||||||
|
// compatible with ECharts 5.x
|
||||||
|
// no delay for rendering but remain animation of elements
|
||||||
|
duration: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.dispatchAction(actionParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(largeMode) {
|
||||||
|
moveHandler = throttle(moveHandler, 20, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._isFirstRender){
|
||||||
|
this._moveHandler = moveHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctrlStartHandler = function(e) {
|
||||||
|
if(e.originalEvent.code === 'ControlLeft') {
|
||||||
|
lmap.dragging.disable();
|
||||||
|
moveHandler(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctrlEndHandler = function(e) {
|
||||||
|
if(e.originalEvent.code === 'ControlLeft') {
|
||||||
|
lmap.dragging.enable();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this._ctrlStartHandler = ctrlStartHandler;
|
||||||
|
this._ctrlEndHandler = ctrlEndHandler;
|
||||||
|
|
||||||
|
lmap.off('move', this._moveHandler);
|
||||||
|
lmap.off('moveend', this._moveHandler);
|
||||||
|
lmap.off('zoom', this._moveHandler);
|
||||||
|
lmap.off('viewreset', this._moveHandler);
|
||||||
|
lmap.off('zoomend', this._moveHandler);
|
||||||
|
lmap.off('keydown', this._ctrlStartHandler);
|
||||||
|
lmap.off('keyup', this._ctrlEndHandler);
|
||||||
|
|
||||||
|
if(this._ctrlStartHandler) {
|
||||||
|
lmap.off('keydown', this._ctrlStartHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._ctrlEndHandler) {
|
||||||
|
lmap.off('keyup', this._ctrlEndHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._resizeHandler) {
|
||||||
|
lmap.off('resize', this._resizeHandler);
|
||||||
|
}
|
||||||
|
if (this._moveStartHandler) {
|
||||||
|
lmap.off('move', this._moveStartHandler);
|
||||||
|
lmap.off('zoomstart', this._moveStartHandler);
|
||||||
|
lmap.off('zoom', this._moveStartHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
lmap.on(renderOnMoving ? 'move' : 'moveend', moveHandler);
|
||||||
|
lmap.on(renderOnMoving ? 'zoom' : 'zoomend', moveHandler);
|
||||||
|
|
||||||
|
if(renderOnMoving){
|
||||||
|
lmap.on('viewreset', moveHandler); // needed?
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!renderOnMoving && !this._moveEndHandler) {
|
||||||
|
const moveEndHandler = function(e) {
|
||||||
|
setTimeout(function() {
|
||||||
|
lmapModel.setEChartsLayerVisiblity(true);
|
||||||
|
}, !largeMode ? 0 : 20);
|
||||||
|
};
|
||||||
|
this._moveEndHandler = moveEndHandler;
|
||||||
|
lmap.on('moveend', moveEndHandler);
|
||||||
|
lmap.on('zoomend', moveEndHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
lmap.on('keydown', ctrlStartHandler);
|
||||||
|
lmap.on('keyup', ctrlEndHandler);
|
||||||
|
|
||||||
|
this._moveHandler = moveHandler;
|
||||||
|
|
||||||
|
if (!renderOnMoving) {
|
||||||
|
const moveStartHandler = function() {
|
||||||
|
lmapModel.setEChartsLayerVisiblity(false);
|
||||||
|
setTimeout(function() {
|
||||||
|
lmapModel.setEChartsLayerVisiblity(true);
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
this._moveStartHandler = moveStartHandler;
|
||||||
|
lmap.on('move', moveStartHandler); // hide when move occurs
|
||||||
|
lmap.on('zoomstart', moveStartHandler); // hide when zoom starts
|
||||||
|
lmap.on('zoom', moveStartHandler); // hide when zoom occurs
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resizeEnable) {
|
||||||
|
let resizeHandler = function() {
|
||||||
|
getInstanceByDom(api.getDom()).resize();
|
||||||
|
};
|
||||||
|
if (largeMode) {
|
||||||
|
resizeHandler = throttle(resizeHandler, 20, true);
|
||||||
|
}
|
||||||
|
this._resizeHandler = resizeHandler;
|
||||||
|
lmap.on('resize', resizeHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isFirstRender = rendering = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
clearLogMap();
|
||||||
|
const component = this.__model;
|
||||||
|
if (component) {
|
||||||
|
const leaflet = component.getLeaflet();
|
||||||
|
if(leaflet){
|
||||||
|
component.getLeaflet().off();
|
||||||
|
component.getLeaflet().remove();
|
||||||
|
}
|
||||||
|
component.setLeaflet(null);
|
||||||
|
component.setEChartsLayer(null);
|
||||||
|
if (component.coordinateSystem) {
|
||||||
|
component.coordinateSystem.setLeaflet(null);
|
||||||
|
component.coordinateSystem = null;
|
||||||
|
}
|
||||||
|
delete this._moveHandler;
|
||||||
|
delete this._resizeHandler;
|
||||||
|
delete this._moveStartHandler;
|
||||||
|
delete this._moveEndHandler;
|
||||||
|
delete this._ctrlEndHandler;
|
||||||
|
delete this._ctrlStartHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComponentView.extend(LeafletView);
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { use } from "echarts/core";
|
||||||
|
import { HeatmapSeriesOption } from "echarts/charts";
|
||||||
|
|
||||||
|
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
||||||
|
k: infer I
|
||||||
|
) => void
|
||||||
|
? I
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type LastOf<T> = UnionToIntersection<
|
||||||
|
T extends any ? () => T : never
|
||||||
|
> extends () => infer R
|
||||||
|
? R
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type Push<T extends any[], V> = [...T, V];
|
||||||
|
|
||||||
|
type TuplifyUnion<
|
||||||
|
T,
|
||||||
|
L = LastOf<T>,
|
||||||
|
N = [T] extends [never] ? true : false
|
||||||
|
> = true extends N ? [] : Push<TuplifyUnion<Exclude<T, L>>, L>;
|
||||||
|
|
||||||
|
type EChartsExtensionInstallRegisters = Parameters<
|
||||||
|
TuplifyUnion<Parameters<typeof use>[0]>[0]
|
||||||
|
>[0];
|
||||||
|
|
||||||
|
export type EChartsExtensionRegisters = EChartsExtensionInstallRegisters;
|
||||||
|
|
||||||
|
// HeatmapSeriesOption does not support 'lmap'
|
||||||
|
type LeafletHeatmapSeriesOption = HeatmapSeriesOption & {
|
||||||
|
coordinateSystem: "lmap";
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To install Leaflet component
|
||||||
|
* @param registers registers echarts registers.
|
||||||
|
*/
|
||||||
|
declare function install(registers: EChartsExtensionRegisters): void;
|
||||||
|
|
||||||
|
export * from "./types";
|
||||||
|
export { install as LeafletComponent, LeafletHeatmapSeriesOption };
|
|
@ -0,0 +1 @@
|
||||||
|
export { LeafletComponent } from './lmap'
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { version } from "echarts/core"; //lib/echarts";
|
||||||
|
|
||||||
|
export const ecVer = version.split('.');
|
||||||
|
|
||||||
|
export function v2Equal(a, b) {
|
||||||
|
return a && b && a[0] === b[0] && a[1] === b[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
let logMap = {};
|
||||||
|
|
||||||
|
export function logWarn(tag, msg, once) {
|
||||||
|
const log = `[ECharts][Extension][Leaflet]${tag ? ' ' + tag + ':' : ''} ${msg}`;
|
||||||
|
once && logMap[log] || console.warn(log);
|
||||||
|
once && (logMap[log] = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearLogMap() {
|
||||||
|
logMap = {};
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./types";
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { use } from 'echarts/core';
|
||||||
|
import { LeafletComponent } from './lmap';
|
||||||
|
|
||||||
|
use(LeafletComponent);
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* Leaflet component extension
|
||||||
|
*/
|
||||||
|
|
||||||
|
import LeafletCoordSys from './LeafletCoordSys';
|
||||||
|
import LeafletModel from './LeafletModel';
|
||||||
|
import LeafletView from './LeafletView';
|
||||||
|
|
||||||
|
import { ecVer } from "./helper";
|
||||||
|
|
||||||
|
function install(registers) {
|
||||||
|
|
||||||
|
// add coordinate system support for pie series for ECharts < 5.4.0
|
||||||
|
if ((ecVer[0] == 5 && ecVer[1] < 4)) {
|
||||||
|
registers.registerLayout(function(ecModel, api) {
|
||||||
|
ecModel.eachSeriesByType('pie', function (seriesModel) {
|
||||||
|
const coordSys = seriesModel.coordinateSystem;
|
||||||
|
const data = seriesModel.getData();
|
||||||
|
const valueDim = data.mapDimension('value');
|
||||||
|
if (coordSys && coordSys.type === 'lmap') {
|
||||||
|
const center = seriesModel.get('center');
|
||||||
|
const point = coordSys.dataToPoint(center);
|
||||||
|
const cx = point[0];
|
||||||
|
const cy = point[1];
|
||||||
|
data.each(valueDim, function (value, idx) {
|
||||||
|
const layout = data.getItemLayout(idx);
|
||||||
|
layout.cx = cx;
|
||||||
|
layout.cy = cy;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registers.registerComponentModel(LeafletModel);
|
||||||
|
registers.registerComponentView(LeafletView);
|
||||||
|
registers.registerCoordinateSystem('lmap', LeafletCoordSys);
|
||||||
|
|
||||||
|
// Action
|
||||||
|
registers.registerAction(
|
||||||
|
{
|
||||||
|
type: 'lmapRoam',
|
||||||
|
event: 'lmapRoam',
|
||||||
|
update: 'updateLayout'
|
||||||
|
},
|
||||||
|
function(payload, ecModel) {
|
||||||
|
ecModel.eachComponent('lmap', function(lmapModel) {
|
||||||
|
const lmap = lmapModel.getLeaflet();
|
||||||
|
const center = lmap.getCenter();
|
||||||
|
lmapModel.setCenterAndZoom([center.lat, center.lng], lmap.getZoom());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {install as LeafletComponent}
|
|
@ -0,0 +1,36 @@
|
||||||
|
declare const name = "echarts-extension-leaflet";
|
||||||
|
declare const version = "1.0.0";
|
||||||
|
|
||||||
|
|
||||||
|
interface InnerLeafletComponentOption {
|
||||||
|
/**
|
||||||
|
* Whether echarts layer is interactive.
|
||||||
|
* @default true
|
||||||
|
* @since v1.0.0
|
||||||
|
*/
|
||||||
|
echartsLayerInteractive?: Boolean;
|
||||||
|
/**
|
||||||
|
* Whether to enable large mode
|
||||||
|
* @default false
|
||||||
|
* @since v1.0.0
|
||||||
|
*/
|
||||||
|
largeMode?: false;
|
||||||
|
/**
|
||||||
|
* Whether echarts layer should be rendered when the map is moving.
|
||||||
|
* if `false`, it will only be re-rendered after the map `moveend`.
|
||||||
|
* It's better to set this option to false if data is large.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
renderOnMoving?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Leaflet component option
|
||||||
|
*/
|
||||||
|
interface LeafletComponentOption<LeafletOption> {
|
||||||
|
leaflet?: LeafletOption extends never
|
||||||
|
? InnerLeafletComponentOption
|
||||||
|
: InnerLeafletComponentOption & LeafletOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { name, version, LeafletComponentOption };
|
|
@ -1 +1 @@
|
||||||
export type MapType = 'baidu' | 'gaode' | 'gaodeLine'
|
export type MapType = 'baidu' | 'gaode' | 'gaodeLine' | 'gaodeCharts';
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
|
||||||
|
import baidu from '../pages/baidu/index.vue';
|
||||||
|
import gaode from '../pages/gaode/index.vue';
|
||||||
|
import echart from '../pages/echart/index.vue';
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{ path: '/', redirect: '/gaode' },
|
||||||
|
{ path: '/gaode', component: gaode },
|
||||||
|
{ path: '/baidu', component: baidu },
|
||||||
|
{ path: '/echart', component: echart },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes,
|
||||||
|
});
|
27
src/main.ts
27
src/main.ts
|
@ -1,20 +1,25 @@
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue';
|
||||||
import './style.scss'
|
import './style.scss';
|
||||||
import App from './App.vue'
|
import App from './App.vue';
|
||||||
|
|
||||||
import { Quasar } from 'quasar'
|
import { Quasar } from 'quasar';
|
||||||
|
import { router } from './lib/router';
|
||||||
|
|
||||||
// Import icon libraries
|
// Import icon libraries
|
||||||
import '@quasar/extras/material-icons/material-icons.css'
|
import '@quasar/extras/material-icons/material-icons.css';
|
||||||
|
|
||||||
import '@quasar/extras/mdi-v7/mdi-v7.css'
|
import '@quasar/extras/mdi-v7/mdi-v7.css';
|
||||||
|
|
||||||
// Import Quasar css
|
// Import Quasar css
|
||||||
import 'quasar/src/css/index.sass'
|
import 'quasar/src/css/index.sass';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
|
||||||
|
const myApp = createApp(App);
|
||||||
|
|
||||||
const myApp = createApp(App)
|
const pinia = createPinia();
|
||||||
myApp.use(Quasar, {
|
myApp.use(Quasar, {
|
||||||
plugins: {}, // import Quasar plugins and add here
|
plugins: {}, // import Quasar plugins and add here
|
||||||
})
|
});
|
||||||
myApp.mount('#app')
|
myApp.use(pinia);
|
||||||
|
myApp.use(router);
|
||||||
|
myApp.mount('#app');
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<div ref="mapContainer" style="width: 100%; height: 100%">
|
||||||
|
<MapMenu :map="map"></MapMenu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import useTileLayer from '../../hook/useTileLayer';
|
||||||
|
import useCrs from '../../hook/useCrs';
|
||||||
|
import L from 'leaflet';
|
||||||
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
import MapMenu from '../../components/MapMenu.vue';
|
||||||
|
|
||||||
|
const mapContainer = ref<HTMLElement>();
|
||||||
|
const { baidu } = useTileLayer();
|
||||||
|
const map = ref<L.Map>();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!mapContainer.value) return;
|
||||||
|
map.value = L.map(mapContainer.value, {
|
||||||
|
crs: useCrs('baidu'),
|
||||||
|
center: [39.926474, 116.403283],
|
||||||
|
zoom: 10,
|
||||||
|
maxZoom: 15,
|
||||||
|
minZoom: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
baidu.addTo(map.value);
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<div ref="mapContainer" style="width: 100%; height: 100%">
|
||||||
|
<q-menu touch-position context-menu>
|
||||||
|
<q-list dense style="min-width: 100px">
|
||||||
|
<q-item clickable v-close-popup>
|
||||||
|
<q-item-section @click="$emit('flyTo')">跳转</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item clickable v-close-popup>
|
||||||
|
<q-item-section @click="$emit('remove')">删除</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import useOption from '../../hook/useOption';
|
||||||
|
import useMap from '../../hook/useMap';
|
||||||
|
import useTileLayer from '../../hook/useTileLayer';
|
||||||
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
import { useResizeObserver } from '@vueuse/core';
|
||||||
|
|
||||||
|
const mapContainer = ref<HTMLElement>();
|
||||||
|
const { heatmap } = useOption();
|
||||||
|
const { gaodeChart } = useTileLayer();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!mapContainer.value) return;
|
||||||
|
const { map } = useMap(mapContainer.value, heatmap({ leaflet: { center: [116.396226, 39.914797], zoom: 13 } }));
|
||||||
|
gaodeChart.addTo(map);
|
||||||
|
useResizeObserver(mapContainer, () => map.invalidateSize(true));
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<template>
|
||||||
|
<div ref="mapContainer" style="width: 100%; height: 100%" ></div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import useTileLayer from '../../hook/useTileLayer';
|
||||||
|
import L from 'leaflet';
|
||||||
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
import useCrs from '../../hook/useCrs';
|
||||||
|
|
||||||
|
const mapContainer = ref<HTMLElement>();
|
||||||
|
const { gaode } = useTileLayer();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!mapContainer.value) return;
|
||||||
|
const map = L.map(mapContainer.value, {
|
||||||
|
crs: useCrs('gaode'),
|
||||||
|
center: [-97.00238024827533, 210.7725501856634],
|
||||||
|
zoom: 10,
|
||||||
|
maxZoom: 15,
|
||||||
|
minZoom: 3,
|
||||||
|
});
|
||||||
|
gaode.addTo(map);
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { useLocalStorage } from '@vueuse/core';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
const key = 'map-marks';
|
||||||
|
export type markType = 'localtion' | 'equipment';
|
||||||
|
export type markInfo = { latlng: [number, number]; title: string; opacity?: number; type?: markType };
|
||||||
|
|
||||||
|
export const useMarkStore = defineStore('mark', () => {
|
||||||
|
const marks = useLocalStorage<markInfo[]>(key, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
marks,
|
||||||
|
};
|
||||||
|
});
|
|
@ -1,9 +1,10 @@
|
||||||
#app {
|
#app {
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
grid-template-areas: "logo bar"
|
grid-template-areas:
|
||||||
"left map";
|
'logo bar'
|
||||||
grid-template-rows: 3em 1fr;
|
'left main';
|
||||||
grid-template-columns: var(--side, 20em) 1fr;
|
grid-template-rows: 3em 1fr;
|
||||||
|
grid-template-columns: var(--side, 20em) 1fr;
|
||||||
}
|
}
|
Loading…
Reference in New Issue