Compare commits

...

25 Commits

Author SHA1 Message Date
lixin e26b939ebe map change 2023-08-15 10:49:38 +08:00
lixin b0c4d472d7 home page 2023-08-02 09:27:32 +08:00
wangmengqi 8f14e18318 Merge branch 'wangmengqi1' of zhangtao/gis into develop 2023-03-09 13:20:18 +08:00
unknown 6a955b5921 222 2023-03-09 13:17:58 +08:00
unknown bcce2530aa 111 2023-03-09 13:08:38 +08:00
wangmengqi 65ac2098ee Merge branch 'wangmengqi' of zhangtao/gis into develop 2022-11-22 10:21:24 +08:00
unknown 012458b47c Merge branch 'develop' of http://192.168.11.14:51037/zhangtao/gis into wangmengqi 2022-11-22 10:19:16 +08:00
unknown 4b69292063 弹窗信息展示 2022-11-22 10:19:01 +08:00
wangmengqi d118c210f6 Merge branch 'wangmengqi' of zhangtao/gis into develop 2022-10-31 15:02:23 +08:00
unknown a722837a11 坐标匹配 2022-10-31 15:00:56 +08:00
wangmengqi 3ac4d6698a Merge branch 'wangmengqi' of zhangtao/gis into develop 2022-10-25 17:15:31 +08:00
unknown 0ac683a25e 聚合图 2022-10-25 17:14:24 +08:00
wangmengqi b846f3cd40 Merge branch 'wangmengqi' of zhangtao/gis into develop 2022-10-24 13:57:52 +08:00
unknown 77aafac3e3 服务文件 2022-10-24 13:57:01 +08:00
wangmengqi 863b7866cb Merge branch 'wangmengqi' of zhangtao/gis into develop 2022-10-24 10:10:49 +08:00
unknown 3f23fbad32 格式调整 2022-10-24 10:07:34 +08:00
lixin 889830aa1b Merge branch 'wangmengqi' of zhangtao/gis into develop 2022-10-24 08:37:27 +08:00
unknown 05c1116703 坐标系转换 2022-10-22 15:31:29 +08:00
unknown 4e2f51a0d2 service.js 2022-10-21 13:54:22 +08:00
unknown 01e3800bea service.js 2022-10-21 13:53:03 +08:00
lixin 7c49d9584e Merge branch 'wangmengqi' of zhangtao/gis into develop 2022-10-21 08:53:43 +08:00
unknown 7b0cf77b27 服务加载 2022-10-20 19:44:06 +08:00
unknown 449070ad2d 服务加载 2022-10-20 19:19:57 +08:00
lixin 73607b7276 Merge branch 'wangmengqi' of zhangtao/gis into develop 2022-10-20 09:25:09 +08:00
unknown 26534c6360 wms 2022-10-20 08:21:08 +08:00
69 changed files with 2481 additions and 15741 deletions

2
.env
View File

@ -1,5 +1,5 @@
# title # title
VITE_APP_TITLE = '智飞' VITE_APP_TITLE = '时空大数据平台'
# 端口号 # 端口号
VITE_PORT = 3050 VITE_PORT = 3050

View File

