切换echarts 问题1 原先的打标点产生冲突 -- 解决中
							parent
							
								
									ca28c0851e
								
							
						
					
					
						commit
						b0c004eab8
					
				| 
						 | 
				
			
			@ -13,13 +13,17 @@
 | 
			
		|||
    "@types/leaflet-draw": "^1.0.7",
 | 
			
		||||
    "@types/proj4leaflet": "^1.0.7",
 | 
			
		||||
    "@vueuse/core": "^10.2.1",
 | 
			
		||||
    "echarts": "^5.4.3",
 | 
			
		||||
    "geojson": "^0.5.0",
 | 
			
		||||
    "heatmapjs": "^2.0.2",
 | 
			
		||||
    "leaflet": "^1.9.4",
 | 
			
		||||
    "leaflet-draw": "^1.0.4",
 | 
			
		||||
    "pinia": "^2.1.6",
 | 
			
		||||
    "proj4": "^2.9.0",
 | 
			
		||||
    "proj4leaflet": "^1.0.2",
 | 
			
		||||
    "quasar": "^2.12.3",
 | 
			
		||||
    "vue": "^3.3.4"
 | 
			
		||||
    "vue": "^3.3.4",
 | 
			
		||||
    "vue-router": "4"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@quasar/vite-plugin": "^1.4.1",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,15 +17,24 @@ dependencies:
 | 
			
		|||
  '@vueuse/core':
 | 
			
		||||
    specifier: ^10.2.1
 | 
			
		||||
    version: 10.2.1(vue@3.3.4)
 | 
			
		||||
  echarts:
 | 
			
		||||
    specifier: ^5.4.3
 | 
			
		||||
    version: 5.4.3
 | 
			
		||||
  geojson:
 | 
			
		||||
    specifier: ^0.5.0
 | 
			
		||||
    version: 0.5.0
 | 
			
		||||
  heatmapjs:
 | 
			
		||||
    specifier: ^2.0.2
 | 
			
		||||
    version: 2.0.2
 | 
			
		||||
  leaflet:
 | 
			
		||||
    specifier: ^1.9.4
 | 
			
		||||
    version: 1.9.4
 | 
			
		||||
  leaflet-draw:
 | 
			
		||||
    specifier: ^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:
 | 
			
		||||
    specifier: ^2.9.0
 | 
			
		||||
    version: 2.9.0
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +47,9 @@ dependencies:
 | 
			
		|||
  vue:
 | 
			
		||||
    specifier: ^3.3.4
 | 
			
		||||
    version: 3.3.4
 | 
			
		||||
  vue-router:
 | 
			
		||||
    specifier: '4'
 | 
			
		||||
    version: 4.2.4(vue@3.3.4)
 | 
			
		||||
 | 
			
		||||
devDependencies:
 | 
			
		||||
  '@quasar/vite-plugin':
 | 
			
		||||
