Compare commits
25 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
e26b939ebe | |
|
|
b0c4d472d7 | |
|
|
8f14e18318 | |
|
|
6a955b5921 | |
|
|
bcce2530aa | |
|
|
65ac2098ee | |
|
|
012458b47c | |
|
|
4b69292063 | |
|
|
d118c210f6 | |
|
|
a722837a11 | |
|
|
3ac4d6698a | |
|
|
0ac683a25e | |
|
|
b846f3cd40 | |
|
|
77aafac3e3 | |
|
|
863b7866cb | |
|
|
3f23fbad32 | |
|
|
889830aa1b | |
|
|
05c1116703 | |
|
|
4e2f51a0d2 | |
|
|
01e3800bea | |
|
|
7c49d9584e | |
|
|
7b0cf77b27 | |
|
|
449070ad2d | |
|
|
73607b7276 | |
|
|
26534c6360 |
2
.env
|
|
@ -1,5 +1,5 @@
|
|||
# title
|
||||
VITE_APP_TITLE = '智飞'
|
||||
VITE_APP_TITLE = '时空大数据平台'
|
||||
|
||||
# 端口号
|
||||
VITE_PORT = 3050
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ module.exports = {
|
|||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true
|
||||
es6: true,
|
||||
'vue/setup-compiler-macros': true
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
|
|
@ -285,7 +286,7 @@ module.exports = {
|
|||
/* 要求或禁止语句块之前的空格 */
|
||||
'space-before-blocks': [2, 'always'],
|
||||
/* 要求或禁止函数圆括号之前有一个空格 */
|
||||
'space-before-function-paren': [2, 'never'],
|
||||
// 'space-before-function-paren': [2, 'never'],
|
||||
/* 禁止或强制圆括号内的空格 */
|
||||
'space-in-parens': [2, 'never'],
|
||||
/* 要求中缀操作符周围有空格 */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import VueSetupExtend from 'vite-plugin-vue-setup-extend'
|
|||
import { unocss } from './unocss'
|
||||
import { configHtmlPlugin } from './html'
|
||||
import { configMockPlugin } from './mock'
|
||||
import cesium from 'vite-plugin-cesium'
|
||||
|
||||
export function createVitePlugins(viteEnv, isBuild) {
|
||||
const plugins = [
|
||||
|
|
@ -17,7 +18,8 @@ export function createVitePlugins(viteEnv, isBuild) {
|
|||
}),
|
||||
VueSetupExtend(),
|
||||
unocss(),
|
||||
configHtmlPlugin(viteEnv, isBuild)
|
||||
configHtmlPlugin(viteEnv, isBuild),
|
||||
cesium()
|
||||
]
|
||||
|
||||
viteEnv?.VITE_APP_USE_MOCK && plugins.push(configMockPlugin(isBuild))
|
||||
|
|
|
|||
32
index.html
|
|
@ -1,16 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/de/prismplayer/2.9.21/skins/default/aliplayer-min.css" />
|
||||
<script charset="utf-8" type="text/javascript" src="https://g.alicdn.com/de/prismplayer/2.9.21/aliplayer-h5-min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/de/prismplayer/2.9.21/skins/default/aliplayer-min.css" />
|
||||
|
||||
<script src='https://code.jquery.com/jquery-3.6.0.min.js'></script>
|
||||
<script charset="utf-8" type="text/javascript"
|
||||
src="https://g.alicdn.com/de/prismplayer/2.9.21/aliplayer-h5-min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
{
|
||||
"name": "vite_vue3",
|
||||
"version": "0.0.0",
|
||||
"globals": {
|
||||
"Cesium": true
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite --mode localhost",
|
||||
"build:test": "vite build --mode test && esno ./build/script",
|
||||
|
|
@ -14,12 +17,15 @@
|
|||
"@vicons/ionicons5": "^0.10.0",
|
||||
"ali-oss": "^6.17.1",
|
||||
"axios": "^0.26.1",
|
||||
"cesium": "^1.92.0",
|
||||
"dayjs": "^1.11.2",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.0.13",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"tinymce": "^5.10.2",
|
||||
"vite-plugin-cesium": "^1.2.22",
|
||||
"vue": "^3.2.16",
|
||||
"vue-jstree": "^2.1.6",
|
||||
"vue-router": "^4.0.14",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
|
|
@ -42,6 +48,7 @@
|
|||
"unocss": "^0.16.4",
|
||||
"unplugin-vue-components": "^0.18.5",
|
||||
"vite": "^2.6.4",
|
||||
"vite-plugin-cesium": "^1.2.22",
|
||||
"vite-plugin-html": "^2.1.2",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0"
|
||||
|
|
|
|||
35
src/App.vue
|
|
@ -7,29 +7,18 @@
|
|||
* @FilePath: \gis\src\App.vue
|
||||
-->
|
||||
<template>
|
||||
<n-config-provider :locale="zhCN" :date-locale="dateZhCN" inline-theme-disabled :theme-overrides="themeOverrides">
|
||||
<n-loading-bar-provider>
|
||||
<loading-bar />
|
||||
<n-dialog-provider>
|
||||
<dialog-content />
|
||||
<n-message-provider>
|
||||
<message-content />
|
||||
</n-message-provider>
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component" />
|
||||
</router-view>
|
||||
</n-dialog-provider>
|
||||
</n-loading-bar-provider>
|
||||
</n-config-provider>
|
||||
<div>
|
||||
<Dashboard />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { zhCN, dateZhCN } from 'naive-ui'
|
||||
import themeOverrides from '@/utils/ui/theme.js'
|
||||
import loadingBar from '@/components/LoadingBar/index.vue'
|
||||
import messageContent from '@/components/Message/index.vue'
|
||||
import dialogContent from '@/components/Dialog/index.vue'
|
||||
|
||||
<script>
|
||||
import Dashboard from './views/dashboard/index.vue'
|
||||
export default {
|
||||
components: {
|
||||
Dashboard
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
@ -38,5 +27,9 @@ import dialogContent from '@/components/Dialog/index.vue'
|
|||
.n-config-provider {
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-ms-user-select: none; /* IE 10+ and Edge */
|
||||
user-select: none; /* Standard syntax */
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
export default {
|
||||
x_pi: Math.PI * 3000.0 / 180.0,
|
||||
a: 6378245.0,
|
||||
ee: 0.00669342162296594323,
|
||||
/**
|
||||
* 百度坐标系(BD-09)与火星坐标系(GCJ-02)的转换
|
||||
* 即百度转谷歌,高德
|
||||
* @param bd_lon
|
||||
* @param bd_lat
|
||||
* @returns {*[]}
|
||||
*/
|
||||
bd09togcj02 (bd_lon, bd_lat) {
|
||||
const x = bd_lon - 0.0065
|
||||
const y = bd_lat - 0.006
|
||||
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi)
|
||||
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi)
|
||||
const gg_lng = z * Math.cos(theta)
|
||||
const gg_lat = z * Math.sin(theta)
|
||||
return [gg_lng, gg_lat]
|
||||
},
|
||||
/**
|
||||
* 火星坐标系(GCJ-02)与百度坐标系(BD-09)的转换
|
||||
* 即谷歌,高德转百度
|
||||
* @param lng
|
||||
* @param lat
|
||||
* @returns {*[]}
|
||||
*/
|
||||
gcj02tobd09 (lng, lat) {
|
||||
const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * this.x_pi)
|
||||
const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * this.x_pi)
|
||||
const bd_lng = z * Math.cos(theta) + 0.0065
|
||||
const bd_lat = z * Math.sin(theta) + 0.006
|
||||
return [bd_lng, bd_lat]
|
||||
},
|
||||
/**
|
||||
* WGS84转GCj02
|
||||
* @param lng
|
||||
* @param lat
|
||||
* @returns {*[]}
|
||||
*/
|
||||
wgs84togcj02 (lng, lat) {
|
||||
if (this.out_of_china(lng, lat)) {
|
||||
return [lng, lat]
|
||||
} else {
|
||||
let dlat = this.transformlat(lng - 105.0, lat - 35.0)
|
||||
let dlng = this.transformlng(lng - 105.0, lat - 35.0)
|
||||
const radlat = lat / 180.0 * Math.PI
|
||||
let magic = Math.sin(radlat)
|
||||
magic = 1 - this.ee * magic * magic
|
||||
const sqrtmagic = Math.sqrt(magic)
|
||||
dlat = (dlat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtmagic) * Math.PI)
|
||||
dlng = (dlng * 180.0) / (this.a / sqrtmagic * Math.cos(radlat) * Math.PI)
|
||||
const mglat = lat + dlat
|
||||
const mglng = lng + dlng
|
||||
return [mglng, mglat]
|
||||
}
|
||||
},
|
||||
/**
|
||||
* GCJ02转换为WGS84
|
||||
* @param lng
|
||||
* @param lat
|
||||
* @returns {*[]}
|
||||
*/
|
||||
gcj02towgs84 (lng, lat) {
|
||||
if (this.out_of_china(lng, lat)) {
|
||||
return [lng, lat]
|
||||
} else {
|
||||
let dlat = this.transformlat(lng - 105.0, lat - 35.0)
|
||||
let dlng = this.transformlng(lng - 105.0, lat - 35.0)
|
||||
const radlat = lat / 180.0 * Math.PI
|
||||
let magic = Math.sin(radlat)
|
||||
magic = 1 - this.ee * magic * magic
|
||||
const sqrtmagic = Math.sqrt(magic)
|
||||
dlat = (dlat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtmagic) * Math.PI)
|
||||
dlng = (dlng * 180.0) / (this.a / sqrtmagic * Math.cos(radlat) * Math.PI)
|
||||
const mglat = lat + dlat
|
||||
const mglng = lng + dlng
|
||||
return [lng * 2 - mglng, lat * 2 - mglat]
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 百度坐标系转wgs84坐标系
|
||||
* @param {*} lng
|
||||
* @param {*} lat
|
||||
* @returns
|
||||
*/
|
||||
bd09towgs84 (lng, lat) {
|
||||
// 百度坐标系先转为火星坐标系
|
||||
const gcj02 = this.bd09togcj02(lng, lat)
|
||||
// 火星坐标系转wgs84坐标系
|
||||
const result = this.gcj02towgs84(gcj02[0], gcj02[1])
|
||||
return result
|
||||
},
|
||||
/**
|
||||
* wgs84坐标系转百度坐标系
|
||||
* @param {*} lng
|
||||
* @param {*} lat
|
||||
* @returns
|
||||
*/
|
||||
wgs84tobd09 (lng, lat) {
|
||||
// wgs84先转为火星坐标系
|
||||
const gcj02 = this.wgs84togcj02(lng, lat)
|
||||
// 火星坐标系转百度坐标系
|
||||
const result = this.gcj02tobd09(gcj02[0], gcj02[1])
|
||||
return result
|
||||
},
|
||||
transformlat (lng, lat) {
|
||||
let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng))
|
||||
ret += (20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 * Math.sin(2.0 * lng * Math.PI)) * 2.0 / 3.0
|
||||
ret += (20.0 * Math.sin(lat * Math.PI) + 40.0 * Math.sin(lat / 3.0 * Math.PI)) * 2.0 / 3.0
|
||||
ret += (160.0 * Math.sin(lat / 12.0 * Math.PI) + 320 * Math.sin(lat * Math.PI / 30.0)) * 2.0 / 3.0
|
||||
return ret
|
||||
},
|
||||
transformlng (lng, lat) {
|
||||
let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng))
|
||||
ret += (20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 * Math.sin(2.0 * lng * Math.PI)) * 2.0 / 3.0
|
||||
ret += (20.0 * Math.sin(lng * Math.PI) + 40.0 * Math.sin(lng / 3.0 * Math.PI)) * 2.0 / 3.0
|
||||
ret += (150.0 * Math.sin(lng / 12.0 * Math.PI) + 300.0 * Math.sin(lng / 30.0 * Math.PI)) * 2.0 / 3.0
|
||||
return ret
|
||||
},
|
||||
/**
|
||||
* 判断是否在国内,不在国内则不做偏移
|
||||
* @param lng
|
||||
* @param lat
|
||||
* @returns {boolean}
|
||||
*/
|
||||
out_of_china (lng, lat) {
|
||||
return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false)
|
||||
},
|
||||
/**
|
||||
* 经纬度转墨卡托
|
||||
* @param poi 经纬度
|
||||
* @returns {{}}
|
||||
* @private
|
||||
*/
|
||||
LngLatToMercator (lng, lat) {
|
||||
const mercator = {}
|
||||
const earthRad = 6378137.0
|
||||
mercator.x = lng * Math.PI / 180 * earthRad
|
||||
const a = lat * Math.PI / 180
|
||||
mercator.y = earthRad / 2 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)))
|
||||
|
||||
return [mercator.x, mercator.y]
|
||||
},
|
||||
|
||||
/**
|
||||
* 墨卡托转经纬度
|
||||
* @param poi 墨卡托
|
||||
* @returns {{}}
|
||||
* @private
|
||||
*/
|
||||
MercatorToLngLat (x, y) {
|
||||
const lnglat = {}
|
||||
lnglat.lng = x / 20037508.34 * 180
|
||||
const mmy = y / 20037508.34 * 180
|
||||
lnglat.lat = 180 / Math.PI * (2 * Math.atan(Math.exp(mmy * Math.PI / 180)) - Math.PI / 2)
|
||||
// console.log(lnglat, '经纬度')
|
||||
return [lnglat.lng, lnglat.lat]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
import axios from 'axios'
|
||||
import { removeLayer } from './service'
|
||||
|
||||
export default {
|
||||
sourceId: '',
|
||||
layerId: '',
|
||||
clusterCountId: '',
|
||||
unclusteredId: '',
|
||||
/**
|
||||
* 加载聚合图层
|
||||
* @param map
|
||||
* @param url String 传递的后端服务地址
|
||||
* @param sourceOption Object 只需传clusterMaxZoom和clusterRadius
|
||||
* @param layerOption Object 只需传filter和paint
|
||||
* @param clusterLayerOption Object 只需传filter layout paint
|
||||
* @param unclusterLayerOption Object 只需传filter paint
|
||||
*/
|
||||
loadAggregationGraph (map, geodata, sourceOption, layerOption, clusterLayerOption, unclusterLayerOption) {
|
||||
// axios.get(url).then((res) => {
|
||||
// if (res) {
|
||||
|
||||
const data = geodata
|
||||
const sourceId = 'sourceis_1'
|
||||
const layerId = 'layerId_1'
|
||||
// const sourceId = res.data.crs.properties.name
|
||||
// this.sourceId = sourceId
|
||||
// const layerId = sourceId + 'layer'
|
||||
// this.layerId = layerId
|
||||
// const clusterCountId = sourceId + 'clusterCount'
|
||||
// this.clusterCountId = clusterCountId
|
||||
// const unclusteredId = sourceId + 'unclustered'
|
||||
// this.unclusteredId = unclusteredId
|
||||
map.addSource(sourceId, {
|
||||
type: 'geojson',
|
||||
data: data,
|
||||
cluster: true,
|
||||
clusterMaxZoom: sourceOption.clusterMaxZoom ? sourceOption.clusterMaxZoom : 14, // 最大缩放到群集点
|
||||
clusterRadius: sourceOption.clusterRadius ? sourceOption.clusterRadius : 50// 每一组点的半径
|
||||
})
|
||||
// 外围有数字的图层,加晕染
|
||||
map.addLayer({
|
||||
id: layerId,
|
||||
type: 'circle',
|
||||
source: sourceId,
|
||||
// filter: ['has', 'poi_id'],
|
||||
filter: layerOption.filter,
|
||||
paint: layerOption.paint ? layerOption.paint : {
|
||||
// 蓝色,当点数小于100时为20px圆
|
||||
// 点计数在100到750之间时为黄色,21px圆
|
||||
// 点计数大于或等于750时为22像素的粉红色圆圈
|
||||
'circle-color': [
|
||||
'step',
|
||||
['get', 'poi_id'],
|
||||
'rgba(81,187,214,0.8)',
|
||||
100,
|
||||
'rgba(241,240,117,0.8)',
|
||||
750,
|
||||
'rgba(242,140,177,0.8)'
|
||||
],
|
||||
'circle-radius': [
|
||||
'step',
|
||||
['get', 'poi_id'],
|
||||
20, // 蓝色,当点数小于100时为20px圆
|
||||
100, // 对应上面circle-color数字,意思为100以内
|
||||
21, // 点计数在100到750之间为黄色,意思为750以内
|
||||
750, // 对应上面circle-color数字,意思为750以内
|
||||
22// 点计数大于或等于750时为22像素的粉红色圆圈
|
||||
],
|
||||
// 这个是外边框的颜色 circle-stroke-color这个对应了上面circle-color
|
||||
'circle-stroke-color': [
|
||||
'step',
|
||||
['get', 'poi_id'],
|
||||
'rgba(81,187,214,0.2)',
|
||||
100,
|
||||
'rgba(241,240,117,0.2)',
|
||||
750,
|
||||
'rgba(242,140,177,0.2)'
|
||||
],
|
||||
// 这个是外边框渲染的范围
|
||||
'circle-stroke-width': [
|
||||
'step',
|
||||
['get', 'poi_id'],
|
||||
5, // 蓝色晕染长度,当点数小于100时为5px晕染
|
||||
100, // 对应上面circle-color数字,意思为100以内
|
||||
6, // 点计数在100到750之间时为黄色,6px晕染
|
||||
750, // 对应上面circle-color数字,意思为750以内
|
||||
7// 点计数大于或等于750时为7px像素的粉红色晕染
|
||||
]
|
||||
}
|
||||
})
|
||||
// 聚合图圆圈中的数字
|
||||
map.addLayer({
|
||||
id: 'clusterCountId',
|
||||
type: 'symbol',
|
||||
source: sourceId,
|
||||
// filter: ['has', 'poi_id'],
|
||||
filter: clusterLayerOption.filter,
|
||||
layout: clusterLayerOption.layout,
|
||||
// layout:{
|
||||
// 'text-field': '{poi_id}', // 文本内容来源字段
|
||||
// 'text-font': ['Microsoft YaHei'],
|
||||
// 'text-size': 12
|
||||
// }
|
||||
paint: clusterLayerOption.paint ? clusterLayerOption.paint : {
|
||||
'text-color': 'hsl(0, 0%, 100%)',
|
||||
'text-halo-color': '#54c081',
|
||||
'text-halo-width': 100
|
||||
}
|
||||
})
|
||||
// 聚合图中没有数字的显示小圆点
|
||||
map.addLayer({
|
||||
id: 'unclusteredId',
|
||||
type: 'circle',
|
||||
source: sourceId,
|
||||
// filter: ['!', ['has', 'poi_id']],
|
||||
filter: unclusterLayerOption.filter,
|
||||
paint: unclusterLayerOption.paint ? unclusterLayerOption.paint : {
|
||||
'circle-color': '#11b4da',
|
||||
'circle-radius': 4,
|
||||
'circle-stroke-width': 1,
|
||||
'circle-stroke-color': '#fff'
|
||||
}
|
||||
// paint: {
|
||||
// 'circle-color': '#11b4da',
|
||||
// 'circle-radius': 4,
|
||||
// 'circle-stroke-width': 1,
|
||||
// 'circle-stroke-color': '#fff'
|
||||
// }
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 移除聚合图层
|
||||
*/
|
||||
removeLayer (map) {
|
||||
map.removeLayer(this.layerId)
|
||||
map.removeLayer(this.clusterCountId)
|
||||
map.removeLayer(this.unclusteredId)
|
||||
map.removeSource(this.sourceId)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
import axios from 'axios'
|
||||
|
||||
/**
|
||||
* @description:加载WMS服务
|
||||
* @param map
|
||||
* @param url String
|
||||
* @param option Object
|
||||
*
|
||||
*/
|
||||
export function loadWMS (map, url, option) {
|
||||
if (url) {
|
||||
const id = url.split('layer=')[1]
|
||||
const source = map.getSource(option.source)
|
||||
const layer = map.getLayer(option.id)
|
||||
if (source === undefined && layer === undefined) {
|
||||
map.addSource(option.source, {
|
||||
|
||||
type: 'raster',
|
||||
tiles: [url],
|
||||
tileSize: 512
|
||||
})
|
||||
map.addLayer({
|
||||
id: id,
|
||||
type: option.type,
|
||||
source: option.source,
|
||||
paint: option.paint
|
||||
})
|
||||
} else {
|
||||
alert('该图层已存在!请勿重复添加。')
|
||||
}
|
||||
} else {
|
||||
alert('请输入WMS服务地址!')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:根据id删除指定图层
|
||||
* @param map
|
||||
* @param url
|
||||
*/
|
||||
export function removeLayer (map, url, option) {
|
||||
const id = url.split('layer=')[1]
|
||||
|
||||
map.removeLayer(id)
|
||||
map.removeSource(option.source)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:移除WFS服务图层
|
||||
* @param {*} map
|
||||
* @param {*} url
|
||||
* @param {*} option
|
||||
*/
|
||||
export function removeWFSLayer (map, option) {
|
||||
map.removeLayer(option.id)
|
||||
map.removeSource(option.id)
|
||||
}
|
||||
/**
|
||||
* @description:加载WFS服务
|
||||
* @param url String
|
||||
* @param option Object
|
||||
*/
|
||||
export function loadWFSLayer (map, url, option) {
|
||||
const source = map.getSource(option.id)
|
||||
const layer = map.getLayer(option.id)
|
||||
if (source === undefined && layer === undefined) {
|
||||
if (url) {
|
||||
axios.get(url).then((res) => {
|
||||
const features = res.data.features
|
||||
// console.log(features, "元素层");
|
||||
// let layerType = null;
|
||||
// let geometryType = features[0].geometry.type;
|
||||
const addLayerOption = {
|
||||
id: option.id,
|
||||
type: option.type,
|
||||
source: {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
features: features
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
visibility: 'visible'
|
||||
},
|
||||
paint: option.paint
|
||||
}
|
||||
// if (geometryType == "MultiLineString") {
|
||||
// addLayerOption.type = "line";
|
||||
// } else if (geometryType == "Line") {
|
||||
// addLayerOption.type = "line";
|
||||
// } else if (geometryType == "Point") {
|
||||
// addLayerOption.type = "circle";
|
||||
// } else if (geometryType == " MultiPoint") {
|
||||
// addLayerOption.type = "circle";
|
||||
// } else if (geometryType == "Polygon") {
|
||||
// addLayerOption.type = "fill";
|
||||
// addLayerOption.paint = {
|
||||
// "fill-color": "rgba(200,100,240,0.4)",
|
||||
// "fill-outline-color": "rgba(200,100,240,1)",
|
||||
// };
|
||||
// } else if (geometryType == "MultiPolygon") {
|
||||
// addLayerOption.type = "fill";
|
||||
// addLayerOption.paint = {
|
||||
// "fill-color": "rgba(200,100,240,0.4)",
|
||||
// "fill-outline-color": "rgba(200,100,240,1)",
|
||||
// };
|
||||
// }
|
||||
|
||||
map.addLayer(addLayerOption)
|
||||
})
|
||||
} else {
|
||||
alert('请输入WFS地址!')
|
||||
}
|
||||
} else {
|
||||
alert('该图层已存在!请勿重复添加')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:加载WMTS服务
|
||||
* @param map
|
||||
* @param url String
|
||||
* @param option Object
|
||||
*/
|
||||
export function loadWMTS (map, url, option) {
|
||||
const source = map.getSource(option.source)
|
||||
const layer = map.getLayer(option.id)
|
||||
if (source === undefined && layer === undefined) {
|
||||
if (url) {
|
||||
const id = url.split('layer=')[1]
|
||||
map.addSource(option.source, {
|
||||
type: 'raster',
|
||||
tiles: [url],
|
||||
|
||||
tileSize: 256
|
||||
})
|
||||
map.addLayer({
|
||||
id: id,
|
||||
type: option.type,
|
||||
source: option.source,
|
||||
paint: option.paint
|
||||
})
|
||||
} else {
|
||||
alert('请输入WMTS服务地址!')
|
||||
}
|
||||
} else {
|
||||
alert('该图层已存在!请勿重复添加。')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:加载TMS服务
|
||||
* @param map
|
||||
* @param url String
|
||||
* @param option Object
|
||||
*/
|
||||
export function loadTMSLayer (map, url, option) {
|
||||
const source = map.getSource(option.source)
|
||||
const layer = map.getLayer(option.id)
|
||||
if (source === undefined && layer === undefined) {
|
||||
if (url) {
|
||||
const id = url.split('layer=')[1]
|
||||
map.addSource(option.source, {
|
||||
type: 'raster',
|
||||
tiles: [url],
|
||||
scheme: 'tms',
|
||||
tileSize: 256
|
||||
})
|
||||
map.addLayer({
|
||||
id: id,
|
||||
type: option.type,
|
||||
source: option.source,
|
||||
paint: option.paint
|
||||
})
|
||||
} else {
|
||||
alert('请输入TMS服务地址!')
|
||||
}
|
||||
} else {
|
||||
alert('该图层已存在!请勿重复添加')
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 244 B |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 865 B |
|
After Width: | Height: | Size: 623 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 628 B |
|
After Width: | Height: | Size: 564 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 499 B |
|
After Width: | Height: | Size: 471 B |
|
After Width: | Height: | Size: 296 B |
|
After Width: | Height: | Size: 244 B |
|
After Width: | Height: | Size: 155 B |
|
After Width: | Height: | Size: 123 B |
|
After Width: | Height: | Size: 164 B |
|
After Width: | Height: | Size: 163 B |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 462 B |
|
After Width: | Height: | Size: 599 B |
|
After Width: | Height: | Size: 437 B |
|
After Width: | Height: | Size: 547 B |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
<template>
|
||||
<div class="searchOption">
|
||||
<div>
|
||||
<input id="keyword"
|
||||
v-model="searchStyle"
|
||||
type="radio"
|
||||
value="keyword">
|
||||
<label for="keyword">按关键字搜索</label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="lnglat"
|
||||
v-model="searchStyle"
|
||||
type="radio"
|
||||
value="lnglat">
|
||||
<label for="lnglat">按经纬度搜索</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<img src="@/assets/img/location.png">
|
||||
<input v-model="searchContent"
|
||||
type="text"
|
||||
placeholder="请输入搜索位置/坐标"
|
||||
@input="searchPlace()">
|
||||
<img src="@/assets/img/search.png"
|
||||
@click="showList()">
|
||||
</div>
|
||||
<div ref="locationList"
|
||||
class="my-click-transition">
|
||||
<li v-for="(item,index) in localList"
|
||||
:key="index"
|
||||
class="itemLocation"
|
||||
@click="flyTo(item.geometry,item.id)"><img src="@/assets/img/location.png">
|
||||
<span class="locationName">{{ item.name }}</span>
|
||||
<span class="locationAddress">{{ item.address }}</span>
|
||||
</li>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
searchType: '',
|
||||
searchStyle: 'keyword',
|
||||
searchContent: '',
|
||||
localList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showList () {
|
||||
// if (this.$refs.locationList.className == 'my-click-transition') {
|
||||
// this.$refs.locationList.className = 'locationList'
|
||||
// } else {
|
||||
// this.$refs.locationList.className = 'my-click-transition'
|
||||
// }
|
||||
},
|
||||
// 实时查询
|
||||
searchPlace () {
|
||||
let url
|
||||
if (this.searchStyle == 'keyword') {
|
||||
console.log('111')
|
||||
url = 'http://192.168.11.35:8500/thmap/poi/searchFeat/keywords?keywords=' + this.searchContent
|
||||
} else {
|
||||
console.log('222')
|
||||
const arr = this.searchContent.split(',')
|
||||
url = 'http://192.168.11.35:8500/thmap/poi/searchFeat/keywords?lat=' + arr[0] + '&lng=' + arr[1] + '&distance=' + 50
|
||||
}
|
||||
|
||||
axios.get(url).then((res) => {
|
||||
// console.log(res.data.data.datas, '数据集')
|
||||
if (res.data.data.datas) {
|
||||
// console.log('有数据了')
|
||||
this.$refs.locationList.className = 'locationList'
|
||||
this.localList = res.data.data.datas
|
||||
} else {
|
||||
this.$refs.locationList.className = 'my-click-transition'
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
flyTo (lonLatInfo, id) {
|
||||
const Arr = lonLatInfo.split(' ')
|
||||
|
||||
// const Num1 = parseFloat(Arr[1])
|
||||
const Num1 = Number(Arr[1].match(/\d+(\.\d+)?/g)[0])
|
||||
const Num2 = Number(Arr[2].match(/\d+(\.\d+)?/g)[0])
|
||||
const Arr1 = [Num1, Num2]
|
||||
// console.log(this.$parent, '父组件')
|
||||
this.$parent.flyTo(Arr1, id)
|
||||
// // console.log(Arr1, '点位信息')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.searchOption {
|
||||
width: 300px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 15px;
|
||||
line-height: 50px;
|
||||
input {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
width: 300px;
|
||||
height: 45px;
|
||||
padding: 10px 10px;
|
||||
background-color: rgba(245, 245, 245, 0.8);
|
||||
box-shadow: 0px 15px 10px -15px #565656;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
input {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
// 位置列表
|
||||
.locationList {
|
||||
margin-top: 5px;
|
||||
width: 300px;
|
||||
// height: 100px;
|
||||
background-color: rgba(245, 245, 245, 0.8);
|
||||
transition: height 0.5s ease-in;
|
||||
-webkit-transform: height 0.5s ease-in;
|
||||
}
|
||||
.my-click-transition {
|
||||
overflow: hidden;
|
||||
margin-top: 5px;
|
||||
transition: height 0.5s ease-out;
|
||||
-webkit-transform: height 0.5s ease-out;
|
||||
height: 0;
|
||||
width: 300px;
|
||||
background-color: rgba(245, 245, 245, 0.8);
|
||||
}
|
||||
.itemLocation {
|
||||
width: 300px;
|
||||
height: 32px;
|
||||
position: relative;
|
||||
padding-left: 32px;
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
line-height: 32px;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
img {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 0px;
|
||||
}
|
||||
.locationName {
|
||||
color: #0082e5;
|
||||
}
|
||||
.locationAddress {
|
||||
color: #565656;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import '@/styles/index.scss'
|
||||
import 'uno.css'
|
||||
import axios from 'axios'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { setupRouter } from '@/router'
|
||||
|
|
@ -7,6 +8,7 @@ import { setupStore } from '@/store'
|
|||
|
||||
import App from './App.vue'
|
||||
|
||||
|
||||
function setupApp() {
|
||||
const app = createApp(App)
|
||||
|
||||
|
|
@ -14,6 +16,7 @@ function setupApp() {
|
|||
setupRouter(app)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
}
|
||||
|
||||
setupApp()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,717 @@
|
|||
<template>
|
||||
|
||||
<div id="cesiumContainer" class="cesium-viewer" />
|
||||
|
||||
<!-- 地图工具栏 -->
|
||||
<ToolBar @tool="handleToolBar" />
|
||||
|
||||
<!-- 图层管理 -->
|
||||
<LayerManage />
|
||||
|
||||
<!-- 底图切换 -->
|
||||
<!-- <MapSwitch /> -->
|
||||
|
||||
<!-- 版权、位置 -->
|
||||
<CopyRight :location="mouseLocation" />
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { onMounted, reactive, toRefs, ref } from 'vue'
|
||||
import * as Cesium from 'cesium'
|
||||
// 地图工具组件
|
||||
import ToolBar from './maptools/ToolBar.vue'
|
||||
import LayerManage from './maptools/LayerManage.vue'
|
||||
import MapSwitch from './maptools/MapSwitch.vue'
|
||||
import CopyRight from './maptools/CopyRight.vue'
|
||||
export default {
|
||||
components: { ToolBar, LayerManage, MapSwitch, CopyRight },
|
||||
emits: ['complete-measure'],
|
||||
setup(props, { emit }) {
|
||||
const measureRef = ref()
|
||||
const data = reactive({
|
||||
viewer: null,
|
||||
scene: null,
|
||||
// 鼠标位置
|
||||
mouseLocation: {
|
||||
lng: 0,
|
||||
lat: 0
|
||||
// height: 0
|
||||
},
|
||||
handler: null,
|
||||
// 蓝底图
|
||||
arcMap: null,
|
||||
// 影像图
|
||||
imgMap: null,
|
||||
// 路网
|
||||
roadNet: null,
|
||||
// 距离量测
|
||||
measureLinePos: [],
|
||||
measureLineEntities: [],
|
||||
// 面积量测
|
||||
measureAreaPos: [],
|
||||
measureAreaEntities: [],
|
||||
// 高度测量
|
||||
measureHeightPos: [],
|
||||
measureHeightEntities: []
|
||||
})
|
||||
|
||||
const initMap = () => {
|
||||
data.viewer = new Cesium.Viewer('cesiumContainer', {
|
||||
// 右上角 搜索
|
||||
geocoder: false,
|
||||
// 右上角 Home
|
||||
homeButton: false,
|
||||
// 右上角 2D/3D切换
|
||||
sceneModePicker: false,
|
||||
// 右上角 地形
|
||||
baseLayerPicker: false,
|
||||
// 右上角 Help
|
||||
navigationHelpButton: false,
|
||||
// 左下角 圆盘动画控件
|
||||
animation: false,
|
||||
// 时间轴
|
||||
timeline: false,
|
||||
// 右小角 全屏
|
||||
fullscreenButton: false,
|
||||
vrButton: false,
|
||||
// 点击要素后提示信息
|
||||
infoBox: false,
|
||||
selectionIndicator: false,
|
||||
// 每个几何实例将只能以3D渲染以节省GPU内存
|
||||
scene3DOnly: false,
|
||||
// 关闭地球光环
|
||||
skyAtmosphere: false,
|
||||
// 初始化底图
|
||||
imageryProvider: new Cesium.SingleTileImageryProvider({
|
||||
url: 'https://gisdata.t-aaron.com/GlobalBkLayer.jpg'
|
||||
}),
|
||||
shouldAnimate: true,
|
||||
orderIndependentTranslucency: false,
|
||||
contextOptions: {
|
||||
webgl: {
|
||||
alpha: true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
data.scene = data.viewer.scene
|
||||
|
||||
// 去除版权信息
|
||||
data.viewer._cesiumWidget._creditContainer.style.display = 'none'
|
||||
|
||||
// 取消双击事件-追踪该位置
|
||||
data.viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
|
||||
Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
|
||||
)
|
||||
|
||||
// //初始化视角
|
||||
data.viewer.camera.position = {
|
||||
x: -2610445.7358946367, y: 4754512.985081314, z: 3344995.579662871
|
||||
}
|
||||
data.viewer.camera.setView({
|
||||
orientation: {
|
||||
heading: 3.2814040916499394,
|
||||
pitch: -0.5626895458436971,
|
||||
roll: 6.283183261091251
|
||||
}
|
||||
})
|
||||
|
||||
// 添加二维底图
|
||||
// arcgis蓝底图
|
||||
// data.arcMap = data.viewer.imageryLayers.addImageryProvider(
|
||||
// new Cesium.ArcGisMapServerImageryProvider({
|
||||
// url: 'http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer',
|
||||
// maximumLevel: 18
|
||||
// })
|
||||
// )
|
||||
// data.arcMap.show = false
|
||||
|
||||
// 天地图影像图
|
||||
const imgLayer = new Cesium.UrlTemplateImageryProvider({
|
||||
url: 'https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=f634525a82da65f715d168d7ba1899c0',
|
||||
maximumLevel: 18
|
||||
})
|
||||
data.imgMap = data.viewer.imageryLayers.addImageryProvider(imgLayer)
|
||||
|
||||
getMouseLocation()
|
||||
|
||||
load3DTiles('https://gisdata.t-aaron.com/b3dm/park/tileset.json')
|
||||
}
|
||||
|
||||
const handleToolBar = (info) => {
|
||||
console.log(info)
|
||||
}
|
||||
|
||||
// 销毁handler事件
|
||||
const destoryHandler = () => {
|
||||
data.handler = data.handler && data.handler.destroy()
|
||||
}
|
||||
|
||||
const clearMeasure = () => {
|
||||
clearMeasureLine()
|
||||
clearMeasureArea()
|
||||
clearMeasureHeight()
|
||||
emit('complete-measure')
|
||||
}
|
||||
|
||||
// 清除测距
|
||||
const clearMeasureLine = () => {
|
||||
data.measureLinePos = []
|
||||
if (data.measureLineEntities.length > 0) {
|
||||
data.measureLineEntities.map(entity => {
|
||||
data.viewer.entities.remove(entity)
|
||||
})
|
||||
data.measureLineEntities = []
|
||||
}
|
||||
}
|
||||
|
||||
// 测距
|
||||
const measureLine = () => {
|
||||
// 先清除上次绘制的线
|
||||
clearMeasureLine()
|
||||
|
||||
var poly = null
|
||||
var distance = 0
|
||||
var cartesian = null
|
||||
|
||||
// 鼠标事件
|
||||
data.handler = new Cesium.ScreenSpaceEventHandler(data.viewer.scene.canvas)
|
||||
|
||||
data.handler.setInputAction(function (movement) {
|
||||
cartesian = data.viewer.scene.camera.pickEllipsoid(movement.endPosition, data.viewer.scene.globe.ellipsoid)
|
||||
if (data.measureLinePos.length > 1) {
|
||||
// Cesium.defined: 如果定义了对象,则返回true,否则返回false。
|
||||
if (!Cesium.defined(poly)) {
|
||||
const polyline = new Cesium.Entity({
|
||||
name: '直线',
|
||||
polyline: {
|
||||
positions: new Cesium.CallbackProperty(() => {
|
||||
return data.measureLinePos
|
||||
}, false),
|
||||
material: Cesium.Color.CHARTREUSE,
|
||||
width: 10,
|
||||
clampToGround: true
|
||||
}
|
||||
})
|
||||
poly = data.viewer.entities.add(polyline)
|
||||
data.measureLineEntities.push(poly)
|
||||
} else {
|
||||
data.measureLinePos.pop()
|
||||
data.measureLinePos.push(cartesian)
|
||||
}
|
||||
distance = getSpaceDistance(data.measureLinePos)
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
||||
|
||||
data.handler.setInputAction(function (movement) {
|
||||
cartesian = data.viewer.scene.camera.pickEllipsoid(movement.position, data.viewer.scene.globe.ellipsoid)
|
||||
if (data.measureLinePos.length === 0) {
|
||||
// 第一次先传入两个点, 生成 线entity
|
||||
data.measureLinePos.push(cartesian)
|
||||
}
|
||||
data.measureLinePos.push(cartesian)
|
||||
// 在三维场景中添加Label
|
||||
var textDisance = distance + '米'
|
||||
const label = data.viewer.entities.add({
|
||||
name: '空间直线距离',
|
||||
position: data.measureLinePos[data.measureLinePos.length - 1],
|
||||
point: {
|
||||
pixelSize: 5,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 2
|
||||
},
|
||||
label: {
|
||||
text: textDisance,
|
||||
font: '18px sans-serif',
|
||||
fillColor: Cesium.Color.GOLD,
|
||||
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||||
outlineWidth: 2,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
pixelOffset: new Cesium.Cartesian2(20, -20)
|
||||
}
|
||||
})
|
||||
data.measureLineEntities.push(label)
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
||||
|
||||
data.handler.setInputAction(function (movement) {
|
||||
data.handler = data.handler && data.handler.destroy()
|
||||
// 最后一个点无效
|
||||
data.measureLinePos.pop()
|
||||
// 结束绘制
|
||||
emit('complete-measure')
|
||||
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
||||
|
||||
// 空间两点距离计算函数
|
||||
function getSpaceDistance(positions) {
|
||||
var distance = 0
|
||||
for (var i = 0; i < positions.length - 1; i++) {
|
||||
var point1cartographic = Cesium.Cartographic.fromCartesian(
|
||||
positions[i]
|
||||
)
|
||||
var point2cartographic = Cesium.Cartographic.fromCartesian(
|
||||
positions[i + 1]
|
||||
)
|
||||
/** 根据经纬度计算出距离**/
|
||||
var geodesic = new Cesium.EllipsoidGeodesic()
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic)
|
||||
var s = geodesic.surfaceDistance
|
||||
distance = distance + s
|
||||
}
|
||||
return distance.toFixed(2)
|
||||
}
|
||||
}
|
||||
|
||||
// 清除测面
|
||||
const clearMeasureArea = () => {
|
||||
data.measureAreaPos = []
|
||||
if (data.measureAreaEntities.length > 0) {
|
||||
data.measureAreaEntities.map(entity => {
|
||||
data.viewer.entities.remove(entity)
|
||||
})
|
||||
data.measureAreaEntities = []
|
||||
}
|
||||
}
|
||||
|
||||
// 测面
|
||||
const measureArea = () => {
|
||||
// 清除上次绘制的面
|
||||
clearMeasureArea()
|
||||
|
||||
var tempPoints = []
|
||||
var poly = null
|
||||
var cartesian = null
|
||||
|
||||
// 鼠标事件
|
||||
data.handler = new Cesium.ScreenSpaceEventHandler(data.viewer.scene.canvas)
|
||||
|
||||
data.handler.setInputAction(function (movement) {
|
||||
const ray = data.viewer.camera.getPickRay(movement.endPosition)
|
||||
cartesian = data.viewer.scene.globe.pick(ray, data.viewer.scene)
|
||||
if (data.measureAreaPos.length > 1) {
|
||||
if (!Cesium.defined(poly)) {
|
||||
const polygon = new Cesium.Entity({
|
||||
name: '多边形',
|
||||
polygon: {
|
||||
hierarchy: new Cesium.CallbackProperty(() => {
|
||||
return new Cesium.PolygonHierarchy(data.measureAreaPos)
|
||||
// 使用最新1.72的时候 必须返回PolygonHierarchy类型 Cannot read property 'length' of undefined
|
||||
// 低版本好像都可以
|
||||
}, false),
|
||||
material: Cesium.Color.RED.withAlpha(0.5)
|
||||
}
|
||||
})
|
||||
poly = data.viewer.entities.add(polygon)
|
||||
data.measureAreaEntities.push(poly)
|
||||
} else {
|
||||
data.measureAreaPos.pop()
|
||||
data.measureAreaPos.push(cartesian)
|
||||
}
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
||||
|
||||
data.handler.setInputAction(function (movement) {
|
||||
const ray = data.viewer.camera.getPickRay(movement.position)
|
||||
cartesian = data.viewer.scene.globe.pick(ray, data.viewer.scene)
|
||||
if (data.measureAreaPos.length === 0) {
|
||||
data.measureAreaPos.push(cartesian)
|
||||
}
|
||||
data.measureAreaPos.push(cartesian)
|
||||
// 在三维场景中添加点
|
||||
var cartographic = Cesium.Cartographic.fromCartesian(
|
||||
data.measureAreaPos[data.measureAreaPos.length - 1]
|
||||
)
|
||||
var longitudeString = Cesium.Math.toDegrees(cartographic.longitude)
|
||||
var latitudeString = Cesium.Math.toDegrees(cartographic.latitude)
|
||||
var heightString = cartographic.height
|
||||
tempPoints.push({
|
||||
lon: longitudeString,
|
||||
lat: latitudeString,
|
||||
hei: heightString
|
||||
})
|
||||
const floatingPoint = data.viewer.entities.add({
|
||||
name: '多边形折点',
|
||||
position: data.measureAreaPos[data.measureAreaPos.length - 1],
|
||||
point: {
|
||||
pixelSize: 5,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 2,
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
|
||||
}
|
||||
})
|
||||
data.measureAreaEntities.push(floatingPoint)
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
||||
|
||||
data.handler.setInputAction(function (movement) {
|
||||
data.handler.destroy()
|
||||
data.measureAreaPos.pop()
|
||||
|
||||
var textArea = getArea(tempPoints) + '平方公里'
|
||||
const textLabel = data.viewer.entities.add({
|
||||
name: '多边形面积',
|
||||
position: data.measureAreaPos[data.measureAreaPos.length - 1],
|
||||
label: {
|
||||
text: textArea,
|
||||
font: '18px sans-serif',
|
||||
fillColor: Cesium.Color.GOLD,
|
||||
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||||
outlineWidth: 2,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
pixelOffset: new Cesium.Cartesian2(20, -40),
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
|
||||
}
|
||||
})
|
||||
data.measureAreaEntities.push(textLabel)
|
||||
// 结束绘制
|
||||
emit('complete-measure')
|
||||
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
||||
|
||||
var radiansPerDegree = Math.PI / 180.0 // 角度转化为弧度(rad)
|
||||
var degreesPerRadian = 180.0 / Math.PI // 弧度转化为角度
|
||||
|
||||
// 计算多边形面积
|
||||
function getArea(points) {
|
||||
var res = 0
|
||||
// 拆分三角曲面
|
||||
for (var i = 0; i < points.length - 2; i++) {
|
||||
var j = (i + 1) % points.length
|
||||
var k = (i + 2) % points.length
|
||||
var totalAngle = Angle(points[i], points[j], points[k])
|
||||
|
||||
var dis_temp1 = distance(data.measureAreaPos[i], data.measureAreaPos[j])
|
||||
var dis_temp2 = distance(data.measureAreaPos[j], data.measureAreaPos[k])
|
||||
res += dis_temp1 * dis_temp2 * Math.abs(Math.sin(totalAngle))
|
||||
}
|
||||
|
||||
return (res / 1000000.0).toFixed(4)
|
||||
}
|
||||
|
||||
/* 角度*/
|
||||
function Angle(p1, p2, p3) {
|
||||
var bearing21 = Bearing(p2, p1)
|
||||
var bearing23 = Bearing(p2, p3)
|
||||
var angle = bearing21 - bearing23
|
||||
if (angle < 0) {
|
||||
angle += 360
|
||||
}
|
||||
return angle
|
||||
}
|
||||
/* 方向*/
|
||||
function Bearing(from, to) {
|
||||
var lat1 = from.lat * radiansPerDegree
|
||||
var lon1 = from.lon * radiansPerDegree
|
||||
var lat2 = to.lat * radiansPerDegree
|
||||
var lon2 = to.lon * radiansPerDegree
|
||||
var angle = -Math.atan2(
|
||||
Math.sin(lon1 - lon2) * Math.cos(lat2),
|
||||
Math.cos(lat1) * Math.sin(lat2) -
|
||||
Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2)
|
||||
)
|
||||
if (angle < 0) {
|
||||
angle += Math.PI * 2.0
|
||||
}
|
||||
angle = angle * degreesPerRadian // 角度
|
||||
return angle
|
||||
}
|
||||
|
||||
function distance(point1, point2) {
|
||||
var point1cartographic = Cesium.Cartographic.fromCartesian(point1)
|
||||
var point2cartographic = Cesium.Cartographic.fromCartesian(point2)
|
||||
/** 根据经纬度计算出距离**/
|
||||
var geodesic = new Cesium.EllipsoidGeodesic()
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic)
|
||||
var s = geodesic.surfaceDistance
|
||||
// 返回两点之间的距离
|
||||
s = Math.sqrt(
|
||||
Math.pow(s, 2) +
|
||||
Math.pow(point2cartographic.height - point1cartographic.height, 2)
|
||||
)
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// 清除测高
|
||||
const clearMeasureHeight = () => {
|
||||
data.measureHeightPos = []
|
||||
if (data.measureHeightEntities.length > 0) {
|
||||
data.measureHeightEntities.map(entity => {
|
||||
data.viewer.entities.remove(entity)
|
||||
})
|
||||
data.measureHeightEntities = []
|
||||
}
|
||||
}
|
||||
|
||||
// 测高
|
||||
const measureHeight = () => {
|
||||
clearMeasureHeight()
|
||||
|
||||
var poly = null
|
||||
var height = 0
|
||||
var cartesian = null
|
||||
|
||||
// 鼠标事件
|
||||
data.handler = new Cesium.ScreenSpaceEventHandler(data.viewer.scene.canvas)
|
||||
|
||||
data.handler.setInputAction(function(movement) {
|
||||
cartesian = data.viewer.scene.pickPosition(movement.endPosition)
|
||||
if (data.measureHeightPos.length >= 2) {
|
||||
if (!Cesium.defined(poly)) {
|
||||
const entity = new Cesium.Entity({
|
||||
name: '直线',
|
||||
position: data.measureHeightPos[0],
|
||||
polyline: {
|
||||
show: true,
|
||||
positions: new Cesium.CallbackProperty(() => {
|
||||
var temp_position = []
|
||||
temp_position.push(data.measureHeightPos[0])
|
||||
var point1cartographic = Cesium.Cartographic.fromCartesian(data.measureHeightPos[0])
|
||||
var point2cartographic = Cesium.Cartographic.fromCartesian(data.measureHeightPos[1])
|
||||
var point_temp = Cesium.Cartesian3.fromDegrees(Cesium.Math.toDegrees(point1cartographic.longitude), Cesium.Math.toDegrees(point1cartographic.latitude), point2cartographic.height)
|
||||
temp_position.push(point_temp)
|
||||
return temp_position
|
||||
}, false),
|
||||
material: Cesium.Color.RED,
|
||||
width: 2
|
||||
},
|
||||
ellipse: {
|
||||
show: true,
|
||||
material: Cesium.Color.GREEN.withAlpha(0.5),
|
||||
semiMinorAxis: _semiMinorAxis(data.measureHeightPos),
|
||||
semiMajorAxis: _semiMinorAxis(data.measureHeightPos),
|
||||
height: new Cesium.CallbackProperty(() => {
|
||||
const point2cartographic = Cesium.Cartographic.fromCartesian(data.measureHeightPos[1])// 终点
|
||||
return point2cartographic.height
|
||||
}, false),
|
||||
// height must be set for outline to display
|
||||
outline: true
|
||||
}
|
||||
})
|
||||
poly = data.viewer.entities.add(entity)
|
||||
data.measureHeightEntities.push(poly)
|
||||
} else {
|
||||
data.measureHeightPos.pop()
|
||||
data.measureHeightPos.push(cartesian)
|
||||
}
|
||||
height = getHeight(data.measureHeightPos)
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
||||
|
||||
data.handler.setInputAction(function(movement) {
|
||||
cartesian = data.viewer.scene.pickPosition(movement.position)
|
||||
|
||||
if (data.measureHeightPos.length === 0) {
|
||||
data.measureHeightPos.push(cartesian)
|
||||
data.measureHeightPos.push(cartesian)
|
||||
|
||||
const startPoint = data.viewer.entities.add({
|
||||
name: '高度',
|
||||
position: data.measureHeightPos[0],
|
||||
point: {
|
||||
pixelSize: 5,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 2,
|
||||
heightReference: Cesium.HeightReference.none
|
||||
},
|
||||
label: {
|
||||
text: '0米',
|
||||
font: '18px sans-serif',
|
||||
fillColor: Cesium.Color.GOLD,
|
||||
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||||
outlineWidth: 2,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
pixelOffset: new Cesium.Cartesian2(20, -40)
|
||||
}
|
||||
})
|
||||
|
||||
data.measureHeightEntities.push(startPoint)
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
||||
|
||||
data.handler.setInputAction(function(movement) {
|
||||
data.handler.destroy()
|
||||
var textDisance = height + '米'
|
||||
|
||||
var point1cartographic = Cesium.Cartographic.fromCartesian(data.measureHeightPos[0])
|
||||
var point2cartographic = Cesium.Cartographic.fromCartesian(data.measureHeightPos[1])
|
||||
var point_temp = Cesium.Cartesian3.fromDegrees(Cesium.Math.toDegrees(point1cartographic.longitude), Cesium.Math.toDegrees(point1cartographic.latitude), point2cartographic.height)
|
||||
|
||||
const entity = data.viewer.entities.add({
|
||||
name: '直线距离',
|
||||
position: point_temp,
|
||||
point: {
|
||||
pixelSize: 5,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 2,
|
||||
heightReference: Cesium.HeightReference.none
|
||||
},
|
||||
label: {
|
||||
text: textDisance,
|
||||
font: '18px sans-serif',
|
||||
fillColor: Cesium.Color.GOLD,
|
||||
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||||
outlineWidth: 2,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
pixelOffset: new Cesium.Cartesian2(20, -20)
|
||||
}
|
||||
})
|
||||
data.measureHeightEntities.push(entity)
|
||||
|
||||
// 结束绘制
|
||||
emit('complete-measure')
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
|
||||
|
||||
function getHeight(_positions) {
|
||||
var cartographic = Cesium.Cartographic.fromCartesian(_positions[0])
|
||||
var cartographic1 = Cesium.Cartographic.fromCartesian(_positions[1])
|
||||
var height_temp = cartographic1.height - cartographic.height
|
||||
return height_temp.toFixed(2)
|
||||
}
|
||||
|
||||
function _semiMinorAxis(_positions) {
|
||||
return new Cesium.CallbackProperty(() => {
|
||||
var point1cartographic = Cesium.Cartographic.fromCartesian(_positions[0])
|
||||
var point2cartographic = Cesium.Cartographic.fromCartesian(_positions[1])
|
||||
/** 根据经纬度计算出距离**/
|
||||
var geodesic = new Cesium.EllipsoidGeodesic()
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic)
|
||||
var s = geodesic.surfaceDistance
|
||||
return s
|
||||
}, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载倾斜摄影
|
||||
* @param {tileset.json 路径} tilesetUrl
|
||||
*/
|
||||
const load3DTiles = (tilesetUrl) => {
|
||||
var tileset3D = new Cesium.Cesium3DTileset({
|
||||
url: tilesetUrl,
|
||||
skipScreenSpaceErrorFactor: 16,
|
||||
// 开启跳级加载
|
||||
skipLevelOfDetail: true,
|
||||
// preferLeaves
|
||||
preferLeaves: true,
|
||||
maximumMemoryUsage: 2800
|
||||
})
|
||||
|
||||
tileset3D.readyPromise.then((tileset) => {
|
||||
data.viewer.scene.primitives.add(tileset)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 地图控制
|
||||
* @param {*} order
|
||||
*/
|
||||
const mapControl = (order) => {
|
||||
// 存储地图相机初始值
|
||||
const x = data.viewer.camera.position.x
|
||||
const y = data.viewer.camera.position.y
|
||||
const z = data.viewer.camera.position.z
|
||||
|
||||
data.viewer.initCameraPosition = { x: x, y: y, z: z }
|
||||
data.viewer.initViewRectangle = data.viewer.camera.computeViewRectangle()
|
||||
data.viewer.initOrientation = {
|
||||
heading: data.viewer.camera.heading,
|
||||
pitch: data.viewer.camera.pitch,
|
||||
roll: data.viewer.camera.roll
|
||||
}
|
||||
if (order === 'turnLeft') {
|
||||
// 向左旋转
|
||||
data.viewer.camera.look(data.viewer.initCameraPosition, 0.4)
|
||||
} else if (order === 'turnRight') {
|
||||
// 向右旋转
|
||||
data.viewer.camera.look(data.viewer.initCameraPosition, -0.4)
|
||||
} else if (order === 'birdEye') {
|
||||
// 鸟瞰
|
||||
const pick1 = new Cesium.Cartesian2(document.body.clientWidth / 2, screen.height / 2)
|
||||
const cartesian3 = data.viewer.scene.globe.pick(data.viewer.camera.getPickRay(pick1), data.viewer.scene)
|
||||
const naokanEntity = data.viewer.entities.add(new Cesium.Entity({
|
||||
point: new Cesium.PointGraphics({}),
|
||||
position: cartesian3
|
||||
}))
|
||||
var promise = data.viewer.flyTo(naokanEntity, {
|
||||
offset: {
|
||||
heading: data.viewer.camera.heading,
|
||||
pitch: Cesium.Math.toRadians(-90),
|
||||
range: 5000
|
||||
}
|
||||
})
|
||||
// console.log(promise)
|
||||
// promise.then(() => {
|
||||
// data.viewer.entities.remove(naokanEntity)
|
||||
// })
|
||||
} else if (order === 'zoomIn') {
|
||||
data.viewer.scene.camera.zoomIn(2500)
|
||||
} else if (order === 'zoomOut') {
|
||||
data.viewer.scene.camera.zoomOut(2500)
|
||||
} else if (order === 'reset') {
|
||||
// //初始化视角
|
||||
data.viewer.camera.position = {
|
||||
x: -2610445.7358946367, y: 4754512.985081314, z: 3344995.579662871
|
||||
}
|
||||
data.viewer.camera.setView({
|
||||
orientation: {
|
||||
heading: 3.2814040916499394,
|
||||
pitch: -0.5626895458436971,
|
||||
roll: 6.283183261091251
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取鼠标所在位置
|
||||
const getMouseLocation = () => {
|
||||
// 得到当前三维场景的椭球体
|
||||
var ellipsoid = data.scene.globe.ellipsoid
|
||||
var handler = new Cesium.ScreenSpaceEventHandler(data.scene.canvas)
|
||||
// 设置鼠标移动事件的处理函数,这里负责监听x,y坐标值变化
|
||||
handler.setInputAction(function(movement) {
|
||||
// 通过指定的椭球或者地图对应的坐标系,将鼠标的二维坐标转换为对应椭球体三维坐标
|
||||
var cartesian = data.viewer.camera.pickEllipsoid(movement.endPosition, ellipsoid)
|
||||
if (cartesian) {
|
||||
// 将笛卡尔坐标转换为地理坐标
|
||||
var cartographic = ellipsoid.cartesianToCartographic(cartesian)
|
||||
// 将弧度转为度的十进制度表示
|
||||
var longitudeString = Cesium.Math.toDegrees(cartographic.longitude)
|
||||
var latitudeString = Cesium.Math.toDegrees(cartographic.latitude)
|
||||
// 获取相机高度
|
||||
// var height = Math.ceil(data.viewer.camera.positionCartographic.height)
|
||||
data.mouseLocation.lng = longitudeString.toFixed(6) || 0
|
||||
data.mouseLocation.lat = latitudeString.toFixed(6) || 0
|
||||
// data.mouseLocation.height = height || 0
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initMap()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(data),
|
||||
mapControl,
|
||||
handleToolBar,
|
||||
measureRef,
|
||||
clearMeasure,
|
||||
measureLine,
|
||||
measureArea,
|
||||
measureHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cesium-viewer{
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<div class="copyright-info">
|
||||
<span class="copyright-info-text">
|
||||
版权所有:拓恒技术有限公司 |
|
||||
问题反馈:张涛 17696850927(微信同号) |
|
||||
<span class="copyright-info-location">
|
||||
经度:<span class="copyright-info-lng">{{ getLocation.lng || 0 }}</span>
|
||||
</span> |
|
||||
<span class="copyright-info-location">
|
||||
纬度:<span class="copyright-info-lat">{{ getLocation.lat || 0 }}</span>
|
||||
</span>
|
||||
<!-- <span class="copyright-info-location">
|
||||
高度:<span class="copyright-info-camera">{{ getLocation.height }}</span>
|
||||
</span> -->
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { computed, toRaw } from 'vue'
|
||||
export default {
|
||||
props: {
|
||||
location: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const getLocation = computed(() => {
|
||||
return props.location
|
||||
})
|
||||
|
||||
return {
|
||||
getLocation
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.copyright-info{
|
||||
width:1200px;
|
||||
height:36px;
|
||||
background:linear-gradient(90deg,rgba(0,0,0,1),rgba(0,0,0,0));
|
||||
opacity:0.6;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.copyright-info-text{
|
||||
width:899px;
|
||||
height:13px;
|
||||
font-size:12px;
|
||||
font-family:Microsoft YaHei;
|
||||
font-weight:400;
|
||||
line-height:6px;
|
||||
line-height: 36px;
|
||||
margin-left: 19px;
|
||||
color:rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
.copyright-info-location{
|
||||
color: #4EFDFF
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
<!--
|
||||
* @Author: lixin
|
||||
* @Date: 2023-5-29
|
||||
* @Description: 图层管理(级联控制)
|
||||
-->
|
||||
<template>
|
||||
<div class="layer-tree">
|
||||
<n-tree
|
||||
block-line
|
||||
cascade
|
||||
checkable
|
||||
:data="data"
|
||||
:default-expanded-keys="defaultExpandedKeys"
|
||||
:default-checked-keys="defaultCheckedKeys"
|
||||
@update:checked-keys="updateCheckedKeys"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from 'vue'
|
||||
import { repeat } from 'seemly'
|
||||
|
||||
function createData(level = 4, baseKey = '') {
|
||||
if (!level) { return void 0 }
|
||||
return repeat(6 - level, void 0).map((_, index) => {
|
||||
const key = '' + baseKey + level + index
|
||||
return {
|
||||
label: createLabel(level),
|
||||
key,
|
||||
children: createData(level - 1, key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function createLabel(level) {
|
||||
if (level === 4) { return '道生一' }
|
||||
if (level === 3) { return '一生二' }
|
||||
if (level === 2) { return '二生三' }
|
||||
if (level === 1) { return '三生万物' }
|
||||
return ''
|
||||
}
|
||||
|
||||
export default ({
|
||||
setup() {
|
||||
console.log(createData())
|
||||
|
||||
return {
|
||||
data: createData(),
|
||||
defaultExpandedKeys: ref(['40', '4030', '403020']),
|
||||
defaultCheckedKeys: ref(['40302010']),
|
||||
updateCheckedKeys: (keys, options, meta) => {
|
||||
console.log('updateCheckedKeys', keys, options, meta)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.layer-tree {
|
||||
width: 270px;
|
||||
height: 430px;
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 20px;
|
||||
background: url('../../../../assets/basemap/layer/tuceng_bg.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
::v-deep(.n-tree-node-content__text){
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
::v-deep(.n-tree-node--checkable:hover){
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<!--
|
||||
* @Author: lixin
|
||||
* @Date: 2023-5-27
|
||||
* @Description: 地图控制:放大、缩小、复位、鸟瞰、向左旋转、向右旋转
|
||||
-->
|
||||
<template>
|
||||
<div class="contorl-list">
|
||||
<div
|
||||
v-for="(item, index) in controlList"
|
||||
:key="index"
|
||||
class="control-bar"
|
||||
>
|
||||
<p v-if="hoverIndex === index" class="title">{{ item.label }}</p>
|
||||
<img
|
||||
:src="hoverIndex === index ? getImageUrl(item.id + '_press'): getImageUrl(item.id)"
|
||||
@mouseover="hoverIndex = index"
|
||||
@mouseout="hoverIndex = null"
|
||||
@click="handleControl(item)"
|
||||
>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { reactive, toRefs } from 'vue'
|
||||
export default {
|
||||
emits: ['control'],
|
||||
setup(props, { emit }) {
|
||||
const data = reactive({
|
||||
controlList: [
|
||||
{ id: 'turnLeft', label: '向左旋转' },
|
||||
{ id: 'turnRight', label: '向右旋转' },
|
||||
{ id: 'birdEye', label: '鸟瞰' },
|
||||
{ id: 'reset', label: '复位' },
|
||||
{ id: 'zoomIn', label: '放大' },
|
||||
{ id: 'zoomOut', label: '缩小' }
|
||||
],
|
||||
hoverIndex: null
|
||||
})
|
||||
|
||||
const getImageUrl = (src) => {
|
||||
return new URL(`../../../../assets/basemap/mapcontrol/${src}.png`, import.meta.url).href
|
||||
}
|
||||
|
||||
const handleControl = (info) => {
|
||||
emit('control', info?.id)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(data),
|
||||
getImageUrl,
|
||||
handleControl
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.contorl-list {
|
||||
width: 42px;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
cursor: pointer;
|
||||
.title {
|
||||
width: 80px;
|
||||
height: 0;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
left: -85px;
|
||||
bottom: -10px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
<!--
|
||||
* @Author: lixin
|
||||
* @Date: 2023-5-29
|
||||
* @Description: 底图切换和出图
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="tool-list">
|
||||
<p v-for="(item, index) in toolList" :key="index" class="map-tool" @click="handleTool(item.id)">
|
||||
<img :src="getImageUrl(item.id)">
|
||||
{{ item.label }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-show="containerShow" class="switch-container">
|
||||
<!-- <div class="road-net">
|
||||
地图
|
||||
<p class="net-switch">
|
||||
<n-switch :rail-style="railStyle" @update:value="handleRoadNet" />
|
||||
路网
|
||||
</p>
|
||||
</div> -->
|
||||
<div class="map-list">
|
||||
<p v-for="(item, index) in mapList" :key="index" :class="selectedType === item.id ? 'selected' : ''" class="map-server" @click="selectMap(item.id)">
|
||||
<img :src="getImageUrl(item.id)">
|
||||
{{ item.label }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, toRefs } from '@vue/reactivity'
|
||||
export default {
|
||||
emits: ['print', 'switch', 'road'],
|
||||
setup(props, { emit }) {
|
||||
const data = reactive({
|
||||
toolList: [
|
||||
{ id: 'switch', label: '切换' },
|
||||
{ id: 'print', label: '下载' }
|
||||
],
|
||||
mapList: [
|
||||
{ id: 'img', label: '卫星地图' },
|
||||
{ id: 'vec', label: '电子地图' }
|
||||
],
|
||||
containerShow: false,
|
||||
roadShow: false,
|
||||
selectedType: 'img'
|
||||
})
|
||||
|
||||
const selectMap = (type) => {
|
||||
if (type !== data.selectedType) {
|
||||
data.selectedType = type
|
||||
emit('switch', type)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRoadNet = (bool) => {
|
||||
emit('road', bool)
|
||||
}
|
||||
|
||||
const handleTool = (value) => {
|
||||
if (value === 'print') {
|
||||
emit('print')
|
||||
} else if (value === 'switch') {
|
||||
data.containerShow = !data.containerShow
|
||||
}
|
||||
}
|
||||
|
||||
const railStyle = (status) => {
|
||||
const style = {}
|
||||
if (status.checked) {
|
||||
style.background = '#1896FF'
|
||||
} else {
|
||||
style.background = '#C3C6CC'
|
||||
}
|
||||
|
||||
return style
|
||||
}
|
||||
|
||||
const getImageUrl = (src) => {
|
||||
return new URL(`../../../../assets/basemap/mapswitch/${src}.png`, import.meta.url).href
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(data),
|
||||
getImageUrl,
|
||||
railStyle,
|
||||
handleTool,
|
||||
selectMap,
|
||||
handleRoadNet
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tool-list {
|
||||
width: 50px;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
left: 40px;
|
||||
|
||||
.map-tool {
|
||||
width: 50px;
|
||||
height: 59px;
|
||||
background: rgba(13,32,67,0.8);
|
||||
border: 1px solid #0F61D4;
|
||||
border-radius: 10px;
|
||||
margin: 6px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-container {
|
||||
width: 163px;
|
||||
height: 145px;
|
||||
position: absolute;
|
||||
bottom: 75px;
|
||||
left: 95px;
|
||||
background: url('../../../../assets/basemap/mapswitch/switchback.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.road-net {
|
||||
width: 150px;
|
||||
height: 42px;
|
||||
border-bottom: 2px dashed #C3C6CC;
|
||||
font-size: 16px;
|
||||
font-family: Source Han Sans CN;
|
||||
font-weight: 400;
|
||||
color: #1896FF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
margin-left: 12px;
|
||||
|
||||
.net-switch {
|
||||
font-size: 14px;
|
||||
font-family: Source Han Sans CN;
|
||||
font-weight: 400;
|
||||
color: #C3C6CC;
|
||||
}
|
||||
}
|
||||
|
||||
.map-list {
|
||||
width: 152px;
|
||||
height: 103px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
margin-left: 12px;
|
||||
|
||||
.map-server{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-family: Source Han Sans CN;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
|
||||
img{
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
// border: 1px solid #fff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.map-server.selected {
|
||||
|
||||
color: #1896FF;
|
||||
|
||||
img{
|
||||
border: 2px solid #1896FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.n-switch .n-switch__rail){
|
||||
background-color: #C3C6CC;
|
||||
.n-switch__button {
|
||||
background-color: #1891F6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
<!--
|
||||
* @Author: lixin
|
||||
* @Date: 2023-5-27
|
||||
* @Description: 测量工具: 测距、测面、测高
|
||||
-->
|
||||
<template>
|
||||
<div class="measure-list">
|
||||
<div
|
||||
v-for="(item, index) in controlList"
|
||||
:key="index"
|
||||
:class="selectedTool === item.id ? 'selceted' : ''"
|
||||
class="measure-bar"
|
||||
@click="handleTool(item)"
|
||||
>
|
||||
<img
|
||||
:src="selectedTool === item.id ? getImageUrl(item.id + '_press'): getImageUrl(item.id)"
|
||||
>
|
||||
<p
|
||||
:class="selectedTool === item.id ? 'selceted' : ''"
|
||||
class="measure-text"
|
||||
>{{ item.label }}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { reactive, toRefs } from 'vue'
|
||||
export default {
|
||||
emits: ['measure'],
|
||||
setup(props, { emit }) {
|
||||
const data = reactive({
|
||||
controlList: [
|
||||
{ id: 'distance', label: '测距' },
|
||||
{ id: 'area', label: '测面' },
|
||||
{ id: 'height', label: '测高' },
|
||||
{ id: 'clear', label: '清除' }
|
||||
],
|
||||
selectedTool: null
|
||||
})
|
||||
|
||||
const getImageUrl = (src) => {
|
||||
return new URL(`../../../../assets/basemap/measure/${src}.png`, import.meta.url).href
|
||||
}
|
||||
|
||||
const handleTool = (info) => {
|
||||
if (info?.id !== data.selectedTool) {
|
||||
data.selectedTool = info?.id
|
||||
}
|
||||
emit('measure', data.selectedTool)
|
||||
}
|
||||
|
||||
const cancelMeasure = () => {
|
||||
data.selectedTool = null
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(data),
|
||||
getImageUrl,
|
||||
handleTool,
|
||||
cancelMeasure
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.measure-list {
|
||||
position: absolute;
|
||||
top: 105px;
|
||||
right: 20px;
|
||||
width: 280px;
|
||||
height: 90px;
|
||||
cursor: pointer;
|
||||
background: url("../../../../assets/basemap/measure/celiang_bg.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.measure-bar{
|
||||
width:60px;
|
||||
height:60px;
|
||||
background:rgba(1,25,63,0.15);
|
||||
border:1px solid rgba(0,223,244,1);
|
||||
border-radius:2px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
margin: 0 5px;
|
||||
|
||||
.measure-text{
|
||||
font-size:12px;
|
||||
font-family:Microsoft YaHei;
|
||||
font-weight:400;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.measure-text.selceted{
|
||||
color: #ffbb00;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.measure-bar.selceted{
|
||||
border:1px solid #ffbb00;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<!--
|
||||
* @Author: lixin
|
||||
* @Date: 2023-5-27
|
||||
* @Description: 地图上半部分工具栏,包括: 图层、漫游、量测、在线标绘、卷帘、出图等
|
||||
-->
|
||||
<template>
|
||||
<div class="tool-list">
|
||||
<div v-for="(item, index) in toolList" :key="index" class="tool-bar">
|
||||
<img :src="item.id === selectedTool ? getImgUrl(item.id + '_press') : getImgUrl(item.id)">
|
||||
<span class="tool-text" :class="item.id === selectedTool ? 'press': ''" @click="handleTool(item)">{{ item.label }}</span>
|
||||
<b v-if="index !==(toolList.length -1)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, toRefs } from 'vue'
|
||||
export default {
|
||||
emits: ['tool'],
|
||||
setup(props, { emit }) {
|
||||
const data = reactive({
|
||||
toolList: [
|
||||
{ id: 'tuceng', label: '图层' },
|
||||
{ id: 'liangce', label: '量测' }
|
||||
],
|
||||
selectedTool: null
|
||||
})
|
||||
|
||||
const getImgUrl = (src) => {
|
||||
return new URL(`../../../../assets/basemap/toolbar/${src}.png`, import.meta.url).href
|
||||
}
|
||||
|
||||
const handleTool = (info) => {
|
||||
if (info?.id !== data.selectedTool) {
|
||||
data.selectedTool = info?.id
|
||||
} else {
|
||||
data.selectedTool = null
|
||||
}
|
||||
emit('tool', data.selectedTool)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(data),
|
||||
getImgUrl,
|
||||
handleTool
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tool-list{
|
||||
// width: 592px;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 20px;
|
||||
cursor: pointer;
|
||||
background: url("../../../../assets/basemap/toolbar/caozuo_bg.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
|
||||
.tool-bar{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 15px;
|
||||
|
||||
.tool-text{
|
||||
font-size:14px;
|
||||
font-family:Microsoft YaHei;
|
||||
font-weight:400;
|
||||
/*color:rgba(78,253,255,1);*/
|
||||
color: #FFFFFF;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
.tool-text.press{
|
||||
color: rgb(98,223,255);
|
||||
}
|
||||
|
||||
b {
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
border-right: 2px solid rgb(98,223,255);
|
||||
// border-right: 1px solid #eee;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,25 +1,15 @@
|
|||
<template>
|
||||
<div>
|
||||
首页
|
||||
</div>
|
||||
<BaseMap />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import BaseMap from './components/BaseMap.vue'
|
||||
|
||||
export default {
|
||||
name: 'HomePage',
|
||||
setup(props) {
|
||||
const router = useRouter()
|
||||
function toSystem() {
|
||||
router.push({ path: '/login' })
|
||||
}
|
||||
return {
|
||||
toSystem
|
||||
}
|
||||
}
|
||||
components: { BaseMap }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 7.7 KiB |
|
|
@ -16,10 +16,12 @@ export default defineConfig(({ command, mode }) => {
|
|||
const env = loadEnv(mode, process.cwd())
|
||||
const viteEnv = wrapperEnv(env)
|
||||
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY } = viteEnv
|
||||
|
||||
return {
|
||||
root,
|
||||
base: VITE_PUBLIC_PATH || '/',
|
||||
plugins: createVitePlugins(viteEnv, isBuild),
|
||||
|
||||
lintOnSave: false,
|
||||
resolve: {
|
||||
alias: {
|
||||
|
|
|
|||