@ -3,7 +3,8 @@ module.exports = {
env: { env: {
browser: true, browser: true,
node: true, node: true,
es6: true es6: true,
'vue/setup-compiler-macros': true
}, },
extends: [ extends: [
'plugin:vue/vue3-recommended', 'plugin:vue/vue3-recommended',
@ -285,7 +286,7 @@ module.exports = {
/* 要求或禁止语句块之前的空格 */ /* 要求或禁止语句块之前的空格 */
'space-before-blocks': [2, 'always'], 'space-before-blocks': [2, 'always'],
/* 要求或禁止函数圆括号之前有一个空格 */ /* 要求或禁止函数圆括号之前有一个空格 */
'space-before-function-paren': [2, 'never'], // 'space-before-function-paren': [2, 'never'],
/* 禁止或强制圆括号内的空格 */ /* 禁止或强制圆括号内的空格 */
'space-in-parens': [2, 'never'], 'space-in-parens': [2, 'never'],
/* 要求中缀操作符周围有空格 */ /* 要求中缀操作符周围有空格 */

2
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,2 @@
{
}

View File

@ -8,6 +8,7 @@ import VueSetupExtend from 'vite-plugin-vue-setup-extend'
import { unocss } from './unocss' import { unocss } from './unocss'
import { configHtmlPlugin } from './html' import { configHtmlPlugin } from './html'
import { configMockPlugin } from './mock' import { configMockPlugin } from './mock'
import cesium from 'vite-plugin-cesium'
export function createVitePlugins(viteEnv, isBuild) { export function createVitePlugins(viteEnv, isBuild) {
const plugins = [ const plugins = [
@ -17,7 +18,8 @@ export function createVitePlugins(viteEnv, isBuild) {
}), }),
VueSetupExtend(), VueSetupExtend(),
unocss(), unocss(),
configHtmlPlugin(viteEnv, isBuild) configHtmlPlugin(viteEnv, isBuild),
cesium()
] ]
viteEnv?.VITE_APP_USE_MOCK && plugins.push(configMockPlugin(isBuild)) viteEnv?.VITE_APP_USE_MOCK && plugins.push(configMockPlugin(isBuild))

View File

@ -1,16 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" /> <head>
<link rel="icon" href="/favicon.ico" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="icon" href="/favicon.ico" />
<title>Vite App</title> <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> <link rel="stylesheet" href="https://g.alicdn.com/de/prismplayer/2.9.21/skins/default/aliplayer-min.css" />
</head>
<body> <script src='https://code.jquery.com/jquery-3.6.0.min.js'></script>
<div id="app"></div> <script charset="utf-8" type="text/javascript"
<script type="module" src="/src/main.js"></script> src="https://g.alicdn.com/de/prismplayer/2.9.21/aliplayer-h5-min.js"></script>
</body> </head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html> </html>

16128
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,9 @@
{ {
"name": "vite_vue3", "name": "vite_vue3",
"version": "0.0.0", "version": "0.0.0",
"globals": {
"Cesium": true
},
"scripts": { "scripts": {
"dev": "vite --mode localhost", "dev": "vite --mode localhost",
"build:test": "vite build --mode test && esno ./build/script", "build:test": "vite build --mode test && esno ./build/script",
@ -14,12 +17,15 @@
"@vicons/ionicons5": "^0.10.0", "@vicons/ionicons5": "^0.10.0",
"ali-oss": "^6.17.1", "ali-oss": "^6.17.1",
"axios": "^0.26.1", "axios": "^0.26.1",
"cesium": "^1.92.0",
"dayjs": "^1.11.2", "dayjs": "^1.11.2",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"pinia": "^2.0.13", "pinia": "^2.0.13",
"pinia-plugin-persist": "^1.0.0", "pinia-plugin-persist": "^1.0.0",
"tinymce": "^5.10.2", "tinymce": "^5.10.2",
"vite-plugin-cesium": "^1.2.22",
"vue": "^3.2.16", "vue": "^3.2.16",
"vue-jstree": "^2.1.6",
"vue-router": "^4.0.14", "vue-router": "^4.0.14",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
@ -42,6 +48,7 @@
"unocss": "^0.16.4", "unocss": "^0.16.4",
"unplugin-vue-components": "^0.18.5", "unplugin-vue-components": "^0.18.5",
"vite": "^2.6.4", "vite": "^2.6.4",
"vite-plugin-cesium": "^1.2.22",
"vite-plugin-html": "^2.1.2", "vite-plugin-html": "^2.1.2",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
"vite-plugin-vue-setup-extend": "^0.4.0" "vite-plugin-vue-setup-extend": "^0.4.0"

1
public/liuyang.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -7,29 +7,18 @@
* @FilePath: \gis\src\App.vue * @FilePath: \gis\src\App.vue
--> -->
<template> <template>
<n-config-provider :locale="zhCN" :date-locale="dateZhCN" inline-theme-disabled :theme-overrides="themeOverrides"> <div>
<n-loading-bar-provider> <Dashboard />
<loading-bar /> </div>
<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>
</template> </template>
<script setup> <script>
import { zhCN, dateZhCN } from 'naive-ui' import Dashboard from './views/dashboard/index.vue'
import themeOverrides from '@/utils/ui/theme.js' export default {
import loadingBar from '@/components/LoadingBar/index.vue' components: {
import messageContent from '@/components/Message/index.vue' Dashboard
import dialogContent from '@/components/Dialog/index.vue' }
}
</script> </script>
<style lang="scss"> <style lang="scss">
@ -38,5 +27,9 @@ import dialogContent from '@/components/Dialog/index.vue'
.n-config-provider { .n-config-provider {
height: inherit; height: inherit;
} }
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10+ and Edge */
user-select: none; /* Standard syntax */
} }
</style> </style>

View File

@ -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]
}
}

View File

@ -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)
}
}

182
src/api/gis/service.js Normal file
View File

@ -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('该图层已存在!请勿重复添加')
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
src/assets/img/TDMap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
src/assets/img/located.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
src/assets/img/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/assets/img/zoomIn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
src/assets/img/zoomout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -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>

View File

@ -1,5 +1,6 @@
import '@/styles/index.scss' import '@/styles/index.scss'
import 'uno.css' import 'uno.css'
import axios from 'axios'
import { createApp } from 'vue' import { createApp } from 'vue'
import { setupRouter } from '@/router' import { setupRouter } from '@/router'
@ -7,6 +8,7 @@ import { setupStore } from '@/store'
import App from './App.vue' import App from './App.vue'
function setupApp() { function setupApp() {
const app = createApp(App) const app = createApp(App)
@ -14,6 +16,7 @@ function setupApp() {
setupRouter(app) setupRouter(app)
app.mount('#app') app.mount('#app')
} }
setupApp() setupApp()

View File

@ -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,
// 3DGPU
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: truefalse
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>

View File

@ -0,0 +1,64 @@
<template>
<div class="copyright-info">
<span class="copyright-info-text">
版权所有拓恒技术有限公司&nbsp;&nbsp;|&nbsp;&nbsp;
问题反馈张涛 17696850927微信同号&nbsp;&nbsp;|&nbsp;&nbsp;
<span class="copyright-info-location">
经度<span class="copyright-info-lng">{{ getLocation.lng || 0 }}</span>
</span>&nbsp;&nbsp;|&nbsp;&nbsp;
<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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,25 +1,15 @@
<template> <template>
<div> <BaseMap />
首页
</div>
</template> </template>
<script> <script>
import { useRouter } from 'vue-router'
import BaseMap from './components/BaseMap.vue'
export default { export default {
name: 'HomePage', components: { BaseMap }
setup(props) {
const router = useRouter()
function toSystem() {
router.push({ path: '/login' })
}
return {
toSystem
}
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -16,10 +16,12 @@ export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd()) const env = loadEnv(mode, process.cwd())
const viteEnv = wrapperEnv(env) const viteEnv = wrapperEnv(env)
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY } = viteEnv const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY } = viteEnv
return { return {
root, root,
base: VITE_PUBLIC_PATH || '/', base: VITE_PUBLIC_PATH || '/',
plugins: createVitePlugins(viteEnv, isBuild), plugins: createVitePlugins(viteEnv, isBuild),
lintOnSave: false, lintOnSave: false,
resolve: { resolve: {
alias: { alias: {