| 
						 | 
				
			
			@ -405,6 +417,10 @@ packages:
 | 
			
		|||
      '@vue/compiler-dom': 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):
 | 
			
		||||
    resolution: {integrity: sha512-DKQNiNQzNV7nrkZQujvjfX73zqKdj2+KoM4YeKl+ft3f+crO3JB4ycPnmgaRMNX/ULJootdQPGHKFRl5cXxwaw==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
| 
						 | 
				
			
			@ -549,6 +565,13 @@ packages:
 | 
			
		|||
    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
 | 
			
		||||
    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:
 | 
			
		||||
    resolution: {integrity: sha512-vhg/WR/Oiu4oUIkVhmfcc23G6/zWuEQKFS+yiosSHe4aN6+DQRXIfeloYGibIfVhkr4wyfuVsGNLr+sQU1rWWw==}
 | 
			
		||||
    engines: {node: '>=12'}
 | 
			
		||||
| 
						 | 
				
			
			@ -614,6 +637,10 @@ packages:
 | 
			
		|||
    hasBin: true
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /heatmapjs@2.0.2:
 | 
			
		||||
    resolution: {integrity: sha512-1pO/bbn9G1NYhndvjnzLVAQMGBOCk8abMM7QnLOPlIMoxDfG9Uylb68PlZe/0MCT9GuwGGtchXVPLWfoT6HrCw==}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /is-binary-path@2.1.0:
 | 
			
		||||
    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
| 
						 | 
				
			
			@ -692,6 +719,24 @@ packages:
 | 
			
		|||
    engines: {node: '>=8.6'}
 | 
			
		||||
    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:
 | 
			
		||||
    resolution: {integrity: sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==}
 | 
			
		||||
    engines: {node: ^10 || ^12 || >=14}
 | 
			
		||||
| 
						 | 
				
			
			@ -763,11 +808,14 @@ packages:
 | 
			
		|||
      is-number: 7.0.0
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /tslib@2.3.0:
 | 
			
		||||
    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /typescript@5.1.6:
 | 
			
		||||
    resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
 | 
			
		||||
    engines: {node: '>=14.17'}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /vite@4.4.4(sass@1.32.12):
 | 
			
		||||
    resolution: {integrity: sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==}
 | 
			
		||||
| 
						 | 
				
			
			@ -820,6 +868,15 @@ packages:
 | 
			
		|||
      vue: 3.3.4
 | 
			
		||||
    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:
 | 
			
		||||
    resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
| 
						 | 
				
			
			@ -855,3 +912,9 @@ packages:
 | 
			
		|||
  /yallist@4.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /zrender@5.4.4:
 | 
			
		||||
    resolution: {integrity: sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      tslib: 2.3.0
 | 
			
		||||
    dev: false
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										206
									
								
								src/App.vue
								
								
								
								
							
							
						
						
									
										206
									
								
								src/App.vue
								
								
								
								
							| 
						 | 
				
			
			@ -1,129 +1,115 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { onBeforeMount, ref, watch } from 'vue';
 | 
			
		||||
import Map from './components/Map.vue'
 | 
			
		||||
import Mark from './components/mapChild/Mark.vue'
 | 
			
		||||
import useMarks, { markType } from './hook/useMarks.ts'
 | 
			
		||||
import ThemeSwitch from './components/ThemeSwitch.vue'
 | 
			
		||||
import MarkMenu from './components/left/MarkMenu.vue'
 | 
			
		||||
import CtrlMenu from './components/left/CtrlMenu.vue'
 | 
			
		||||
import { mdiLanConnect } from '@quasar/extras/mdi-v7'
 | 
			
		||||
import { useWindowSize } from '@vueuse/core';
 | 
			
		||||
import { Config } from './hook/useConfig';
 | 
			
		||||
	import { onBeforeMount, ref, watch } from 'vue';
 | 
			
		||||
	import ThemeSwitch from './components/ThemeSwitch.vue';
 | 
			
		||||
	import MarkMenu from './components/left/MarkMenu.vue';
 | 
			
		||||
	import CtrlMenu from './components/left/CtrlMenu.vue';
 | 
			
		||||
	import { useWindowSize } from '@vueuse/core';
 | 
			
		||||
	import { useRoute, useRouter } from 'vue-router';
 | 
			
		||||
import { useMarkStore } from './store/useMarkStore';
 | 
			
		||||
 | 
			
		||||
	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>>(); // 获取地图的实例 纂取实例方法
 | 
			
		||||
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()
 | 
			
		||||
	watch(nowRouter, (path) => router.push(path));
 | 
			
		||||
 | 
			
		||||
onBeforeMount(() => {
 | 
			
		||||
  sideStyle.value = width.value > 600 ? '20em,logo' : '0em,bar'
 | 
			
		||||
})
 | 
			
		||||
	onBeforeMount(() => {
 | 
			
		||||
		sideStyle.value = width.value > 600 ? '20em,logo' : '0em,bar';
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
function changeSide(data: string) {
 | 
			
		||||
  const _data = data.split(',')
 | 
			
		||||
  document.body.style.setProperty('--side', _data[0])
 | 
			
		||||
  document.body.style.setProperty('--logoOrBar', _data[1])
 | 
			
		||||
}
 | 
			
		||||
watch(sideStyle, changeSide)
 | 
			
		||||
	function changeSide(data: string) {
 | 
			
		||||
		const _data = data.split(',');
 | 
			
		||||
		document.body.style.setProperty('--side', _data[0]);
 | 
			
		||||
		document.body.style.setProperty('--logoOrBar', _data[1]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
function pushMark(params: any, type: markType) {
 | 
			
		||||
  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
 | 
			
		||||
}
 | 
			
		||||
	watch(sideStyle, changeSide);
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <h5 class="logo">离线地图</h5>
 | 
			
		||||
  <Map ref="map" v-slot="{ contextmenu }" v-model="mapConfig">
 | 
			
		||||
    <q-menu touch-position context-menu>
 | 
			
		||||
      <q-list dense style="min-width: 100px">
 | 
			
		||||
        <q-item clickable v-close-popup>
 | 
			
		||||
          <q-item-section @click="pushMark(contextmenu, 'localtion')">添加标点</q-item-section>
 | 
			
		||||
        </q-item>
 | 
			
		||||
        <q-item clickable v-close-popup>
 | 
			
		||||
          <q-item-section @click="pushMark(contextmenu, 'equipment')">添加设备</q-item-section>
 | 
			
		||||
        </q-item>
 | 
			
		||||
      </q-list>
 | 
			
		||||
    </q-menu>
 | 
			
		||||
    <Mark v-for="(mark, i) in marks" :key="i" :latlng="mark.latlng" :title="mark.title" :opacity="mark.opacity"
 | 
			
		||||
      :ref="`mark-${i}`">
 | 
			
		||||
      <template v-if="mark.type === 'equipment'">
 | 
			
		||||
        <q-icon :name="mdiLanConnect" size="md"></q-icon>
 | 
			
		||||
      </template>
 | 
			
		||||
    </Mark>
 | 
			
		||||
  </Map>
 | 
			
		||||
  <div class="bar">
 | 
			
		||||
 | 
			
		||||
    <ThemeSwitch></ThemeSwitch>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="left ">
 | 
			
		||||
    <q-toggle class="leftctl" v-model="sideStyle" true-value="20em,logo" false-value="0em,bar" />
 | 
			
		||||
    <q-tabs v-model="tab" dense active-color="primary" indicator-color="primary" align="justify" narrow-indicator>
 | 
			
		||||
      <q-tab name="mark" label="标点" />
 | 
			
		||||
      <q-tab name="ctrl" label="控制" />
 | 
			
		||||
    </q-tabs>
 | 
			
		||||
    <q-tab-panels v-model="tab" animated keep-alive>
 | 
			
		||||
      <q-tab-panel name="mark">
 | 
			
		||||
        <MarkMenu :marks="marks" @flyTo="flytoMark" @remove="removeMark"></MarkMenu>
 | 
			
		||||
      </q-tab-panel>
 | 
			
		||||
      <q-tab-panel name="ctrl">
 | 
			
		||||
        <CtrlMenu @change="ctrlMap"></CtrlMenu>
 | 
			
		||||
      </q-tab-panel>
 | 
			
		||||
    </q-tab-panels>
 | 
			
		||||
  </div>
 | 
			
		||||
	<h5 class="logo">离线地图</h5>
 | 
			
		||||
	<div class="main" ><router-view></router-view></div>
 | 
			
		||||
	<div class="bar">
 | 
			
		||||
		<q-toggle class="leftctl" v-model="sideStyle" true-value="20em,logo" false-value="0em,bar" />
 | 
			
		||||
		<ThemeSwitch></ThemeSwitch>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="left">
 | 
			
		||||
		<q-btn-toggle
 | 
			
		||||
			v-model="nowRouter"
 | 
			
		||||
			spread
 | 
			
		||||
			class="my-custom-toggle"
 | 
			
		||||
			no-caps
 | 
			
		||||
			rounded
 | 
			
		||||
			unelevated
 | 
			
		||||
			toggle-color="primary"
 | 
			
		||||
			color="white"
 | 
			
		||||
			text-color="primary"
 | 
			
		||||
			:options="[
 | 
			
		||||
				{ label: '百度', value: '/baidu' },
 | 
			
		||||
				{ label: '高德', value: '/gaode' },
 | 
			
		||||
				{ label: '热力图', value: '/echart' },
 | 
			
		||||
			]" />
 | 
			
		||||
		<q-tabs v-model="tab" glossy>
 | 
			
		||||
			<q-tab name="mark" label="标点" />
 | 
			
		||||
			<q-tab name="ctrl" label="控制" />
 | 
			
		||||
		</q-tabs>
 | 
			
		||||
		<q-tab-panels v-model="tab" animated keep-alive>
 | 
			
		||||
			<!-- <q-tab-panel name="mark">
 | 
			
		||||
				<MarkMenu :marks="marks" @flyTo="flytoMark" @remove="removeMark"></MarkMenu>
 | 
			
		||||
			</q-tab-panel>
 | 
			
		||||
			<q-tab-panel name="ctrl">
 | 
			
		||||
				<CtrlMenu @change="ctrlMap"></CtrlMenu>
 | 
			
		||||
			</q-tab-panel> -->
 | 
			
		||||
		</q-tab-panels>
 | 
			
		||||
	</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="scss">
 | 
			
		||||
.logo {
 | 
			
		||||
  margin: unset;
 | 
			
		||||
  grid-area: var(--logoOrBar, logo);
 | 
			
		||||
  will-change: filter;
 | 
			
		||||
  transition: filter 300ms;
 | 
			
		||||
  align-self: center;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
}
 | 
			
		||||
	.logo {
 | 
			
		||||
		margin: unset;
 | 
			
		||||
		grid-area: var(--logoOrBar, logo);
 | 
			
		||||
		will-change: filter;
 | 
			
		||||
		transition: filter 300ms;
 | 
			
		||||
		align-self: center;
 | 
			
		||||
		text-align: center;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
.logo:hover {
 | 
			
		||||
  filter: drop-shadow(0 0 2em #646cffaa);
 | 
			
		||||
}
 | 
			
		||||
	.logo:hover {
 | 
			
		||||
		filter: drop-shadow(0 0 2em #646cffaa);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
.logo.vue:hover {
 | 
			
		||||
  filter: drop-shadow(0 0 2em #42b883aa);
 | 
			
		||||
}
 | 
			
		||||
	.logo.vue:hover {
 | 
			
		||||
		filter: drop-shadow(0 0 2em #42b883aa);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
.left {
 | 
			
		||||
  grid-area: left;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
	.left {
 | 
			
		||||
		grid-area: left;
 | 
			
		||||
		overflow: auto;
 | 
			
		||||
		user-select: none;
 | 
			
		||||
 | 
			
		||||
  .leftctl {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0px;
 | 
			
		||||
    left: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.bar {
 | 
			
		||||
  grid-area: bar;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
  margin-right: 20px;
 | 
			
		||||
}
 | 
			
		||||
		.leftctl {
 | 
			
		||||
			position: absolute;
 | 
			
		||||
			top: 0px;
 | 
			
		||||
			left: 10px;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	.main {
 | 
			
		||||
		grid-area: main;
 | 
			
		||||
		position: relative;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.bar {
 | 
			
		||||
		grid-area: bar;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: flex-end;
 | 
			
		||||
		margin-right: 20px;
 | 
			
		||||
	}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,70 +1,62 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { ref, onMounted, provide, watch } from 'vue'
 | 
			
		||||
import 'leaflet/dist/leaflet.css'
 | 
			
		||||
import useMap from '../hook/useMap.ts'
 | 
			
		||||
import { useResizeObserver } from '@vueuse/core'
 | 
			
		||||
import { setOptions } from 'leaflet'
 | 
			
		||||
	import { ref, onMounted, provide, watch } from 'vue';
 | 
			
		||||
	import 'leaflet/dist/leaflet.css';
 | 
			
		||||
	import useMap from '../hook/useMap.ts';
 | 
			
		||||
	import { useResizeObserver } from '@vueuse/core';
 | 
			
		||||
	import { setOptions } from 'leaflet';
 | 
			
		||||
 | 
			
		||||
const mapContainer = ref()
 | 
			
		||||
const { map, tileLayers, props: mapProps } = useMap(mapContainer, { use: 'gaode', zoom: 13 });
 | 
			
		||||
	const mapContainer = ref();
 | 
			
		||||
	const { map, tileLayers, props: mapProps } = useMap(mapContainer, { use: 'gaode', zoom: 13 });
 | 
			
		||||
 | 
			
		||||
// const config = defineModel<Object>()
 | 
			
		||||
	// const config = defineModel<Object>()
 | 
			
		||||
 | 
			
		||||
const slotProps = ref({
 | 
			
		||||
  contextmenu: {}
 | 
			
		||||
})
 | 
			
		||||
	const slotProps = ref({
 | 
			
		||||
		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(() => {
 | 
			
		||||
  map.value?.addEventListener("contextmenu", (e) => {
 | 
			
		||||
    slotProps.value.contextmenu = e
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
	watch({ center: mapProps.value.center }, ({ center }) => center && map.value?.flyTo(center));
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
watch({ center: mapProps.value.center }, ({ center }) => center && map.value?.flyTo(center))
 | 
			
		||||
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 })
 | 
			
		||||
	defineExpose({ changeMapFilter, setOptions });
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="mapContainer" ref="mapContainer" :style="{ '--mapFilter': mapFilter }">
 | 
			
		||||
    <slot v-if="map" v-bind="slotProps"></slot>
 | 
			
		||||
  </div>
 | 
			
		||||
	<div class="mapContainer" ref="mapContainer" :style="{ '--mapFilter': mapFilter }">
 | 
			
		||||
		<slot v-if="map" v-bind="slotProps"></slot>
 | 
			
		||||
	</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style  lang="scss">
 | 
			
		||||
.mapContainer {
 | 
			
		||||
  grid-area: map;
 | 
			
		||||
}
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
	.mapContainer {
 | 
			
		||||
		grid-area: map;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
body.body--dark {
 | 
			
		||||
  .mapFilter {
 | 
			
		||||
    filter: var(--mapFilter);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
	body.body--dark {
 | 
			
		||||
		.mapFilter {
 | 
			
		||||
			filter: var(--mapFilter);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
</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>
 | 
			
		||||
import { ref, watch } from 'vue';
 | 
			
		||||
import useConfig from '../../hook/useConfig';
 | 
			
		||||
import { MapType } from '../../lib/mapType';
 | 
			
		||||
const _configGetter = useConfig()
 | 
			
		||||
const maptype = ref<MapType>('gaode')
 | 
			
		||||
const data = ref(_configGetter('gaode'))
 | 
			
		||||
const emit = defineEmits(['change'])
 | 
			
		||||
 | 
			
		||||
watch(maptype, v => data.value = _configGetter(v))
 | 
			
		||||
watch(data, (v) => emit('change', v), { deep: true })
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
| 
						 | 
				
			
			@ -30,11 +25,11 @@ watch(data, (v) => emit('change', v), { deep: true })
 | 
			
		|||
        <q-item clickable v-close-popup>
 | 
			
		||||
            <q-item-section>
 | 
			
		||||
                <q-item-label>缩放控制</q-item-label>
 | 
			
		||||
                <q-item-label caption>
 | 
			
		||||
                <!-- <q-item-label caption>
 | 
			
		||||
                    <q-range :model-value="{ max: data.maxZoom, min: data.minZoom }"
 | 
			
		||||
                        @update:model-value="({ max, min }) => { data.maxZoom = max; data.minZoom = min; }" :min="3"
 | 
			
		||||
                        :max="19" color="deep-orange" label-always markers switch-label-side />
 | 
			
		||||
                </q-item-label>
 | 
			
		||||
                </q-item-label> -->
 | 
			
		||||
                <!-- TODO 控制多图层 ?? 缩放级别减少 直接控制路线等 -->
 | 
			
		||||
                <!-- TODO 地图不清楚 -->
 | 
			
		||||
            </q-item-section>
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +38,7 @@ watch(data, (v) => emit('change', v), { deep: true })
 | 
			
		|||
            <q-item-section>
 | 
			
		||||
                <q-item-label>访问地址</q-item-label>
 | 
			
		||||
                <q-item-label caption>
 | 
			
		||||
                    <q-input v-model="data.urlTemplate" />
 | 
			
		||||
                    <!-- <q-input v-model="data.urlTemplate" /> -->
 | 
			
		||||
                </q-item-label>
 | 
			
		||||
            </q-item-section>
 | 
			
		||||
        </q-item>
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +46,7 @@ watch(data, (v) => emit('change', v), { deep: true })
 | 
			
		|||
            <q-item-section>
 | 
			
		||||
                <q-item-label>失败图片</q-item-label>
 | 
			
		||||
                <q-item-label caption>
 | 
			
		||||
                    <q-input v-model="data.errorTileUrl" />
 | 
			
		||||
                    <!-- <q-input v-model="data.errorTileUrl" /> -->
 | 
			
		||||
                </q-item-label>
 | 
			
		||||
            </q-item-section>
 | 
			
		||||
        </q-item>
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +54,7 @@ watch(data, (v) => emit('change', v), { deep: true })
 | 
			
		|||
            <q-item-section>
 | 
			
		||||
                <q-item-label>缩放倍数</q-item-label>
 | 
			
		||||
                <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-section>
 | 
			
		||||
        </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 'proj4'
 | 
			
		||||
import 'proj4leaflet'
 | 
			
		||||
import L from 'leaflet';
 | 
			
		||||
import 'proj4';
 | 
			
		||||
import 'proj4leaflet';
 | 
			
		||||
import { MapType } from '../lib/mapType';
 | 
			
		||||
const _level = 19;
 | 
			
		||||
 | 
			
		||||
const crss: Record<MapType, L.CRS> = {
 | 
			
		||||
    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',
 | 
			
		||||
        {
 | 
			
		||||
            resolutions: function () {
 | 
			
		||||
                let level = 19
 | 
			
		||||
                var res = [];
 | 
			
		||||
                res[0] = Math.pow(2, 18);
 | 
			
		||||
                for (var i = 1; i < level; i++) {
 | 
			
		||||
                    res[i] = Math.pow(2, (18 - i))
 | 
			
		||||
                }
 | 
			
		||||
                return res;
 | 
			
		||||
            }(),
 | 
			
		||||
            origin: [0, 0],
 | 
			
		||||
            bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244])
 | 
			
		||||
        }),
 | 
			
		||||
    gaode: L.CRS.Simple,
 | 
			
		||||
    gaodeLine:L.CRS.EPSG4326
 | 
			
		||||
}
 | 
			
		||||
	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', {
 | 
			
		||||
		resolutions: (function () {
 | 
			
		||||
			let level = 19;
 | 
			
		||||
			var res = [];
 | 
			
		||||
			res[0] = Math.pow(2, 18);
 | 
			
		||||
			for (var i = 1; i < level; i++) {
 | 
			
		||||
				res[i] = Math.pow(2, 18 - i);
 | 
			
		||||
			}
 | 
			
		||||
			return res;
 | 
			
		||||
		})(),
 | 
			
		||||
		origin: [0, 0],
 | 
			
		||||
		bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244]),
 | 
			
		||||
	}),
 | 
			
		||||
	gaode: L.CRS.Simple,
 | 
			
		||||
	gaodeCharts: L.CRS.Simple,
 | 
			
		||||
	gaodeLine: L.CRS.EPSG4326,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 { 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 { use, init, ComposeOption } from 'echarts/core';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 地图接口 地图类型
 | 
			
		||||
 */
 | 
			
		||||
interface MapController {
 | 
			
		||||
import { ScatterChart, ScatterSeriesOption, EffectScatterChart, EffectScatterSeriesOption, HeatmapChart } from 'echarts/charts';
 | 
			
		||||
 | 
			
		||||
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 }
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
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 './style.scss'
 | 
			
		||||
import App from './App.vue'
 | 
			
		||||
import { createApp } from 'vue';
 | 
			
		||||
import './style.scss';
 | 
			
		||||
import App from './App.vue';
 | 
			
		||||
 | 
			
		||||
import { Quasar } from 'quasar'
 | 
			
		||||
import { Quasar } from 'quasar';
 | 
			
		||||
import { router } from './lib/router';
 | 
			
		||||
 | 
			
		||||
// 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/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, {
 | 
			
		||||
    plugins: {}, // import Quasar plugins and add here
 | 
			
		||||
})
 | 
			
		||||
myApp.mount('#app')
 | 
			
		||||
	plugins: {}, // import Quasar plugins and add here
 | 
			
		||||
});
 | 
			
		||||
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 {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  width: 100vw;
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  grid-template-areas: "logo bar"
 | 
			
		||||
    "left  map";
 | 
			
		||||
  grid-template-rows: 3em 1fr;
 | 
			
		||||
  grid-template-columns: var(--side, 20em) 1fr;
 | 
			
		||||
}
 | 
			
		||||
	display: grid;
 | 
			
		||||
	width: 100vw;
 | 
			
		||||
	height: 100vh;
 | 
			
		||||
	grid-template-areas:
 | 
			
		||||
		'logo bar'
 | 
			
		||||
		'left  main';
 | 
			
		||||
	grid-template-rows: 3em 1fr;
 | 
			
		||||
	grid-template-columns: var(--side, 20em) 1fr;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue