@go-home-early/go-home-early98 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @go-home-early/go-home-early98 might be problematic. Click here for more details.
- package/README.md +28 -16
- package/dist/go-home-early.umd.js +5 -5
- package/package.json +2 -16
- package/src/packages/Cesium/DrawMap/DrawJs/CesiumEntityEdit.js +427 -0
- package/src/packages/Cesium/DrawMap/DrawJs/CreatePolygonOnGround.js +202 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawAttackArrow.js +477 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawBillboard.js +114 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawCircle.js +311 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawCircleTY.js +237 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawCurve.js +256 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawPincerArrow.js +584 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawPoint.js +158 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawPolygon.js +215 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawPolyline.js +213 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawPolylineJT.js +218 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawProFile.js +314 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawRectangle.js +215 -0
- package/src/packages/Cesium/DrawMap/DrawJs/DrawstraightArrow.js +364 -0
- package/src/packages/Cesium/DrawMap/DrawJs/MeasurePolygon.js +337 -0
- package/src/packages/Cesium/DrawMap/DrawJs/MeasurePolyline.js +285 -0
- package/src/packages/Cesium/DrawMap/DrawJs/ReminderTip.js +78 -0
- package/src/packages/Cesium/DrawMap/DrawJs/SlopeAspect.js +436 -0
- package/src/packages/Cesium/DrawMap/DrawJs/measureLength.js +177 -0
- package/src/packages/Cesium/DrawMap/index.vue +272 -0
- package/src/packages/Cesium/components/LocationDialog.vue +283 -0
- package/src/packages/Cesium/components/Popup.vue +591 -0
- package/src/packages/Cesium/components/PoumianDialog.vue +200 -0
- package/src/packages/Cesium/imgs/add-off.png +0 -0
- package/src/packages/Cesium/imgs/add.png +0 -0
- package/src/packages/Cesium/imgs/circle.png +0 -0
- package/src/packages/Cesium/imgs/icon-position-blue.png +0 -0
- package/src/packages/Cesium/imgs/line.png +0 -0
- package/src/packages/Cesium/imgs/map-delete.png +0 -0
- package/src/packages/Cesium/imgs/map.png +0 -0
- package/src/packages/Cesium/imgs/orientation-bg.svg +14 -0
- package/src/packages/Cesium/imgs/orientation.svg +21 -0
- package/src/packages/Cesium/imgs/poi.png +0 -0
- package/src/packages/Cesium/imgs/point.png +0 -0
- package/src/packages/Cesium/imgs/polygon.png +0 -0
- package/src/packages/Cesium/imgs/popup/back-popup-img.png +0 -0
- package/src/packages/Cesium/imgs/popup/backimg-btn.png +0 -0
- package/src/packages/Cesium/imgs/popup/dialog-bd.png +0 -0
- package/src/packages/Cesium/imgs/popup/dialog-btn.png +0 -0
- package/src/packages/Cesium/imgs/popup/dialog-ft.png +0 -0
- package/src/packages/Cesium/imgs/popup/dialog-hd.png +0 -0
- package/src/packages/Cesium/imgs/reduce-off.png +0 -0
- package/src/packages/Cesium/index.md +292 -0
- package/src/packages/Cesium/index.vue +3830 -0
- package/src/packages/Cesium/materials/CircularDiffusionMaterialProperty.js +98 -0
- package/src/packages/Cesium/materials/CircularRippleMaterialProperty.js +126 -0
- package/src/packages/Cesium/materials/PolylineArrowMaterialProperty.js +109 -0
- package/src/packages/Cesium/utils/MeasureHeight.js +261 -0
- package/src/packages/Cesium/utils/MeasureTools.js +1188 -0
- package/src/packages/index.js +33 -0
- package/src/packages/utils/charts.js +54 -0
- package/src/packages/utils/index.js +50 -0
|
@@ -0,0 +1,3830 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="cesium">
|
|
3
|
+
<div :id="id" :style="{ height: height }" v-loading="loading" />
|
|
4
|
+
<!-- 导航工具 -->
|
|
5
|
+
<div v-if="showNavigation" class="navigation-container">
|
|
6
|
+
<!-- 指北针 -->
|
|
7
|
+
<div class="navigation-item">
|
|
8
|
+
<div :style="{ transform: `rotateZ(${360 - cameraInfo.heading}deg)` }" class="compass-outer"></div>
|
|
9
|
+
<div class="compass-inner">
|
|
10
|
+
<div class="compass-orientation">{{ compassOrientation }}</div>
|
|
11
|
+
<div class="compass-angle">{{ parseInt(cameraInfo.heading) }}°</div>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
<!-- 2d/3d地图模式切换 -->
|
|
15
|
+
<div @click="changeMapMode" class="map-action-container map-mode">{{ is3D ? '2D' : '3D' }}</div>
|
|
16
|
+
<!-- 放大缩小 -->
|
|
17
|
+
<div class="widget-zoom">
|
|
18
|
+
<div @click="handleZoomIn" class="map-action-container">
|
|
19
|
+
<img src="./imgs/add-off.png" />
|
|
20
|
+
</div>
|
|
21
|
+
<div @click="handleZoomOut" class="map-action-container">
|
|
22
|
+
<img src="./imgs/reduce-off.png" />
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<!-- 底图 -->
|
|
26
|
+
<el-popover :append-to-body="false" placement="left">
|
|
27
|
+
<div class="base-maps">
|
|
28
|
+
<div class="base-map-tdt">
|
|
29
|
+
<div class="street-selector">
|
|
30
|
+
<el-checkbox @change="changeStreetSelector" v-model="streetSelector">开启路网</el-checkbox>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="base-map-txt">卫星地图-天地图</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<div slot="reference" class="map-switch">
|
|
36
|
+
<div class="img-satellite"></div>
|
|
37
|
+
</div>
|
|
38
|
+
</el-popover>
|
|
39
|
+
</div>
|
|
40
|
+
<Popup ref="cesiumPopupRef" v-if="popupVisible" :show-popup="popupVisible" :popup-data="popupData"
|
|
41
|
+
:popup-position="popupPosition" @close-popup="closePopup" />
|
|
42
|
+
|
|
43
|
+
<el-dialog title="定位" :visible.sync="locationDialogVisible" append-to-body top="0" width="800px"
|
|
44
|
+
:custom-class="'c-dialog c-dialog-no-title c-dialog-mini'">
|
|
45
|
+
<LocationDialog @flyToPoint="flyToPoint" />
|
|
46
|
+
</el-dialog>
|
|
47
|
+
|
|
48
|
+
<el-dialog title="剖面" :visible.sync="poumianDialogVisible" append-to-body top="10%" width="1000px"
|
|
49
|
+
:custom-class="'c-dialog c-dialog-no-title c-dialog-mini'">
|
|
50
|
+
<PoumianDialog :params="poumianDialogParams" :is-cesium="false" />
|
|
51
|
+
</el-dialog>
|
|
52
|
+
|
|
53
|
+
<!-- 后台管理坐标采集框 -->
|
|
54
|
+
<div class="coordinate-collection" v-if="isShowCoordinate">
|
|
55
|
+
<div v-if="dataState == 'update' || dataState == 'add'" class="title">坐标采集框</div>
|
|
56
|
+
<div v-else class="title">坐标</div>
|
|
57
|
+
<div v-if="dataState == 'update' || dataState == 'add'" class="btn-box">
|
|
58
|
+
<div v-if="dratTypeArr.includes('Point')" class="btn-item" :class="[btnSelectIndex == 0 ? 'btn-item-click' : '']" @click="drawBtnClick(0)">
|
|
59
|
+
<img src="./imgs/point.png" alt="" />
|
|
60
|
+
</div>
|
|
61
|
+
<div v-if="dratTypeArr.includes('LineString')" class="btn-item" :class="[btnSelectIndex == 1 ? 'btn-item-click' : '']" @click="drawBtnClick(1)">
|
|
62
|
+
<img src="./imgs/line.png" alt="" />
|
|
63
|
+
</div>
|
|
64
|
+
<div v-if="dratTypeArr.includes('Polygon')" class="btn-item" :class="[btnSelectIndex == 2 ? 'btn-item-click' : '']" @click="drawBtnClick(2)">
|
|
65
|
+
<img src="./imgs/polygon.png" alt="" />
|
|
66
|
+
</div>
|
|
67
|
+
<div class="btn-item" :class="[btnSelectIndex == 3 ? 'btn-item-click' : '']" @click="deleteMangLayerClick">
|
|
68
|
+
<img src="./imgs/map-delete.png" alt="" />
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<el-form ref="positioningForm" :model="positioningData" label-width="60px" label-position="left" class="coordinate-form">
|
|
72
|
+
<el-form-item label="经度" prop="longitude" :rules="{
|
|
73
|
+
required: true,
|
|
74
|
+
message: '请输入经度',
|
|
75
|
+
trigger: 'blur',
|
|
76
|
+
}">
|
|
77
|
+
<div v-if="dataState == 'details'">
|
|
78
|
+
{{ handleCoordinateLength(positioningData.longitude) }}
|
|
79
|
+
</div>
|
|
80
|
+
<el-input-number v-else v-model.trim="positioningData.longitude" style="width: 100%" placeholder="请输入经度" size="small" :controls="false" />
|
|
81
|
+
</el-form-item>
|
|
82
|
+
<el-form-item v-if="!dratTypeArr.includes('Point') && btnSelectIndex != 0" label="至东经" prop="longitudeEnd">
|
|
83
|
+
<el-input-number v-if="false" v-model.trim="positioningData.longitudeEnd" style="width: 100%" placeholder="请输入" :controls="false" />
|
|
84
|
+
<div v-else>
|
|
85
|
+
{{ handleCoordinateLength(positioningData.longitudeEnd) }}
|
|
86
|
+
</div>
|
|
87
|
+
</el-form-item>
|
|
88
|
+
<el-form-item label="纬度" prop="latitude" :rules="{
|
|
89
|
+
required: true,
|
|
90
|
+
message: '请输入纬度',
|
|
91
|
+
trigger: 'blur',
|
|
92
|
+
}">
|
|
93
|
+
<div v-if="dataState == 'details'">
|
|
94
|
+
{{ handleCoordinateLength(positioningData.latitude) }}
|
|
95
|
+
</div>
|
|
96
|
+
<el-input-number v-else v-model.trim="positioningData.latitude" style="width: 100%" placeholder="请输入纬度" size="small" :controls="false" />
|
|
97
|
+
</el-form-item>
|
|
98
|
+
<el-form-item v-if="!dratTypeArr.includes('Point') && btnSelectIndex != 0" label="至北纬" prop="latitudeEnd">
|
|
99
|
+
<!-- v-if="dataState == 'update'" -->
|
|
100
|
+
<el-input-number v-if="false" v-model.trim="positioningData.latitudeEnd" style="width: 100%" placeholder="请输入" :controls="false" />
|
|
101
|
+
<div v-else>
|
|
102
|
+
{{ handleCoordinateLength(positioningData.latitudeEnd) }}
|
|
103
|
+
</div>
|
|
104
|
+
</el-form-item>
|
|
105
|
+
<el-form-item v-show="isClickAnalyze" label="范围" prop="">
|
|
106
|
+
<div style="width: 100%; display: flex; justify-content: space-between">
|
|
107
|
+
<el-input v-model="radius" style="width: 80%" placeholder="请输入分析范围">
|
|
108
|
+
<template slot="append">米</template>
|
|
109
|
+
</el-input>
|
|
110
|
+
<el-button style="margin-left: 10px" icon="el-icon-search" circle></el-button>
|
|
111
|
+
</div>
|
|
112
|
+
</el-form-item>
|
|
113
|
+
</el-form>
|
|
114
|
+
<el-button v-if="dataState != 'details' && btnSelectIndex == 0" type="primary" size="small" style="margin: 0 0 13px; float: right" @click="updateMapBtn">修改</el-button>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</template>
|
|
118
|
+
|
|
119
|
+
<script>
|
|
120
|
+
import * as Cesium from 'cesium';
|
|
121
|
+
import * as turf from "@turf/turf"
|
|
122
|
+
import CesiumNavigation from "cesium-navigation-es6"
|
|
123
|
+
import DrawMap from "./DrawMap"
|
|
124
|
+
import WKT from "terraformer-wkt-parser"
|
|
125
|
+
import Popup from "./components/Popup"
|
|
126
|
+
import LocationDialog from "./components/LocationDialog"
|
|
127
|
+
import PoumianDialog from "./components/PoumianDialog"
|
|
128
|
+
import { deepClone } from "@/packages/utils"
|
|
129
|
+
import MeasureTools from "./utils/MeasureTools.js"
|
|
130
|
+
import MeasureHeight from "./utils/MeasureHeight.js"
|
|
131
|
+
import CircularDiffusionMaterialProperty from "./materials/CircularDiffusionMaterialProperty.js";
|
|
132
|
+
import "@cesium/widgets/Source/widgets.css";
|
|
133
|
+
// import "cesium/Build/Widgets/widgets.css";
|
|
134
|
+
var token = "f536641212889df16a6afaa1beff020b"
|
|
135
|
+
// 服务域名
|
|
136
|
+
var tdtUrl = "https://t{s}.tianditu.gov.cn/"
|
|
137
|
+
// 服务负载子域服务负载子域
|
|
138
|
+
var subdomains = ["0", "1", "2", "3", "4", "5", "6", "7"]
|
|
139
|
+
export default {
|
|
140
|
+
name:'zv-cesium',
|
|
141
|
+
props: {
|
|
142
|
+
params: {
|
|
143
|
+
type: Object,
|
|
144
|
+
default: () => ({
|
|
145
|
+
autoFlyTo: true,
|
|
146
|
+
}),
|
|
147
|
+
},
|
|
148
|
+
height: {
|
|
149
|
+
type: String,
|
|
150
|
+
default: "100%",
|
|
151
|
+
},
|
|
152
|
+
// 导航工具(指北针、2d/3d地图模式切换、放大缩小、底图)
|
|
153
|
+
showNavigation: {
|
|
154
|
+
type: Boolean,
|
|
155
|
+
default: false,
|
|
156
|
+
},
|
|
157
|
+
// 时钟轴
|
|
158
|
+
showTimeline: {
|
|
159
|
+
type: Boolean,
|
|
160
|
+
default: false,
|
|
161
|
+
},
|
|
162
|
+
//缩放倍数
|
|
163
|
+
scale: {
|
|
164
|
+
type: Number,
|
|
165
|
+
default: 1
|
|
166
|
+
},
|
|
167
|
+
//后台管理绘制类型,默认Point
|
|
168
|
+
mangDrawType: {
|
|
169
|
+
type: String,
|
|
170
|
+
default: 'Point'
|
|
171
|
+
},
|
|
172
|
+
//是否展示后台管理坐标采集框
|
|
173
|
+
isShowCoordinate: {
|
|
174
|
+
type: Boolean,
|
|
175
|
+
default: false
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
mixins: [DrawMap],
|
|
179
|
+
components: { Popup, LocationDialog, PoumianDialog },
|
|
180
|
+
computed: {
|
|
181
|
+
is3D () {
|
|
182
|
+
return this.cameraInfo.pitch > -80
|
|
183
|
+
},
|
|
184
|
+
// 指北针方向
|
|
185
|
+
compassOrientation () {
|
|
186
|
+
const heading = parseFloat(this.cameraInfo.heading)
|
|
187
|
+
if (heading >= 337.5 || heading < 22.5) {
|
|
188
|
+
return 'N'
|
|
189
|
+
} else if (heading >= 22.5 && heading < 67.5) {
|
|
190
|
+
return 'NE'
|
|
191
|
+
} else if (heading >= 67.5 && heading < 112.5) {
|
|
192
|
+
return 'E'
|
|
193
|
+
} else if (heading >= 112.5 && heading < 157.5) {
|
|
194
|
+
return 'SE'
|
|
195
|
+
} else if (heading >= 157.5 && heading < 202.5) {
|
|
196
|
+
return 'S'
|
|
197
|
+
} else if (heading >= 202.5 && heading < 247.5) {
|
|
198
|
+
return 'SW'
|
|
199
|
+
} else if (heading >= 247.5 && heading < 292.5) {
|
|
200
|
+
return 'W'
|
|
201
|
+
} else if (heading >= 292.5 && heading < 337.5) {
|
|
202
|
+
return 'NW'
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
dratTypeArr() {
|
|
206
|
+
switch (this.mangDrawType) {
|
|
207
|
+
case "Point":
|
|
208
|
+
return ["Point"];
|
|
209
|
+
case "LineString":
|
|
210
|
+
return ["LineString"];
|
|
211
|
+
case "Polygon":
|
|
212
|
+
return ["Polygon"];
|
|
213
|
+
case "PointPolygon":
|
|
214
|
+
return ["Point", "Polygon"];
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
data () {
|
|
219
|
+
return {
|
|
220
|
+
id: "map_" + Date.now(),
|
|
221
|
+
query: {},
|
|
222
|
+
loading: false,
|
|
223
|
+
viewer: null,
|
|
224
|
+
popupVisible: false,
|
|
225
|
+
popupData: {},
|
|
226
|
+
popupPosition: { x: 0, y: 0 },
|
|
227
|
+
selectedEntity: null,
|
|
228
|
+
layers: [],
|
|
229
|
+
streetSelector: false, // 开启路网注记
|
|
230
|
+
layerClickHandlerWkt: null,
|
|
231
|
+
layerClickHandlerWms: null,
|
|
232
|
+
drawLayerObj: {}, // 前端绘制图层信息存放obj
|
|
233
|
+
isSurrounding: false, // 开启环绕
|
|
234
|
+
surroundHandler: null, // 环绕实例
|
|
235
|
+
currentViewer: {},
|
|
236
|
+
measureTool: null,
|
|
237
|
+
locationDialogVisible: false,
|
|
238
|
+
poumianDialogVisible: false,
|
|
239
|
+
poumianDialogParams: {},
|
|
240
|
+
b3dmTemp: {},
|
|
241
|
+
dataPath: process.env.VUE_APP_NW ? "/bhdjcweb" : "", // 本地测试时,http://dev.vanone.com
|
|
242
|
+
cameraInfo: {
|
|
243
|
+
height: 0,
|
|
244
|
+
longitude: 0,
|
|
245
|
+
latitude: 0,
|
|
246
|
+
pitch: 0,
|
|
247
|
+
heading: 0,
|
|
248
|
+
roll: 0,
|
|
249
|
+
},
|
|
250
|
+
terrainSmallSelect: -1, // 地形模式小框选择的按钮
|
|
251
|
+
terrainCacheMap: new Map(), //地形高度缓存
|
|
252
|
+
mouseHandler: null, // 鼠标移动事件处理器
|
|
253
|
+
drawHandler: null, // 绘制事件处理器
|
|
254
|
+
hh: null,
|
|
255
|
+
_arrowDimensionCache: null, // 箭头尺寸计算缓存
|
|
256
|
+
dataState: 'update',//显示后台管理地图当前状态
|
|
257
|
+
btnSelectIndex: -1,//绘制类型选择
|
|
258
|
+
positioningData: {
|
|
259
|
+
// 坐标采集的经纬度
|
|
260
|
+
longitude: undefined,
|
|
261
|
+
latitude: undefined,
|
|
262
|
+
longitudeEnd: undefined,
|
|
263
|
+
latitudeEnd: undefined,
|
|
264
|
+
},
|
|
265
|
+
isClickAnalyze: false,
|
|
266
|
+
radius: 5000,
|
|
267
|
+
mangDrawLayerName: 'draw_layers',//后台绘制图层统一名称
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
created () {
|
|
271
|
+
this._arrowDimensionCache = {
|
|
272
|
+
lastFrame: -1,
|
|
273
|
+
cache: new Map(),
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
mounted () {
|
|
277
|
+
this.query = Object.assign({}, this.params)
|
|
278
|
+
this.init()
|
|
279
|
+
this.measureTool = new MeasureTools(this.viewer)
|
|
280
|
+
},
|
|
281
|
+
destroyed () {
|
|
282
|
+
this.destroy()
|
|
283
|
+
},
|
|
284
|
+
methods: {
|
|
285
|
+
// 2d/3d地图模式切换
|
|
286
|
+
changeMapMode () {
|
|
287
|
+
const viewer = this.viewer
|
|
288
|
+
const scene = viewer.scene
|
|
289
|
+
// 地图中心点
|
|
290
|
+
const center = new Cesium.Cartesian2(viewer.canvas.clientWidth / 2, viewer.canvas.clientHeight / 2)
|
|
291
|
+
const ray = scene.camera.getPickRay(center)
|
|
292
|
+
const centerPosition = scene.globe.pick(ray, scene)
|
|
293
|
+
const cartographic = Cesium.Cartographic.fromCartesian(centerPosition)
|
|
294
|
+
const longitude = Cesium.Math.toDegrees(cartographic.longitude)
|
|
295
|
+
const latitude = Cesium.Math.toDegrees(cartographic.latitude)
|
|
296
|
+
const { height } = scene.camera.positionCartographic
|
|
297
|
+
let { heading } = scene.camera
|
|
298
|
+
let centerCartesianPosition = Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
|
|
299
|
+
let newPosition
|
|
300
|
+
if (this.is3D) {
|
|
301
|
+
newPosition = centerCartesianPosition
|
|
302
|
+
} else {
|
|
303
|
+
const hpr = new Cesium.HeadingPitchRoll(
|
|
304
|
+
heading,
|
|
305
|
+
Cesium.Math.toRadians(0),
|
|
306
|
+
Cesium.Math.toRadians(0)
|
|
307
|
+
)
|
|
308
|
+
const orientation = Cesium.Transforms.headingPitchRollQuaternion(centerCartesianPosition, hpr)
|
|
309
|
+
const rotationMatrix = Cesium.Matrix3.fromQuaternion(orientation)
|
|
310
|
+
const forward = Cesium.Matrix3.getColumn(rotationMatrix, 1, new Cesium.Cartesian3())
|
|
311
|
+
|
|
312
|
+
const moveDistance = (height - cartographic.height) * Math.tan(Cesium.Math.toRadians(45))
|
|
313
|
+
newPosition = Cesium.Cartesian3.add(
|
|
314
|
+
centerCartesianPosition,
|
|
315
|
+
Cesium.Cartesian3.multiplyByScalar(forward, -moveDistance, new Cesium.Cartesian3()),
|
|
316
|
+
new Cesium.Cartesian3()
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
viewer.camera.flyTo({
|
|
320
|
+
destination: newPosition,
|
|
321
|
+
orientation: {
|
|
322
|
+
heading: scene.camera.heading,
|
|
323
|
+
pitch: this.is3D ? Cesium.Math.toRadians(-90) : Cesium.Math.toRadians(-45),
|
|
324
|
+
roll: 0
|
|
325
|
+
}
|
|
326
|
+
})
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
handleZoomIn () {
|
|
330
|
+
const camera = this.viewer.camera
|
|
331
|
+
const zoomFactor = 0.1 // 调整倍率,值越小,放大越多
|
|
332
|
+
camera.zoomIn(zoomFactor * this.viewer.camera.positionCartographic.height)
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
handleZoomOut () {
|
|
336
|
+
const camera = this.viewer.camera
|
|
337
|
+
const zoomFactor = 0.1 // 调整倍率,值越小,缩小越多
|
|
338
|
+
camera.zoomOut(zoomFactor * camera.positionCartographic.height)
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
processGeoJSONData (data) {
|
|
342
|
+
const list = []
|
|
343
|
+
data.features.forEach((feature) => {
|
|
344
|
+
const { coordinates, type } = feature.geometry
|
|
345
|
+
if (type === "MultiPolygon") {
|
|
346
|
+
coordinates.forEach((part) => {
|
|
347
|
+
const coords = []
|
|
348
|
+
part[0].forEach((point) => {
|
|
349
|
+
coords.push(...point)
|
|
350
|
+
})
|
|
351
|
+
list.push(coords)
|
|
352
|
+
})
|
|
353
|
+
} else {
|
|
354
|
+
const coords = []
|
|
355
|
+
coordinates[0].forEach((point) => {
|
|
356
|
+
coords.push(...point)
|
|
357
|
+
})
|
|
358
|
+
list.push(coords)
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
return list
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
loadMaskPrimitive (layer) {
|
|
365
|
+
fetch("/vendor/data-map-json/36.geojson")
|
|
366
|
+
.then((response) => {
|
|
367
|
+
if (response.ok) {
|
|
368
|
+
return response.json()
|
|
369
|
+
} else {
|
|
370
|
+
throw new Error("请求失败!")
|
|
371
|
+
}
|
|
372
|
+
})
|
|
373
|
+
.then((data) => {
|
|
374
|
+
// console.log('data', data);
|
|
375
|
+
const list = this.processGeoJSONData(data)
|
|
376
|
+
// const polygonEntities = this.getPolygonPrimitive(list);
|
|
377
|
+
// // const wallPrimitive = this.getWallPrimitive(list);
|
|
378
|
+
|
|
379
|
+
// // 添加多边形遮罩到图层
|
|
380
|
+
// polygonEntities.forEach((entity) => {
|
|
381
|
+
// layer.entities.add(entity);
|
|
382
|
+
// });
|
|
383
|
+
|
|
384
|
+
// // 如果需要也可以添加墙体遮罩(如果墙体没有效果,可以忽略)
|
|
385
|
+
// this.viewer.scene.primitives.add(wallPrimitive);
|
|
386
|
+
|
|
387
|
+
// const inverseMaskPrimitive = this.getInverseMaskPrimitive(list);
|
|
388
|
+
|
|
389
|
+
// // 将遮罩添加到 Cesium 场景中
|
|
390
|
+
// this.viewer.scene.primitives.add(inverseMaskPrimitive);
|
|
391
|
+
|
|
392
|
+
// // 添加反转遮罩的多边形
|
|
393
|
+
// const inverseMaskPolygonEntity = this.getInverseMaskPolygonEntity(list);
|
|
394
|
+
// console.log('inverseMaskPolygonEntity', inverseMaskPolygonEntity);
|
|
395
|
+
// layer.entities.add(inverseMaskPolygonEntity);
|
|
396
|
+
|
|
397
|
+
// // 获取墙体实体列表并添加
|
|
398
|
+
// const wallEntities = this.getWallEntity(list);
|
|
399
|
+
// console.log('wallEntities', wallEntities);
|
|
400
|
+
// wallEntities.forEach((entity) => {
|
|
401
|
+
// layer.entities.add(entity);
|
|
402
|
+
// });
|
|
403
|
+
|
|
404
|
+
let arr = list[0]
|
|
405
|
+
|
|
406
|
+
var polygonWithHole = new Cesium.PolygonGeometry({
|
|
407
|
+
polygonHierarchy: new Cesium.PolygonHierarchy(
|
|
408
|
+
Cesium.Cartesian3.fromDegreesArray([
|
|
409
|
+
73.0, 53.0, 73.0, 0.0, 135.0, 0.0, 135.0, 53.0,
|
|
410
|
+
]),
|
|
411
|
+
[
|
|
412
|
+
new Cesium.PolygonHierarchy(
|
|
413
|
+
Cesium.Cartesian3.fromDegreesArray(arr)
|
|
414
|
+
),
|
|
415
|
+
]
|
|
416
|
+
),
|
|
417
|
+
})
|
|
418
|
+
var geometry = Cesium.PolygonGeometry.createGeometry(polygonWithHole)
|
|
419
|
+
let instances = []
|
|
420
|
+
instances.push(
|
|
421
|
+
new Cesium.GeometryInstance({
|
|
422
|
+
geometry: geometry,
|
|
423
|
+
attributes: {
|
|
424
|
+
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
|
425
|
+
Cesium.Color.BLACK.withAlpha(0.5)
|
|
426
|
+
),
|
|
427
|
+
// disableDepthTestDistance: Number.POSITIVE_INFINITY, // 禁用深度检测
|
|
428
|
+
},
|
|
429
|
+
})
|
|
430
|
+
)
|
|
431
|
+
this.viewer.scene.primitives.add(
|
|
432
|
+
new Cesium.Primitive({
|
|
433
|
+
geometryInstances: instances,
|
|
434
|
+
appearance: new Cesium.PerInstanceColorAppearance({
|
|
435
|
+
flat: true,
|
|
436
|
+
translucent: true, // 开启透明度支持
|
|
437
|
+
renderState: {
|
|
438
|
+
depthTest: {
|
|
439
|
+
enabled: false, // 禁用深度测试(如果需要)
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
}),
|
|
443
|
+
})
|
|
444
|
+
)
|
|
445
|
+
})
|
|
446
|
+
.catch((error) => {
|
|
447
|
+
console.log(error)
|
|
448
|
+
})
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
// 工具
|
|
452
|
+
handleTool (obj, viewer = this.viewer) {
|
|
453
|
+
if (!obj.type) return
|
|
454
|
+
|
|
455
|
+
const data = {
|
|
456
|
+
color: "#EA3535",
|
|
457
|
+
fontSize: 30,
|
|
458
|
+
pxNum: 2,
|
|
459
|
+
// callback: this.drawCallbackGJ,
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// 测距离
|
|
463
|
+
if (obj.type == "LineString") {
|
|
464
|
+
if (obj.clear) {
|
|
465
|
+
this.measureTool._drawLayer.entities.removeAll()
|
|
466
|
+
return
|
|
467
|
+
}
|
|
468
|
+
this.measureTool.drawLineMeasureGraphics({
|
|
469
|
+
clampToGround: true,
|
|
470
|
+
measure: true,
|
|
471
|
+
style: {
|
|
472
|
+
line: {
|
|
473
|
+
width: 2,
|
|
474
|
+
material: Cesium.Color.fromCssColorString("#ffff00"),
|
|
475
|
+
},
|
|
476
|
+
point: {
|
|
477
|
+
color: Cesium.Color.RED,
|
|
478
|
+
pixelSize: 5,
|
|
479
|
+
outlineColor: Cesium.Color.GREEN,
|
|
480
|
+
outlineWidth: 3,
|
|
481
|
+
show: false, //默认是显示点位
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
callback: (e) => {
|
|
485
|
+
console.log(e, "88888888888")
|
|
486
|
+
},
|
|
487
|
+
})
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// 测面积
|
|
491
|
+
if (obj.type == "Polygon") {
|
|
492
|
+
if (obj.clear) {
|
|
493
|
+
this.measureTool._drawLayer.entities.removeAll()
|
|
494
|
+
return
|
|
495
|
+
}
|
|
496
|
+
this.measureTool.drawAreaMeasureGraphics({
|
|
497
|
+
clampToGround: true,
|
|
498
|
+
measure: true,
|
|
499
|
+
style: {
|
|
500
|
+
line: {
|
|
501
|
+
width: 2,
|
|
502
|
+
material: Cesium.Color.fromCssColorString("#0099ff"),
|
|
503
|
+
show: true, //默认为true
|
|
504
|
+
},
|
|
505
|
+
point: {
|
|
506
|
+
pixelSize: 5,
|
|
507
|
+
outlineColor: Cesium.Color.BLUE,
|
|
508
|
+
outlineWidth: 2,
|
|
509
|
+
show: false, //默认为true
|
|
510
|
+
},
|
|
511
|
+
//如果不设置centerPoint则会把测量的位置现在在最后一个点击的位置
|
|
512
|
+
centerPoint: {
|
|
513
|
+
pixelSize: 5,
|
|
514
|
+
outlineColor: Cesium.Color.RED,
|
|
515
|
+
outlineWidth: 2,
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
callback: (e) => {
|
|
519
|
+
// console.log(e, "88888888888");
|
|
520
|
+
},
|
|
521
|
+
})
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// 测高度
|
|
525
|
+
if (obj.type === "Height") {
|
|
526
|
+
if (!this.hh) {
|
|
527
|
+
this.hh = new MeasureHeight(viewer)
|
|
528
|
+
}
|
|
529
|
+
this.hh.activate()
|
|
530
|
+
// const ii = new measureLineFlat(viewer, 5);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// 测坡度
|
|
534
|
+
if (obj.type === "Slope") {
|
|
535
|
+
if (obj.clear) {
|
|
536
|
+
this.measureTool._drawLayer.entities.removeAll()
|
|
537
|
+
return
|
|
538
|
+
}
|
|
539
|
+
this.drawTypeFunctionGJ("CEPODU")
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// 测剖面
|
|
543
|
+
if (obj.type === "Section") {
|
|
544
|
+
// this.drawTypeFunctionGJ("CEPOUMIAN", data);
|
|
545
|
+
|
|
546
|
+
if (obj.clear) {
|
|
547
|
+
this.measureTool._drawLayer.entities.removeAll()
|
|
548
|
+
return
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
this.measureTool.drawLineMeasureGraphics({
|
|
552
|
+
showPoumian: true,
|
|
553
|
+
clampToGround: true,
|
|
554
|
+
measure: true,
|
|
555
|
+
style: {
|
|
556
|
+
line: {
|
|
557
|
+
width: 2,
|
|
558
|
+
material: Cesium.Color.fromCssColorString("#ffff00"),
|
|
559
|
+
},
|
|
560
|
+
point: {
|
|
561
|
+
color: Cesium.Color.RED,
|
|
562
|
+
pixelSize: 5,
|
|
563
|
+
outlineColor: Cesium.Color.GREEN,
|
|
564
|
+
outlineWidth: 3,
|
|
565
|
+
show: false, //默认是显示点位
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
callback: (e) => {
|
|
569
|
+
let data = []
|
|
570
|
+
this.loading = true
|
|
571
|
+
e.poumian.forEach((v, k) => {
|
|
572
|
+
data.push({
|
|
573
|
+
title: k + 1,
|
|
574
|
+
value: v,
|
|
575
|
+
})
|
|
576
|
+
})
|
|
577
|
+
setTimeout(() => {
|
|
578
|
+
this.poumianDialogParams = {
|
|
579
|
+
id: "PoumianChart",
|
|
580
|
+
data: data,
|
|
581
|
+
scaleNum: this.params.scaleNum,
|
|
582
|
+
}
|
|
583
|
+
this.loading = false
|
|
584
|
+
this.poumianDialogVisible = true
|
|
585
|
+
}, 500)
|
|
586
|
+
},
|
|
587
|
+
})
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// 环绕
|
|
591
|
+
if (obj.type === "Surround") {
|
|
592
|
+
this.toggleSurround()
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (obj.type === "Location") {
|
|
596
|
+
this.locationDialogVisible = true
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (obj.type === "ZoomIn") {
|
|
600
|
+
this.handleZoomIn()
|
|
601
|
+
}
|
|
602
|
+
if (obj.type === "ZoomOut") {
|
|
603
|
+
this.handleZoomOut()
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
|
|
607
|
+
// 销毁
|
|
608
|
+
destroy () {
|
|
609
|
+
// 销毁鼠标事件处理器
|
|
610
|
+
if (this.mouseHandler) {
|
|
611
|
+
this.mouseHandler.destroy()
|
|
612
|
+
this.mouseHandler = null
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (Cesium.defined(this.viewer)) {
|
|
616
|
+
this.viewer.entities.removeAll()
|
|
617
|
+
this.viewer.imageryLayers.removeAll()
|
|
618
|
+
this.viewer.dataSources.removeAll()
|
|
619
|
+
// viewer.scene.primitives.removeAll();
|
|
620
|
+
// 获取webgl上下文
|
|
621
|
+
let gl = this.viewer.scene.context._originalGLContext
|
|
622
|
+
gl.canvas.width = 1
|
|
623
|
+
gl.canvas.height = 1
|
|
624
|
+
this.viewer.destroy() // 销毁Viewer实例
|
|
625
|
+
gl.getExtension("WEBGL_lose_context").loseContext()
|
|
626
|
+
gl = null
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
|
|
630
|
+
setCesiumStyle (style) {
|
|
631
|
+
let cesiumStyle = {}
|
|
632
|
+
if (style.fill) {
|
|
633
|
+
cesiumStyle.material = Cesium.Color.fromCssColorString(
|
|
634
|
+
style.fill.color || "#ff0000"
|
|
635
|
+
)
|
|
636
|
+
}
|
|
637
|
+
if (style.stroke) {
|
|
638
|
+
cesiumStyle.outline = true
|
|
639
|
+
cesiumStyle.outlineColor = Cesium.Color.fromCssColorString(
|
|
640
|
+
style.stroke.color || "#ff0000"
|
|
641
|
+
)
|
|
642
|
+
cesiumStyle.outlineWidth = style.stroke.width || 2
|
|
643
|
+
}
|
|
644
|
+
return cesiumStyle
|
|
645
|
+
},
|
|
646
|
+
|
|
647
|
+
async drawCircleByWkt (circleObj) {
|
|
648
|
+
if (!circleObj) return
|
|
649
|
+
|
|
650
|
+
if (this.drawLayerObj[circleObj.name]) {
|
|
651
|
+
this.drawLayerObj[circleObj.name].forEach((entity) => {
|
|
652
|
+
this.viewer.entities.remove(entity)
|
|
653
|
+
})
|
|
654
|
+
this.drawLayerObj[circleObj.name] = []
|
|
655
|
+
}
|
|
656
|
+
let wkts = Array.isArray(circleObj.wkt) ? circleObj.wkt : [circleObj.wkt]
|
|
657
|
+
let entities = []
|
|
658
|
+
let style = circleObj.style || {}
|
|
659
|
+
let cesiumStyle = this.setCesiumStyle(style)
|
|
660
|
+
let ellipse = {
|
|
661
|
+
semiMinorAxis: circleObj.distance,
|
|
662
|
+
semiMajorAxis: circleObj.distance,
|
|
663
|
+
material: cesiumStyle.material || Cesium.Color.RED.withAlpha(0.5),
|
|
664
|
+
outline: cesiumStyle.outline || false,
|
|
665
|
+
outlineColor: cesiumStyle.outlineColor || Cesium.Color.BLACK,
|
|
666
|
+
outlineWidth: cesiumStyle.outlineWidth || 1,
|
|
667
|
+
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
wkts.forEach((wkt) => {
|
|
671
|
+
if (typeof wkt !== "string" || wkt.indexOf("POINT") === -1) return
|
|
672
|
+
const geojson = WKT.parse(wkt)
|
|
673
|
+
const coordinates = geojson.coordinates
|
|
674
|
+
|
|
675
|
+
const entity = this.viewer.entities.add({
|
|
676
|
+
name: circleObj.name,
|
|
677
|
+
position: Cesium.Cartesian3.fromDegrees(
|
|
678
|
+
coordinates[0],
|
|
679
|
+
coordinates[1],
|
|
680
|
+
0
|
|
681
|
+
),
|
|
682
|
+
ellipse: ellipse,
|
|
683
|
+
})
|
|
684
|
+
entities.push(entity)
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
this.drawLayerObj[circleObj.name] = entities
|
|
688
|
+
|
|
689
|
+
if (circleObj.fit && entities.length) {
|
|
690
|
+
this.viewer.flyTo(entities)
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
|
|
694
|
+
// 初始化
|
|
695
|
+
async init () {
|
|
696
|
+
this.viewer = new Cesium.Viewer(this.id, {
|
|
697
|
+
homeButton: false, // 视角返回初始位置
|
|
698
|
+
sceneModePicker: false, // 控制是3d还是
|
|
699
|
+
baseLayerPicker: false, // 影像切换
|
|
700
|
+
animation: false, // 是否显示动画控件
|
|
701
|
+
infoBox: false, // 是否显示点击要素之后显示的信息
|
|
702
|
+
selectionIndicator: false, // 要素选中框
|
|
703
|
+
geocoder: false, // 是否显示地名查找控件
|
|
704
|
+
timeline: this.showTimeline, // 是否显示时间线控件
|
|
705
|
+
fullscreenButton: false, //全屏
|
|
706
|
+
shouldAnimate: true, //是否允许动画
|
|
707
|
+
navigationHelpButton: false, // 是否显示帮助信息控件
|
|
708
|
+
imageryProvider: false, // 禁用默认的影像图层
|
|
709
|
+
})
|
|
710
|
+
|
|
711
|
+
// 抗锯齿
|
|
712
|
+
this.viewer.scene.fxaa = true
|
|
713
|
+
this.viewer.scene.postProcessStages.fxaa.enabled = true
|
|
714
|
+
// 水雾特效
|
|
715
|
+
this.viewer.scene.globe.showGroundAtmosphere = true
|
|
716
|
+
// 开启深度检测
|
|
717
|
+
this.viewer.scene.globe.depthTestAgainstTerrain = true
|
|
718
|
+
// 隐藏Cesium logo
|
|
719
|
+
this.viewer.cesiumWidget.creditContainer.style.display = "none"
|
|
720
|
+
|
|
721
|
+
if (!process.env.VUE_APP_TIAN_MAP_ZOOM) {
|
|
722
|
+
// 叠加影像服务
|
|
723
|
+
var imgMap = new Cesium.UrlTemplateImageryProvider({
|
|
724
|
+
url: tdtUrl + "DataServer?T=img_w&x={x}&y={y}&l={z}&tk=" + token,
|
|
725
|
+
subdomains: subdomains,
|
|
726
|
+
tilingScheme: new Cesium.WebMercatorTilingScheme(),
|
|
727
|
+
maximumLevel: 18,
|
|
728
|
+
})
|
|
729
|
+
this.viewer.imageryLayers.addImageryProvider(imgMap)
|
|
730
|
+
|
|
731
|
+
// 叠加国界服务
|
|
732
|
+
var iboMap = new Cesium.UrlTemplateImageryProvider({
|
|
733
|
+
url: tdtUrl + "DataServer?T=ibo_w&x={x}&y={y}&l={z}&tk=" + token,
|
|
734
|
+
subdomains: subdomains,
|
|
735
|
+
tilingScheme: new Cesium.WebMercatorTilingScheme(),
|
|
736
|
+
maximumLevel: 10,
|
|
737
|
+
})
|
|
738
|
+
this.viewer.imageryLayers.addImageryProvider(iboMap)
|
|
739
|
+
} else {
|
|
740
|
+
// 叠加影像服务 使用地理坐标系
|
|
741
|
+
var imgMap = new Cesium.UrlTemplateImageryProvider({
|
|
742
|
+
// url: tdtUrl + 'DataServer?T=img_c&x={xx}&y={yy}&l={zz}&tk=' + token,
|
|
743
|
+
url: `${process.env.VUE_APP_TIAN_API}/imageMap/file/management/imageShow/onemap21/{zz}/{xx}/{yy}`,
|
|
744
|
+
customTags: {
|
|
745
|
+
zz: function (imageryProvider, x, y, z) {
|
|
746
|
+
return z + 1
|
|
747
|
+
},
|
|
748
|
+
xx: function (imageryProvider, x, y, z) {
|
|
749
|
+
return x
|
|
750
|
+
},
|
|
751
|
+
yy: function (imageryProvider, x, y, z) {
|
|
752
|
+
return y
|
|
753
|
+
},
|
|
754
|
+
},
|
|
755
|
+
subdomains: subdomains,
|
|
756
|
+
tilingScheme: new Cesium.GeographicTilingScheme({
|
|
757
|
+
ellipsoid: Cesium.Ellipsoid.WGS84,
|
|
758
|
+
numberOfLevelZeroTilesX: 2,
|
|
759
|
+
numberOfLevelZeroTilesY: 1,
|
|
760
|
+
}),
|
|
761
|
+
// rectangle: Cesium.Rectangle.fromDegrees(-180, -90, 180, 90), // 地理坐标系的全局范围
|
|
762
|
+
layer: "img",
|
|
763
|
+
style: "default",
|
|
764
|
+
format: "image/png",
|
|
765
|
+
tileMatrixSetID: "EPSG:4326_img",
|
|
766
|
+
maximumLevel: 14,
|
|
767
|
+
})
|
|
768
|
+
this.viewer.imageryLayers.addImageryProvider(imgMap)
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// 叠加地形服务
|
|
772
|
+
let provider = new Cesium.CesiumTerrainProvider({
|
|
773
|
+
url: "http://data.mars3d.cn/terrain",
|
|
774
|
+
requestWaterMask: true,
|
|
775
|
+
requestVertexNormals: true,
|
|
776
|
+
maximumScreenSpaceError: 8
|
|
777
|
+
})
|
|
778
|
+
this.viewer.terrainProvider = provider
|
|
779
|
+
const options = {
|
|
780
|
+
// 设置罗盘重置后的点位
|
|
781
|
+
defaultResetView: Cesium.Rectangle.fromDegrees(
|
|
782
|
+
113.3411, // 江西-西经
|
|
783
|
+
23.5, // 江西-南纬
|
|
784
|
+
118.6553, // 江西-东经
|
|
785
|
+
30.8 // 江西-北纬
|
|
786
|
+
),
|
|
787
|
+
enableCompass: false,
|
|
788
|
+
enableZoomControls: false,
|
|
789
|
+
enableDistanceLegend: true,
|
|
790
|
+
enableCompassOuterRing: false,
|
|
791
|
+
}
|
|
792
|
+
new CesiumNavigation(this.viewer, options)
|
|
793
|
+
|
|
794
|
+
// // 创建 CustomDataSource 图层
|
|
795
|
+
// const maskLayer = new Cesium.CustomDataSource("MaskLayer");
|
|
796
|
+
|
|
797
|
+
// // 将图层添加到 Viewer 中
|
|
798
|
+
// this.viewer.dataSources.add(maskLayer);
|
|
799
|
+
|
|
800
|
+
// // 调用 loadMaskPrimitive,将 customLayer 作为参数传入
|
|
801
|
+
// this.loadMaskPrimitive(maskLayer);
|
|
802
|
+
|
|
803
|
+
if (this.query.autoFlyTo) {
|
|
804
|
+
this.initFlyTo(this.viewer)
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
this.getCameraInfo()
|
|
808
|
+
},
|
|
809
|
+
|
|
810
|
+
getCameraInfo (viewer = this.viewer) {
|
|
811
|
+
viewer.camera.percentageChanged = 0.01
|
|
812
|
+
viewer.camera.changed.addEventListener(() => {
|
|
813
|
+
var camera = viewer.camera
|
|
814
|
+
var cartographic = camera.positionCartographic
|
|
815
|
+
|
|
816
|
+
// 获取相机的高度、经度、纬度
|
|
817
|
+
this.cameraInfo.height = cartographic.height.toFixed(2)
|
|
818
|
+
this.cameraInfo.longitude = Cesium.Math.toDegrees(
|
|
819
|
+
cartographic.longitude
|
|
820
|
+
).toFixed(6)
|
|
821
|
+
this.cameraInfo.latitude = Cesium.Math.toDegrees(
|
|
822
|
+
cartographic.latitude
|
|
823
|
+
).toFixed(6)
|
|
824
|
+
|
|
825
|
+
// 获取相机的朝向角度
|
|
826
|
+
this.cameraInfo.pitch = Cesium.Math.toDegrees(camera.pitch).toFixed(2)
|
|
827
|
+
this.cameraInfo.heading = Cesium.Math.toDegrees(camera.heading).toFixed(
|
|
828
|
+
2
|
|
829
|
+
)
|
|
830
|
+
this.cameraInfo.roll = Cesium.Math.toDegrees(camera.roll).toFixed(2)
|
|
831
|
+
})
|
|
832
|
+
},
|
|
833
|
+
|
|
834
|
+
async addB3dmLayer (params, viewer = this.viewer) {
|
|
835
|
+
if (this.tileset) viewer.scene.primitives.remove(this.tileset)
|
|
836
|
+
this.tileset = new Cesium.Cesium3DTileset({
|
|
837
|
+
url: params.url,
|
|
838
|
+
maximumScreenSpaceError: 1,
|
|
839
|
+
maximumMemoryUsage: 1024,
|
|
840
|
+
preloadWhenHidden: true,
|
|
841
|
+
preloadFlightDestinations: true,
|
|
842
|
+
dynamicScreenSpaceError: true,
|
|
843
|
+
dynamicScreenSpaceErrorDensity: 0.001,
|
|
844
|
+
dynamicScreenSpaceErrorFactor: 4.0,
|
|
845
|
+
dynamicScreenSpaceErrorHeightFalloff: 0.25
|
|
846
|
+
})
|
|
847
|
+
viewer.scene.primitives.add(this.tileset)
|
|
848
|
+
const scene = viewer.scene
|
|
849
|
+
scene.globe.depthTestAgainstTerrain = false
|
|
850
|
+
// 等待模型完全加载
|
|
851
|
+
await this.tileset.readyPromise
|
|
852
|
+
this.viewer.flyTo(this.tileset)
|
|
853
|
+
},
|
|
854
|
+
|
|
855
|
+
async addGltfLayer (params, viewer = this.viewer) {
|
|
856
|
+
params.scale = params.scale ? params.scale : 2.0
|
|
857
|
+
|
|
858
|
+
params.size = params.size ? params.size : 5
|
|
859
|
+
|
|
860
|
+
// 恢复默认尺寸
|
|
861
|
+
params.scale = 1
|
|
862
|
+
|
|
863
|
+
let geoJson = WKT.parse(params.boundaryLine)
|
|
864
|
+
// console.log('geoJson', geoJson);
|
|
865
|
+
const coordinates = geoJson.coordinates
|
|
866
|
+
// console.log('coordinates', coordinates);
|
|
867
|
+
let longitude = coordinates[0]
|
|
868
|
+
let latitude = coordinates[1]
|
|
869
|
+
|
|
870
|
+
// let height = await this.getPointHeight([longitude, latitude], viewer);
|
|
871
|
+
let height = 0
|
|
872
|
+
// console.log('gltfHeight', height);
|
|
873
|
+
|
|
874
|
+
const modelPosition = Cesium.Cartesian3.fromDegrees(
|
|
875
|
+
longitude,
|
|
876
|
+
latitude,
|
|
877
|
+
height
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
const model = Cesium.Model.fromGltf({
|
|
881
|
+
url: params.url,
|
|
882
|
+
modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(modelPosition),
|
|
883
|
+
scale: params.scale,
|
|
884
|
+
})
|
|
885
|
+
|
|
886
|
+
viewer.scene.primitives.add(model)
|
|
887
|
+
|
|
888
|
+
// 使用 readyPromise 确保模型加载完成
|
|
889
|
+
model.readyPromise
|
|
890
|
+
.then(() => {
|
|
891
|
+
// console.log('模型加载完成', params);
|
|
892
|
+
const boundingSphere = model.boundingSphere
|
|
893
|
+
const optimalDistance = boundingSphere.radius * params.size // 根据模型大小调整距离
|
|
894
|
+
|
|
895
|
+
const modelPosition1 = Cesium.Cartesian3.fromDegrees(
|
|
896
|
+
longitude,
|
|
897
|
+
latitude,
|
|
898
|
+
height + params.size
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
// 以模型的实际地理位置为中心,设置视角
|
|
902
|
+
viewer.camera.lookAt(
|
|
903
|
+
modelPosition1,
|
|
904
|
+
new Cesium.HeadingPitchRange(
|
|
905
|
+
Cesium.Math.toRadians(-90.0), // 水平视角
|
|
906
|
+
Cesium.Math.toRadians(-10.0), // 轻微仰视角度
|
|
907
|
+
optimalDistance // 适当距离
|
|
908
|
+
)
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
// 可选:解除相机锁定,以便用户自由控制视角
|
|
912
|
+
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
|
|
913
|
+
|
|
914
|
+
viewer.scene.screenSpaceCameraController.inertiaZoom = 0
|
|
915
|
+
viewer.scene.screenSpaceCameraController.minimumZoomDistance = 10 // 5 相机的高度的最小值
|
|
916
|
+
viewer.scene.screenSpaceCameraController.maximumZoomDistance = 1000 // 相机高度的最大值
|
|
917
|
+
viewer.scene.screenSpaceCameraController._minimumZoomRate = 100 // 50 设置相机缩小时的速率
|
|
918
|
+
viewer.scene.screenSpaceCameraController._maximumZoomRate = 200 // 50 设置相机放大时的速率
|
|
919
|
+
})
|
|
920
|
+
.catch((error) => {
|
|
921
|
+
console.error("模型加载失败:", error)
|
|
922
|
+
})
|
|
923
|
+
},
|
|
924
|
+
|
|
925
|
+
// 定位到中国
|
|
926
|
+
initFlyTo (viewer = this.viewer) {
|
|
927
|
+
// 调整为直接定位到江西
|
|
928
|
+
viewer.camera.flyTo({
|
|
929
|
+
destination: Cesium.Rectangle.fromDegrees(
|
|
930
|
+
113.3411, // 西经
|
|
931
|
+
23.5, // 南纬
|
|
932
|
+
118.6553, // 东经
|
|
933
|
+
30.8 // 北纬
|
|
934
|
+
),
|
|
935
|
+
duration: 3, // 飞行持续时间(秒)
|
|
936
|
+
})
|
|
937
|
+
},
|
|
938
|
+
|
|
939
|
+
flyToDefault () {
|
|
940
|
+
this.viewer.camera.flyTo({
|
|
941
|
+
destination: Cesium.Rectangle.fromDegrees(
|
|
942
|
+
113.3411, // 西经
|
|
943
|
+
23.5, // 南纬
|
|
944
|
+
118.6553, // 东经
|
|
945
|
+
30.8 // 北纬
|
|
946
|
+
),
|
|
947
|
+
orientation: {
|
|
948
|
+
heading: Cesium.Math.toRadians(350),
|
|
949
|
+
pitch: Cesium.Math.toRadians(-90),
|
|
950
|
+
roll: Cesium.Math.toRadians(10),
|
|
951
|
+
},
|
|
952
|
+
duration: 3, // 飞行持续时间(秒)
|
|
953
|
+
})
|
|
954
|
+
},
|
|
955
|
+
|
|
956
|
+
// 定位到点位环绕
|
|
957
|
+
async flyToPoint (params, viewer = this.viewer) {
|
|
958
|
+
if (this.locationDialogVisible) {
|
|
959
|
+
this.locationDialogVisible = false
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// 将wkt格式转化为geojson
|
|
963
|
+
let geoJson = params.point
|
|
964
|
+
if (!geoJson || !this.isWkt(geoJson)) {
|
|
965
|
+
console.log("后端数据不严谨,经纬度为null")
|
|
966
|
+
}
|
|
967
|
+
geoJson = WKT.parse(geoJson)
|
|
968
|
+
// console.log(geoJson); return;
|
|
969
|
+
|
|
970
|
+
const coordinates = geoJson.coordinates
|
|
971
|
+
|
|
972
|
+
let height = params.height || 0
|
|
973
|
+
let heading = params.heading
|
|
974
|
+
? Cesium.Math.toRadians(params.heading)
|
|
975
|
+
: viewer.scene.camera.heading
|
|
976
|
+
let pitch = params.pitch
|
|
977
|
+
? Cesium.Math.toRadians(params.pitch)
|
|
978
|
+
: viewer.scene.camera.pitch
|
|
979
|
+
let roll = params.roll
|
|
980
|
+
? Cesium.Math.toRadians(params.roll)
|
|
981
|
+
: viewer.scene.camera.roll
|
|
982
|
+
|
|
983
|
+
if (params.surround) {
|
|
984
|
+
// 开启环绕
|
|
985
|
+
|
|
986
|
+
// 高度需要定死,这样在环绕函数里设置的拉远的距离才会一样
|
|
987
|
+
height = params.height || 17850000
|
|
988
|
+
|
|
989
|
+
// 航向已原航向为准
|
|
990
|
+
heading = viewer.scene.camera.heading
|
|
991
|
+
|
|
992
|
+
// 俯仰角现已原角度为准,在环绕函数里再修改角度,这样整体效果会更好
|
|
993
|
+
pitch = viewer.scene.camera.pitch
|
|
994
|
+
|
|
995
|
+
// 滚转已原滚转为准
|
|
996
|
+
roll = viewer.scene.camera.roll
|
|
997
|
+
} else {
|
|
998
|
+
// 关闭环绕时到点位,增加高度,解决放大到点位时层级过大导致点位图标乱串问题
|
|
999
|
+
let h = await this.getPointHeight(coordinates, viewer)
|
|
1000
|
+
height += h
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const center = Cesium.Cartesian3.fromDegrees(
|
|
1004
|
+
coordinates[0],
|
|
1005
|
+
coordinates[1],
|
|
1006
|
+
height
|
|
1007
|
+
)
|
|
1008
|
+
|
|
1009
|
+
// 将三维球定位到点位
|
|
1010
|
+
viewer.camera.flyTo({
|
|
1011
|
+
destination: center,
|
|
1012
|
+
orientation: {
|
|
1013
|
+
heading: heading,
|
|
1014
|
+
pitch: pitch,
|
|
1015
|
+
roll: roll,
|
|
1016
|
+
},
|
|
1017
|
+
duration: params.duration ? params.duration : 2, // 飞行持续时间(秒)
|
|
1018
|
+
complete: () => {
|
|
1019
|
+
// 开启环绕
|
|
1020
|
+
if (params.surround) {
|
|
1021
|
+
params.center = center
|
|
1022
|
+
this.startSurround(params, viewer)
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// 关闭环绕、存在拉远距离
|
|
1026
|
+
if (!params.surround && params.range) {
|
|
1027
|
+
var offset = new Cesium.HeadingPitchRange(
|
|
1028
|
+
viewer.scene.camera.heading,
|
|
1029
|
+
viewer.scene.camera.pitch,
|
|
1030
|
+
params.range
|
|
1031
|
+
)
|
|
1032
|
+
|
|
1033
|
+
// 设置相机视图
|
|
1034
|
+
viewer.camera.lookAt(center, offset)
|
|
1035
|
+
|
|
1036
|
+
// 解决在使用camera.lookAt之后,造成问题:cesium内置的鼠标控制相机功能发生改变,例如:鼠标右键的挪移后相机是绕中心点旋转,而不是平移,flyto也会变得很诡异(虽然跳到目标点正常,但是相机飞的非常高
|
|
1037
|
+
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
|
|
1038
|
+
}
|
|
1039
|
+
},
|
|
1040
|
+
})
|
|
1041
|
+
},
|
|
1042
|
+
|
|
1043
|
+
// 设置相机视角
|
|
1044
|
+
setView (longitude, latitude, height, heading = 0, pitch = 0, roll = 0) {
|
|
1045
|
+
this.viewer.camera.setView({
|
|
1046
|
+
destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
|
|
1047
|
+
orientation: {
|
|
1048
|
+
heading: Cesium.Math.toRadians(heading),
|
|
1049
|
+
pitch: Cesium.Math.toRadians(pitch),
|
|
1050
|
+
roll
|
|
1051
|
+
}
|
|
1052
|
+
})
|
|
1053
|
+
},
|
|
1054
|
+
|
|
1055
|
+
// 存储当前视角
|
|
1056
|
+
// 传入viewer对象
|
|
1057
|
+
// 存储当前视角的position(位置)、heading(航向角)、pitch(俯仰角)、roll(翻滚角)
|
|
1058
|
+
storeViewer (viewer = this.viewer) {
|
|
1059
|
+
//获取当前视角的postion(位置)
|
|
1060
|
+
const position = viewer.camera.position
|
|
1061
|
+
|
|
1062
|
+
this.currentViewer = {
|
|
1063
|
+
x: position.x,
|
|
1064
|
+
y: position.y,
|
|
1065
|
+
z: position.z,
|
|
1066
|
+
heading: viewer.camera.heading,
|
|
1067
|
+
pitch: viewer.camera.pitch,
|
|
1068
|
+
roll: viewer.camera.roll,
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
|
|
1072
|
+
restoreViewer (viewer = this.viewer) {
|
|
1073
|
+
console.log("开始返回视角")
|
|
1074
|
+
viewer.camera.flyTo({
|
|
1075
|
+
//定位到范围中心点
|
|
1076
|
+
destination: {
|
|
1077
|
+
x: this.currentViewer.x,
|
|
1078
|
+
y: this.currentViewer.y,
|
|
1079
|
+
z: this.currentViewer.z,
|
|
1080
|
+
},
|
|
1081
|
+
orientation: {
|
|
1082
|
+
heading: this.currentViewer.heading,
|
|
1083
|
+
pitch: this.currentViewer.pitch,
|
|
1084
|
+
roll: this.currentViewer.roll,
|
|
1085
|
+
},
|
|
1086
|
+
})
|
|
1087
|
+
},
|
|
1088
|
+
|
|
1089
|
+
// 叠加WMS图层
|
|
1090
|
+
addWmsLayer (params, viewer = this.viewer) {
|
|
1091
|
+
this.delWmsLayer(params.name, viewer)
|
|
1092
|
+
|
|
1093
|
+
// 添加WMS图层
|
|
1094
|
+
const wmsImageryProvider = new Cesium.WebMapServiceImageryProvider({
|
|
1095
|
+
url: `${params.url}${params.work}/wms`, // 替换为你的WMS服务URL
|
|
1096
|
+
layers: `${params.work}:${params.layerName}`, // 替换为你想要叠加的图层名称
|
|
1097
|
+
parameters: {
|
|
1098
|
+
service: "WMS",
|
|
1099
|
+
version: "1.1.1",
|
|
1100
|
+
request: "GetMap",
|
|
1101
|
+
styles: params.style || "",
|
|
1102
|
+
format: "image/png",
|
|
1103
|
+
transparent: true,
|
|
1104
|
+
cql_filter: params.filter || "1=1",
|
|
1105
|
+
},
|
|
1106
|
+
})
|
|
1107
|
+
|
|
1108
|
+
const layer = viewer.imageryLayers.addImageryProvider(wmsImageryProvider)
|
|
1109
|
+
|
|
1110
|
+
const layerItem = {
|
|
1111
|
+
name: params.name,
|
|
1112
|
+
url: params.url,
|
|
1113
|
+
work: params.work,
|
|
1114
|
+
filterSql: params.filterSql,
|
|
1115
|
+
function: params.layerEventFun,
|
|
1116
|
+
layer,
|
|
1117
|
+
}
|
|
1118
|
+
const layerIndex = this.layers.findIndex((l) => l.name === params.name)
|
|
1119
|
+
if (layerIndex >= 0) {
|
|
1120
|
+
this.layers[layerIndex] = layerItem
|
|
1121
|
+
} else {
|
|
1122
|
+
this.layers.push(layerItem)
|
|
1123
|
+
}
|
|
1124
|
+
if (typeof params.layerEventFun == "function") {
|
|
1125
|
+
if (!this.layerClickHandlerWms) {
|
|
1126
|
+
this.layerClickHandlerWms = new Cesium.ScreenSpaceEventHandler(
|
|
1127
|
+
viewer.scene.canvas
|
|
1128
|
+
)
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
this.layerClickHandlerWms.setInputAction((click) => {
|
|
1132
|
+
// const pickedObject = viewer.scene.pickPosition(click.position);
|
|
1133
|
+
// console.log('进来了1', pickedObject);
|
|
1134
|
+
|
|
1135
|
+
const clickPosition = click.position
|
|
1136
|
+
const pickedObject = viewer.camera.getPickRay(clickPosition)
|
|
1137
|
+
// const pickPosition = viewer.scene.pickPosition(pickPosition);
|
|
1138
|
+
// console.log('clickPosition', clickPosition);
|
|
1139
|
+
|
|
1140
|
+
if (Cesium.defined(pickedObject)) {
|
|
1141
|
+
let pickedImagery = viewer.imageryLayers.pickImageryLayerFeatures(
|
|
1142
|
+
pickedObject,
|
|
1143
|
+
viewer.scene
|
|
1144
|
+
)
|
|
1145
|
+
// console.log('进来了2', pickedImagery);
|
|
1146
|
+
// 叠加geosever图层后,每次点击都会调用这个,所以需要加个判断,不然会一直报错
|
|
1147
|
+
if (pickedImagery) {
|
|
1148
|
+
pickedImagery.then((features) => {
|
|
1149
|
+
// console.log('进来了2', features);
|
|
1150
|
+
if (features && features.length > 0) {
|
|
1151
|
+
const pickedPosition = viewer.scene.globe.pick(
|
|
1152
|
+
pickedObject,
|
|
1153
|
+
viewer.scene
|
|
1154
|
+
)
|
|
1155
|
+
|
|
1156
|
+
const cartographic =
|
|
1157
|
+
Cesium.Cartographic.fromCartesian(pickedPosition)
|
|
1158
|
+
const longitude = Cesium.Math.toDegrees(
|
|
1159
|
+
cartographic.longitude
|
|
1160
|
+
)
|
|
1161
|
+
const latitude = Cesium.Math.toDegrees(cartographic.latitude)
|
|
1162
|
+
|
|
1163
|
+
// 创建一个临时实体用于定位弹窗位置
|
|
1164
|
+
const entity = new Cesium.Entity({
|
|
1165
|
+
position: Cesium.Cartesian3.fromDegrees(
|
|
1166
|
+
longitude,
|
|
1167
|
+
latitude
|
|
1168
|
+
),
|
|
1169
|
+
})
|
|
1170
|
+
|
|
1171
|
+
let newFeatures = {}
|
|
1172
|
+
|
|
1173
|
+
// 重定义features.name名称,否则默认为geoserver的发布名称
|
|
1174
|
+
features.forEach((v) => {
|
|
1175
|
+
const pickedLayer = v.imageryLayer
|
|
1176
|
+
|
|
1177
|
+
// 查找对应的图层名称
|
|
1178
|
+
const layerInfo = this.layers.find(
|
|
1179
|
+
(layer) => layer.layer === pickedLayer
|
|
1180
|
+
)
|
|
1181
|
+
// console.log('layerInfo', layerInfo);
|
|
1182
|
+
if (layerInfo) {
|
|
1183
|
+
// v.name = layerInfo.name;
|
|
1184
|
+
// v.url = this.getFeatureInfoUrl(Object.assign(params, { position: clickPosition }), viewer);
|
|
1185
|
+
newFeatures[layerInfo.name] = this.getFeatureInfoUrl(
|
|
1186
|
+
Object.assign(layerInfo, { position: clickPosition }),
|
|
1187
|
+
viewer
|
|
1188
|
+
)
|
|
1189
|
+
if (typeof layerInfo.function === "function") {
|
|
1190
|
+
layerInfo.function(entity, newFeatures)
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
})
|
|
1194
|
+
}
|
|
1195
|
+
})
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
|
1199
|
+
}
|
|
1200
|
+
},
|
|
1201
|
+
|
|
1202
|
+
// 删除WMS图层
|
|
1203
|
+
delWmsLayer (name, viewer = this.viewer) {
|
|
1204
|
+
// 查找并删除已有的同名图层
|
|
1205
|
+
const existingLayerIndex = this.layers.findIndex((l) => l.name === name)
|
|
1206
|
+
if (existingLayerIndex >= 0) {
|
|
1207
|
+
viewer.imageryLayers.remove(this.layers[existingLayerIndex].layer)
|
|
1208
|
+
this.layers.splice(existingLayerIndex, 1)
|
|
1209
|
+
}
|
|
1210
|
+
},
|
|
1211
|
+
|
|
1212
|
+
// 设置WMS图层显示/隐藏
|
|
1213
|
+
setWmsLayerVisible (params) {
|
|
1214
|
+
const layer = this.layers.find((l) => l.name === params.name)
|
|
1215
|
+
if (layer) {
|
|
1216
|
+
layer.layer.show = params.active
|
|
1217
|
+
}
|
|
1218
|
+
},
|
|
1219
|
+
// 叠加路网注记服务
|
|
1220
|
+
changeStreetSelector (show) {
|
|
1221
|
+
if (!this.ciaLayer) {
|
|
1222
|
+
let ciaMap = new Cesium.UrlTemplateImageryProvider({
|
|
1223
|
+
url: tdtUrl + 'DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=' + token,
|
|
1224
|
+
subdomains: subdomains,
|
|
1225
|
+
tilingScheme: new Cesium.WebMercatorTilingScheme()
|
|
1226
|
+
})
|
|
1227
|
+
this.ciaLayer = this.viewer.imageryLayers.addImageryProvider(ciaMap)
|
|
1228
|
+
}
|
|
1229
|
+
this.ciaLayer.show = show
|
|
1230
|
+
},
|
|
1231
|
+
|
|
1232
|
+
//获取点位经纬度计算高度
|
|
1233
|
+
async getPointHeight (point, viewer = this.viewer) {
|
|
1234
|
+
// 将经纬度坐标转换为笛卡尔坐标
|
|
1235
|
+
let position = Cesium.Cartesian3.fromDegrees(point[0], point[1])
|
|
1236
|
+
|
|
1237
|
+
let terrainProvider = this.viewer.terrainProvider
|
|
1238
|
+
// console.log(`output->terrainProvider`, terrainProvider);
|
|
1239
|
+
|
|
1240
|
+
// 获取地形高度信息
|
|
1241
|
+
let x = position.x
|
|
1242
|
+
let y = position.y
|
|
1243
|
+
let z = position.z
|
|
1244
|
+
|
|
1245
|
+
let cartesianPosition = new Cesium.Cartesian3(x, y, z) // 假设这是你的三维坐标
|
|
1246
|
+
let ellipsoid = this.viewer.scene.globe.ellipsoid
|
|
1247
|
+
let cartographicPosition = ellipsoid.cartesianToCartographic(cartesianPosition)
|
|
1248
|
+
|
|
1249
|
+
// 获取地形高度信息
|
|
1250
|
+
const updatedPositions = await Cesium.sampleTerrain(terrainProvider, 11, [cartographicPosition,])
|
|
1251
|
+
let terrainHeight = updatedPositions[0].height || 1000 // 获取地形高度信息
|
|
1252
|
+
// console.log(`output->terrainHeight`, terrainHeight);
|
|
1253
|
+
return terrainHeight
|
|
1254
|
+
|
|
1255
|
+
},
|
|
1256
|
+
|
|
1257
|
+
// 批量获取地形高度
|
|
1258
|
+
async getTerrainHeight (points) {
|
|
1259
|
+
let result
|
|
1260
|
+
let key = JSON.stringify(points.map(point => [point[0], point[1]]))
|
|
1261
|
+
let { terrainProvider } = this.viewer
|
|
1262
|
+
if (this.terrainCacheMap.has(key)) {
|
|
1263
|
+
result = this.terrainCacheMap.get(key)
|
|
1264
|
+
} else {
|
|
1265
|
+
let cartographicList = points.map(point => Cesium.Cartographic.fromDegrees(point[0], point[1]))
|
|
1266
|
+
result = await Cesium.sampleTerrainMostDetailed(terrainProvider, cartographicList)
|
|
1267
|
+
this.terrainCacheMap.set(key, result)
|
|
1268
|
+
}
|
|
1269
|
+
return result
|
|
1270
|
+
},
|
|
1271
|
+
|
|
1272
|
+
// 叠加WKT图层
|
|
1273
|
+
async addWktLayer(params, viewer = this.viewer) {
|
|
1274
|
+
// 判断是否存在,处理显示/隐藏
|
|
1275
|
+
const name = params.type || params.enName || params.name;
|
|
1276
|
+
|
|
1277
|
+
// 不存在,执行下面步骤
|
|
1278
|
+
let options = this.handleStyle(params);
|
|
1279
|
+
const data = options.coordsArr || options.list || [];
|
|
1280
|
+
let coordsList = data.map(item => {
|
|
1281
|
+
let geoJson = WKT.parse(item[options.coordField]);
|
|
1282
|
+
return geoJson.type == "Point" ? geoJson.coordinates : null;
|
|
1283
|
+
}).filter(Boolean);
|
|
1284
|
+
let terrainHeights;
|
|
1285
|
+
if (coordsList.length) {
|
|
1286
|
+
|
|
1287
|
+
terrainHeights = await this.getTerrainHeight(coordsList);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// 先删除已有图层数据,//请求高度后再清除,防止上一个图层还没绘制完成就提前清除了,导致没有清除到
|
|
1291
|
+
this.delWktLayer(name);
|
|
1292
|
+
|
|
1293
|
+
// 创建数据源
|
|
1294
|
+
const dataSource = new Cesium.CustomDataSource(name);
|
|
1295
|
+
|
|
1296
|
+
for (let index = 0; index < data.length; index++) {
|
|
1297
|
+
const v = data[index];
|
|
1298
|
+
// 将wkt格式转化为geojson
|
|
1299
|
+
let geoJson = v[options.coordField];
|
|
1300
|
+
if (!geoJson || !this.isWkt(geoJson)) {
|
|
1301
|
+
console.log("后端数据不严谨,经纬度为null");
|
|
1302
|
+
continue;
|
|
1303
|
+
}
|
|
1304
|
+
geoJson = WKT.parse(geoJson);
|
|
1305
|
+
|
|
1306
|
+
const { type } = geoJson;
|
|
1307
|
+
const { coordinates } = geoJson;
|
|
1308
|
+
|
|
1309
|
+
const style = v.olstyle || options.coordsStyle;
|
|
1310
|
+
let entity = null;
|
|
1311
|
+
|
|
1312
|
+
if (type === "Point") {
|
|
1313
|
+
entity = dataSource.entities.add({
|
|
1314
|
+
position: Cesium.Cartesian3.fromDegrees(coordinates[0], coordinates[1], v.height || terrainHeights[index].height),
|
|
1315
|
+
properties: v,
|
|
1316
|
+
callback: options.vectorEventFun, // 附加回调函数
|
|
1317
|
+
});
|
|
1318
|
+
const pointProperties = entity.properties;
|
|
1319
|
+
pointProperties.addProperty("index", index);
|
|
1320
|
+
|
|
1321
|
+
if (style.label) {
|
|
1322
|
+
entity.label = style.label;
|
|
1323
|
+
entity.label.text = v[options.textField];
|
|
1324
|
+
}
|
|
1325
|
+
if (style.billboard) {
|
|
1326
|
+
entity.billboard = style.billboard;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// 处理Cesium特定的point样式
|
|
1330
|
+
if (style.cesium && style.cesium.point) {
|
|
1331
|
+
entity.point = Object.assign(
|
|
1332
|
+
{},
|
|
1333
|
+
entity.point || {},
|
|
1334
|
+
style.cesium.point
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
// 处理ellipse样式
|
|
1338
|
+
if (style.ellipse) {
|
|
1339
|
+
entity.ellipse = style.ellipse;
|
|
1340
|
+
}
|
|
1341
|
+
} else if (type === "LineString") {
|
|
1342
|
+
const positions = await Promise.all(
|
|
1343
|
+
coordinates.map(async (coord) =>
|
|
1344
|
+
Cesium.Cartesian3.fromDegrees(
|
|
1345
|
+
coord[0],
|
|
1346
|
+
coord[1],
|
|
1347
|
+
coord[2] || await this.getPointHeight(coord, viewer)
|
|
1348
|
+
)
|
|
1349
|
+
));
|
|
1350
|
+
entity = dataSource.entities.add({
|
|
1351
|
+
polyline: {
|
|
1352
|
+
positions: positions,
|
|
1353
|
+
width: params.coordsStyle.stroke.width,
|
|
1354
|
+
material: Cesium.Color.fromCssColorString(
|
|
1355
|
+
params.coordsStyle.stroke.color
|
|
1356
|
+
),
|
|
1357
|
+
clampToGround: params.clampToGround || false,//是否开启贴地
|
|
1358
|
+
},
|
|
1359
|
+
});
|
|
1360
|
+
} else if (type === "Polygon") {
|
|
1361
|
+
let terrainHeight = await this.getTerrainHeight(coordinates[0], viewer);
|
|
1362
|
+
const hierarchy = coordinates.map((ring) =>
|
|
1363
|
+
ring.map((coord, index) =>
|
|
1364
|
+
Cesium.Cartesian3.fromDegrees(coord[0], coord[1], terrainHeight[index].height)
|
|
1365
|
+
)
|
|
1366
|
+
);
|
|
1367
|
+
entity = dataSource.entities.add({
|
|
1368
|
+
id: v.id,
|
|
1369
|
+
polygon: {
|
|
1370
|
+
hierarchy: new Cesium.PolygonHierarchy(hierarchy[0]),
|
|
1371
|
+
},
|
|
1372
|
+
callback: options.vectorEventFun, // 附加回调函数
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
// 确保样式正确应用,包括透明度
|
|
1376
|
+
if (style.polygon) {
|
|
1377
|
+
entity.polygon = Object.assign(entity.polygon, style.polygon);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// 根据传入的参数设置边框
|
|
1381
|
+
if (style.stroke) {
|
|
1382
|
+
if (style.stroke.width == 0) {
|
|
1383
|
+
entity.polygon.outline = false;
|
|
1384
|
+
} else {
|
|
1385
|
+
// 如果设置了stroke,则启用outline并设置样式
|
|
1386
|
+
entity.polygon.outline = true;
|
|
1387
|
+
if (style.stroke.color) {
|
|
1388
|
+
entity.polygon.outlineColor = Cesium.Color.fromCssColorString(
|
|
1389
|
+
this.handleColor(style.stroke.color)
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
if (style.stroke.width) {
|
|
1393
|
+
entity.polygon.outlineWidth = style.stroke.width;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// 多面边框设置在 Cesium 1.95 下无效,改为线条方式模拟多边形的边框
|
|
1398
|
+
if (style.stroke.color && style.stroke.width > 0) {
|
|
1399
|
+
dataSource.entities.add({
|
|
1400
|
+
polyline: {
|
|
1401
|
+
positions: [...hierarchy[0], hierarchy[0][0]], // 确保边框闭合
|
|
1402
|
+
width: style.stroke.width, // 边框宽度
|
|
1403
|
+
material: Cesium.Color.fromCssColorString(
|
|
1404
|
+
this.handleColor(style.stroke.color)
|
|
1405
|
+
), // 边框颜色
|
|
1406
|
+
clampToGround: true, // 贴地显示
|
|
1407
|
+
},
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
} else if (type === "MultiPolygon") {
|
|
1412
|
+
let terrainHeight = await this.getTerrainHeight(coordinates[0][0], viewer);
|
|
1413
|
+
let positions = coordinates[0][0].map((coord, index) => Cesium.Cartesian3.fromDegrees(coord[0], coord[1], terrainHeight[index].height));
|
|
1414
|
+
entity = dataSource.entities.add({
|
|
1415
|
+
polygon: {
|
|
1416
|
+
hierarchy: positions,
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
if (style.polygon) {
|
|
1420
|
+
entity.polygon = Object.assign(entity.polygon, style.polygon);
|
|
1421
|
+
}
|
|
1422
|
+
// 多面边框设置在 Cesium 1.95 下无效,改为以下方式模拟多边形的边框
|
|
1423
|
+
if (style.polygon && style.polygon.outline) {
|
|
1424
|
+
// 添加对应的多边形边框
|
|
1425
|
+
dataSource.entities.add({
|
|
1426
|
+
polyline: {
|
|
1427
|
+
positions: [...positions, positions[0]], // 确保边框闭合
|
|
1428
|
+
width: style.polyline.width, // 边框宽度
|
|
1429
|
+
material: style.polyline.material, // 边框颜色
|
|
1430
|
+
clampToGround: true, // 贴地显示
|
|
1431
|
+
},
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// 将数据源添加到viewer
|
|
1438
|
+
viewer.dataSources.add(dataSource);
|
|
1439
|
+
// 处理fit参数 - 自动定位到图层范围
|
|
1440
|
+
if (options.fit && dataSource.entities.values.length) {
|
|
1441
|
+
// 收集所有实体的位置信息
|
|
1442
|
+
const positions = [];
|
|
1443
|
+
dataSource.entities.values.forEach((entity) => {
|
|
1444
|
+
if (entity.position) {
|
|
1445
|
+
positions.push(entity.position.getValue(Cesium.JulianDate.now()));
|
|
1446
|
+
} else if (entity.polygon) {
|
|
1447
|
+
// 对于多边形,获取其边界
|
|
1448
|
+
const hierarchy = entity.polygon.hierarchy.getValue(
|
|
1449
|
+
Cesium.JulianDate.now()
|
|
1450
|
+
);
|
|
1451
|
+
if (hierarchy && hierarchy.positions) {
|
|
1452
|
+
hierarchy.positions.forEach((pos) => {
|
|
1453
|
+
positions.push(pos);
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
} else if (entity.polyline) {
|
|
1457
|
+
// 对于线条,获取其所有点
|
|
1458
|
+
const polylinePositions = entity.polyline.positions.getValue(
|
|
1459
|
+
Cesium.JulianDate.now()
|
|
1460
|
+
);
|
|
1461
|
+
if (polylinePositions) {
|
|
1462
|
+
polylinePositions.forEach((pos) => {
|
|
1463
|
+
positions.push(pos);
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
// 如果有位置信息,则进行自动定位
|
|
1470
|
+
if (positions.length > 0) {
|
|
1471
|
+
const boundingSphere = Cesium.BoundingSphere.fromPoints(positions);
|
|
1472
|
+
// 设置fit选项
|
|
1473
|
+
const fitOptions = options.fitOptions || {
|
|
1474
|
+
duration: 2.0,
|
|
1475
|
+
offset: new Cesium.HeadingPitchRange(
|
|
1476
|
+
0,
|
|
1477
|
+
-Math.PI / 4,
|
|
1478
|
+
boundingSphere.radius * 2.5
|
|
1479
|
+
)
|
|
1480
|
+
};
|
|
1481
|
+
// 执行自动定位
|
|
1482
|
+
viewer.camera.flyToBoundingSphere(boundingSphere, fitOptions);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
const layerItem = {
|
|
1487
|
+
name: name,
|
|
1488
|
+
entities: dataSource.entities.values,
|
|
1489
|
+
function: options.vectorEventFun,
|
|
1490
|
+
dataSource: dataSource, // 添加数据源引用
|
|
1491
|
+
isHover: params.isHover || false, // 添加悬浮标识
|
|
1492
|
+
};
|
|
1493
|
+
const layerIndex = this.layers.findIndex((l) => l.name === name);
|
|
1494
|
+
if (layerIndex >= 0) {
|
|
1495
|
+
this.layers[layerIndex] = layerItem;
|
|
1496
|
+
} else {
|
|
1497
|
+
this.layers.push(layerItem);
|
|
1498
|
+
}
|
|
1499
|
+
const pixelRange = 45;
|
|
1500
|
+
const minimumClusterSize = 5; //至少5个点才聚合
|
|
1501
|
+
|
|
1502
|
+
const clusterHandler = (clusteredEntities, cluster) => {
|
|
1503
|
+
cluster.label.show = false;
|
|
1504
|
+
cluster.billboard.show = true;
|
|
1505
|
+
if (
|
|
1506
|
+
options.clusterStyle.circle.fill &&
|
|
1507
|
+
options.clusterStyle.circle.fill.color
|
|
1508
|
+
) {
|
|
1509
|
+
cluster.billboard.image = this.handleClusterColorIcon(
|
|
1510
|
+
options.clusterStyle.circle.fill.color,
|
|
1511
|
+
clusteredEntities.length.toLocaleString(),
|
|
1512
|
+
48,
|
|
1513
|
+
48
|
|
1514
|
+
);
|
|
1515
|
+
} else if (
|
|
1516
|
+
options.clusterStyle.circle.image &&
|
|
1517
|
+
options.clusterStyle.circle.image.src
|
|
1518
|
+
) {
|
|
1519
|
+
cluster.billboard.image = this.handleClusterImageIcon(
|
|
1520
|
+
options.clusterStyle.circle.image.src,
|
|
1521
|
+
clusteredEntities.length.toLocaleString(),
|
|
1522
|
+
72,
|
|
1523
|
+
72
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
cluster.billboard.heightReference =
|
|
1528
|
+
Cesium.HeightReference.CLAMP_TO_GROUND;
|
|
1529
|
+
cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.CENTER;
|
|
1530
|
+
cluster.billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY;
|
|
1531
|
+
cluster.billboard.id = {
|
|
1532
|
+
entities: clusteredEntities,
|
|
1533
|
+
isCluster: true,
|
|
1534
|
+
callback: options.vectorEventFun,
|
|
1535
|
+
};
|
|
1536
|
+
};
|
|
1537
|
+
|
|
1538
|
+
dataSource.clustering.enabled = options.isCluster;
|
|
1539
|
+
dataSource.clustering.pixelRange = pixelRange;
|
|
1540
|
+
dataSource.clustering.minimumClusterSize = minimumClusterSize;
|
|
1541
|
+
dataSource.clustering.clusterEvent.addEventListener(clusterHandler);
|
|
1542
|
+
|
|
1543
|
+
// console.log('进入wkt图层点击事件', name);
|
|
1544
|
+
if (!this.layerClickHandlerWkt) {
|
|
1545
|
+
// console.log('不存在点击处理程序,创建wkt点击处理程序');
|
|
1546
|
+
this.layerClickHandlerWkt = new Cesium.ScreenSpaceEventHandler(
|
|
1547
|
+
viewer.scene.canvas
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// this.layerClickHandlerWkt = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
1552
|
+
this.layerClickHandlerWkt.setInputAction((click) => {
|
|
1553
|
+
let position = { x: click.position.x / this.scale, y: click.position.y / this.scale };
|
|
1554
|
+
const pickedObject = viewer.scene.pick(position);
|
|
1555
|
+
if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
|
|
1556
|
+
if (pickedObject.id.isCluster) {
|
|
1557
|
+
// console.log('聚合点位');
|
|
1558
|
+
this.zoomToCluster(pickedObject.id.entities, viewer);
|
|
1559
|
+
} else {
|
|
1560
|
+
// console.log('单个点位', pickedObject.id);
|
|
1561
|
+
|
|
1562
|
+
// 获取所有自定义属性
|
|
1563
|
+
const properties = pickedObject.id.properties;
|
|
1564
|
+
const propertyNames =
|
|
1565
|
+
properties && properties.propertyNames
|
|
1566
|
+
? properties.propertyNames
|
|
1567
|
+
: [];
|
|
1568
|
+
// console.log('properties', properties);
|
|
1569
|
+
// console.log('propertyNames', propertyNames);
|
|
1570
|
+
|
|
1571
|
+
const propertyValues = {};
|
|
1572
|
+
propertyNames.forEach((name) => {
|
|
1573
|
+
// 会存在properties比propertyNames字段多的情况,需要判断
|
|
1574
|
+
if (properties["_" + name]) {
|
|
1575
|
+
propertyValues[name] = properties["_" + name].getValue();
|
|
1576
|
+
}
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1579
|
+
// 组成openlayers统一的接收格式
|
|
1580
|
+
const res = {
|
|
1581
|
+
values_: {
|
|
1582
|
+
attr: propertyValues,
|
|
1583
|
+
},
|
|
1584
|
+
};
|
|
1585
|
+
|
|
1586
|
+
// 调用附加在实体上的回调函数(如果存在)
|
|
1587
|
+
if (pickedObject.id.callback) {
|
|
1588
|
+
pickedObject.id.callback(pickedObject.id, res);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
1593
|
+
},
|
|
1594
|
+
|
|
1595
|
+
// 删除WKT图层
|
|
1596
|
+
delWktLayer (name, viewer = this.viewer) {
|
|
1597
|
+
// 清理图层的悬浮事件处理器
|
|
1598
|
+
const layerIndex = this.layers.findIndex((l) => l.name === name)
|
|
1599
|
+
if (layerIndex >= 0) {
|
|
1600
|
+
this.layers.splice(layerIndex, 1)
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
const existingDataSources = viewer.dataSources.getByName(name)
|
|
1604
|
+
// 删除已有的数据源
|
|
1605
|
+
if (existingDataSources.length) {
|
|
1606
|
+
existingDataSources.forEach(v => {
|
|
1607
|
+
viewer.dataSources.remove(v)
|
|
1608
|
+
})
|
|
1609
|
+
}
|
|
1610
|
+
delete this.drawLayerObj[name]
|
|
1611
|
+
},
|
|
1612
|
+
|
|
1613
|
+
// 设置WKT图层显示/隐藏
|
|
1614
|
+
setWktLayerVisible (params) {
|
|
1615
|
+
const name = params.type || params.enName || params.name
|
|
1616
|
+
const layer = this.layers.find((l) => l.name === name)
|
|
1617
|
+
if (layer) {
|
|
1618
|
+
layer.entities.forEach((entity) => {
|
|
1619
|
+
entity.show = params.active
|
|
1620
|
+
})
|
|
1621
|
+
}
|
|
1622
|
+
},
|
|
1623
|
+
|
|
1624
|
+
// 获取WMS图层当前点击位置url
|
|
1625
|
+
getFeatureInfoUrl (params, viewer = this.viewer) {
|
|
1626
|
+
const {
|
|
1627
|
+
url,
|
|
1628
|
+
work,
|
|
1629
|
+
name,
|
|
1630
|
+
filterSql,
|
|
1631
|
+
width = 256,
|
|
1632
|
+
height = 256,
|
|
1633
|
+
infoFormat = "application/json",
|
|
1634
|
+
} = params
|
|
1635
|
+
|
|
1636
|
+
const clickPosition = params.position
|
|
1637
|
+
const canvas = viewer.scene.canvas
|
|
1638
|
+
|
|
1639
|
+
// Calculate BBox around click position with a small buffer
|
|
1640
|
+
const buffer = 0.0001 // You can adjust this value as needed
|
|
1641
|
+
const cartographic = Cesium.Cartographic.fromCartesian(
|
|
1642
|
+
viewer.camera.pickEllipsoid(clickPosition, viewer.scene.globe.ellipsoid)
|
|
1643
|
+
)
|
|
1644
|
+
const bbox = [
|
|
1645
|
+
Cesium.Math.toDegrees(cartographic.longitude) - buffer,
|
|
1646
|
+
Cesium.Math.toDegrees(cartographic.latitude) - buffer,
|
|
1647
|
+
Cesium.Math.toDegrees(cartographic.longitude) + buffer,
|
|
1648
|
+
Cesium.Math.toDegrees(cartographic.latitude) + buffer,
|
|
1649
|
+
].join(",")
|
|
1650
|
+
|
|
1651
|
+
// Calculate i and j using canvas size
|
|
1652
|
+
const i = Math.round((clickPosition.x / canvas.width) * width)
|
|
1653
|
+
const j = Math.round((1 - clickPosition.y / canvas.height) * height)
|
|
1654
|
+
|
|
1655
|
+
let _url = url
|
|
1656
|
+
if (_url == "/geoserver/") {
|
|
1657
|
+
_url = "https://jxbhq.zhlzz.com:8006/geoserver/"
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
let _filterSql = params.filterSql
|
|
1661
|
+
? `&cql_filter=${encodeURIComponent(filterSql)}`
|
|
1662
|
+
: ""
|
|
1663
|
+
|
|
1664
|
+
const featureInfoUrl = `${_url}${work}/wms?service=WMS&version=1.1.1&request=GetFeatureInfo&layers=${work}:${name}&query_layers=${work}:${name}&bbox=${bbox}&width=${width}&height=${height}&x=${i}&y=${j}&info_format=${infoFormat}&srs=EPSG:4326${_filterSql}`
|
|
1665
|
+
|
|
1666
|
+
return featureInfoUrl
|
|
1667
|
+
},
|
|
1668
|
+
|
|
1669
|
+
// 处理颜色
|
|
1670
|
+
handleColor (color) {
|
|
1671
|
+
// 如果是rgba需将透明调整为不透明,否则会存在图标背景有透明,文字的背景不透明的情况;
|
|
1672
|
+
if (color.includes("rgba")) {
|
|
1673
|
+
color = color.split(",")
|
|
1674
|
+
if (color.length > 0 && color[color.length - 1] != " 1.0)") {
|
|
1675
|
+
color[color.length - 1] = " 1.0)"
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
color = color.join(",")
|
|
1679
|
+
// console.log(color);
|
|
1680
|
+
}
|
|
1681
|
+
return color
|
|
1682
|
+
},
|
|
1683
|
+
|
|
1684
|
+
// 处理样式
|
|
1685
|
+
handleStyle (params) {
|
|
1686
|
+
let paramsDefault = {
|
|
1687
|
+
isCluster: false,
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// 深拷贝、不改变原对象
|
|
1691
|
+
let res = deepClone(params)
|
|
1692
|
+
res = Object.assign(paramsDefault, res)
|
|
1693
|
+
|
|
1694
|
+
// 处理统一的点、线、面样式
|
|
1695
|
+
if (res.coordsStyle) {
|
|
1696
|
+
res.coordsStyle = this.handleItemStyle(res.coordsStyle)
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
// 处理单个点、线、面的样式
|
|
1700
|
+
if (res.coordsArr && Array.isArray(res.coordsArr)) {
|
|
1701
|
+
res.coordsArr.forEach((item) => {
|
|
1702
|
+
if (item.olstyle) {
|
|
1703
|
+
item.olstyle = this.handleItemStyle(item.olstyle)
|
|
1704
|
+
}
|
|
1705
|
+
})
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
return res
|
|
1709
|
+
},
|
|
1710
|
+
|
|
1711
|
+
// 处理单个样式
|
|
1712
|
+
handleItemStyle (obj) {
|
|
1713
|
+
// 初始参数
|
|
1714
|
+
obj.label = false
|
|
1715
|
+
obj.billboard = false
|
|
1716
|
+
obj.point = false
|
|
1717
|
+
obj.ellipse = false
|
|
1718
|
+
obj.polyline = {
|
|
1719
|
+
material: Cesium.Color.fromCssColorString("rgba(255, 255, 255, 0.5)"),
|
|
1720
|
+
}
|
|
1721
|
+
obj.polygon = {
|
|
1722
|
+
material: Cesium.Color.fromCssColorString("rgba(255, 255, 255, 0.5)"),
|
|
1723
|
+
outline: true,
|
|
1724
|
+
outlineColor: Cesium.Color.fromCssColorString("#ffff00"),
|
|
1725
|
+
outlineWidth: 2,
|
|
1726
|
+
}
|
|
1727
|
+
// 文本
|
|
1728
|
+
if (obj.text) {
|
|
1729
|
+
obj.label = {}
|
|
1730
|
+
obj.label.font = obj.text.font
|
|
1731
|
+
if (obj.text.fill && obj.text.fill.color) {
|
|
1732
|
+
obj.label.fillColor = Cesium.Color.fromCssColorString(
|
|
1733
|
+
this.handleColor(obj.text.fill.color)
|
|
1734
|
+
)
|
|
1735
|
+
}
|
|
1736
|
+
if (obj.text.stroke && obj.text.stroke.color) {
|
|
1737
|
+
obj.label.outlineColor = Cesium.Color.fromCssColorString(
|
|
1738
|
+
this.handleColor(obj.text.stroke.color)
|
|
1739
|
+
)
|
|
1740
|
+
obj.label.outlineWidth = obj.text.stroke.width
|
|
1741
|
+
? obj.text.stroke.width
|
|
1742
|
+
: 2
|
|
1743
|
+
obj.label.style = Cesium.LabelStyle.FILL_AND_OUTLINE //有填充和边框
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// 覆盖独有参数
|
|
1747
|
+
if (obj.text.cesiumFont) {
|
|
1748
|
+
obj.label.font = obj.text.cesiumFont
|
|
1749
|
+
}
|
|
1750
|
+
if (obj.text.cesiumFillColor) {
|
|
1751
|
+
obj.label.fillColor = Cesium.Color.fromCssColorString(
|
|
1752
|
+
this.handleColor(obj.text.cesiumColor)
|
|
1753
|
+
)
|
|
1754
|
+
}
|
|
1755
|
+
if (obj.text.cesiumOutlineColor) {
|
|
1756
|
+
obj.label.outlineColor = Cesium.Color.fromCssColorString(
|
|
1757
|
+
this.handleColor(obj.text.cesiumOutlineColor)
|
|
1758
|
+
)
|
|
1759
|
+
obj.label.outlineWidth = obj.text.cesiumOutlineWidth
|
|
1760
|
+
? obj.text.cesiumOutlineWidth
|
|
1761
|
+
: 2
|
|
1762
|
+
obj.label.style = Cesium.LabelStyle.FILL_AND_OUTLINE //有填充和边框
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
obj.label.showBackground = false // 能不能看见背景,并不是指字体透不透明,而是字体那块板子可不可见
|
|
1766
|
+
// obj.label.showBackground = true;
|
|
1767
|
+
// obj.label.backgroundColor = Cesium.Color.WHITE.withAlpha(0.0);
|
|
1768
|
+
obj.label.verticalOrigin = Cesium.VerticalOrigin.BOTTOM
|
|
1769
|
+
obj.label.horizontalOrigin = Cesium.HorizontalOrigin.CENTER
|
|
1770
|
+
obj.label.pixelOffset = new Cesium.Cartesian2(0, 16) // 调整标签位置
|
|
1771
|
+
|
|
1772
|
+
if (obj.text.cesiumOffsetX || obj.text.cesiumOffsetY) {
|
|
1773
|
+
// console.log('进来了', obj.text.offsetX);
|
|
1774
|
+
obj.label.pixelOffset = new Cesium.Cartesian2(
|
|
1775
|
+
obj.text.cesiumOffsetX,
|
|
1776
|
+
obj.text.cesiumOffsetY
|
|
1777
|
+
) // 调整标签位置
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
obj.label.disableDepthTestDistance = Number.POSITIVE_INFINITY
|
|
1781
|
+
// obj.text.disableDepthTestDistance = Number.POSITIVE_INFINITY;
|
|
1782
|
+
// obj.label.heightReference: Cesium.HeightReference.CLAMP_TO_GROUND; // 贴地显示
|
|
1783
|
+
}
|
|
1784
|
+
// 图标
|
|
1785
|
+
if (obj.image && obj.image.src) {
|
|
1786
|
+
obj.billboard = {}
|
|
1787
|
+
// obj.billboard.image = Cesium.buildModuleUrl(obj.image.src); // 自定义图标的路径
|
|
1788
|
+
obj.billboard.image = obj.image.src
|
|
1789
|
+
obj.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM
|
|
1790
|
+
|
|
1791
|
+
if (obj.image.scale) {
|
|
1792
|
+
obj.billboard.scale = obj.image.scale
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
if (obj.image.verticalOrigin && obj.image.verticalOrigin == "center") {
|
|
1796
|
+
obj.billboard.verticalOrigin = Cesium.VerticalOrigin.CENTER
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
if (obj.image.cesiumOffsetX || obj.image.cesiumOffsetY) {
|
|
1800
|
+
obj.billboard.pixelOffset = new Cesium.Cartesian2(
|
|
1801
|
+
obj.image.cesiumOffsetX || 0,
|
|
1802
|
+
obj.image.cesiumOffsetY || 0
|
|
1803
|
+
) // 调整标签位置
|
|
1804
|
+
}
|
|
1805
|
+
// 是否禁用深度检测
|
|
1806
|
+
// if(!obj.image.isDepth){
|
|
1807
|
+
obj.billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY // 禁用深度检测
|
|
1808
|
+
// }
|
|
1809
|
+
|
|
1810
|
+
// obj.billboard.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND; // 贴地显示
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// 自定义,扩散效果
|
|
1814
|
+
if (obj.spread && obj.spread.color) {
|
|
1815
|
+
obj.billboard = false
|
|
1816
|
+
obj.point = {
|
|
1817
|
+
pixelSize: obj.spread.pointSize ? obj.spread.pointSize : 10,
|
|
1818
|
+
// color: Cesium.Color.RED,
|
|
1819
|
+
color: Cesium.Color.fromCssColorString(
|
|
1820
|
+
this.handleColor(obj.spread.color)
|
|
1821
|
+
),
|
|
1822
|
+
outlineColor: Cesium.Color.fromCssColorString(
|
|
1823
|
+
this.handleColor(obj.spread.color)
|
|
1824
|
+
),
|
|
1825
|
+
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
obj.ellipse = {
|
|
1829
|
+
semiMinorAxis: obj.spread.semiMinorAxis
|
|
1830
|
+
? obj.spread.semiMinorAxis
|
|
1831
|
+
: 10000.0, // 短半轴距离
|
|
1832
|
+
semiMajorAxis: obj.spread.semiMajorAxis
|
|
1833
|
+
? obj.spread.semiMajorAxis
|
|
1834
|
+
: 10000.0, // 长半轴距离
|
|
1835
|
+
// ! 重点
|
|
1836
|
+
material: new CircularDiffusionMaterialProperty({
|
|
1837
|
+
// color: new Cesium.Color(1.0, 0.0, 0.0, 0.8),
|
|
1838
|
+
color: Cesium.Color.fromCssColorString(
|
|
1839
|
+
this.handleColor(obj.spread.color)
|
|
1840
|
+
),
|
|
1841
|
+
duration: 3000,
|
|
1842
|
+
}),
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
// 填充
|
|
1847
|
+
if (obj.fill && obj.fill.color) {
|
|
1848
|
+
// let _color = Cesium.Color.fromCssColorString(
|
|
1849
|
+
// this.handleColor(obj.fill.color)
|
|
1850
|
+
// );
|
|
1851
|
+
obj.polyline.material = Cesium.Color.fromCssColorString(
|
|
1852
|
+
this.handleColor(obj.fill.color)
|
|
1853
|
+
)
|
|
1854
|
+
obj.polygon.material = Cesium.Color.fromCssColorString(obj.fill.color)
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
if (obj.stroke) {
|
|
1858
|
+
if (obj.stroke.width == 0) {
|
|
1859
|
+
obj.polygon.outline = false
|
|
1860
|
+
} else {
|
|
1861
|
+
// 如果设置了stroke,则启用outline并设置样式
|
|
1862
|
+
obj.polygon.outline = true
|
|
1863
|
+
if (obj.stroke.color) {
|
|
1864
|
+
obj.polygon.outlineColor = Cesium.Color.fromCssColorString(
|
|
1865
|
+
obj.stroke.color
|
|
1866
|
+
)
|
|
1867
|
+
}
|
|
1868
|
+
if (obj.stroke.width) {
|
|
1869
|
+
obj.polygon.outlineWidth = obj.stroke.width
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
// 多面边框设置在 Cesium 1.95 下无效,改为线条方式模拟多边形的边框,因此增加以下参数
|
|
1874
|
+
if (obj.stroke.color)
|
|
1875
|
+
obj.polyline.material = Cesium.Color.fromCssColorString(
|
|
1876
|
+
this.handleColor(obj.stroke.color)
|
|
1877
|
+
)
|
|
1878
|
+
if (obj.stroke.width) obj.polyline.width = obj.stroke.width
|
|
1879
|
+
}
|
|
1880
|
+
// console.log('style', obj);
|
|
1881
|
+
return obj
|
|
1882
|
+
},
|
|
1883
|
+
|
|
1884
|
+
// 处理聚合图片图标
|
|
1885
|
+
handleClusterImageIcon (url, label, width, height) {
|
|
1886
|
+
// const url = params.url; // 图片地址
|
|
1887
|
+
// const label = params.label; // 文字
|
|
1888
|
+
// const size = params.size; // 画布大小
|
|
1889
|
+
|
|
1890
|
+
// 创建画布对象
|
|
1891
|
+
let canvas = document.createElement("canvas")
|
|
1892
|
+
canvas.width = width
|
|
1893
|
+
canvas.height = height
|
|
1894
|
+
|
|
1895
|
+
let ctx = canvas.getContext("2d")
|
|
1896
|
+
|
|
1897
|
+
let promise = new Cesium.Resource.fetchImage(url).then((image) => {
|
|
1898
|
+
// 异常判断
|
|
1899
|
+
try {
|
|
1900
|
+
ctx.drawImage(image, 0, 0, width, height)
|
|
1901
|
+
} catch (e) {
|
|
1902
|
+
console.log(e)
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
// 渲染字体
|
|
1906
|
+
// font属性设置顺序:font-style, font-variant, font-weight, font-size, line-height, font-family
|
|
1907
|
+
ctx.fillStyle = Cesium.Color.WHITE.toCssColorString()
|
|
1908
|
+
ctx.font = "16px Microsoft YaHei"
|
|
1909
|
+
ctx.textAlign = "center"
|
|
1910
|
+
ctx.textBaseline = "middle"
|
|
1911
|
+
ctx.fillText(label, width / 2, height / 2)
|
|
1912
|
+
|
|
1913
|
+
return canvas
|
|
1914
|
+
})
|
|
1915
|
+
|
|
1916
|
+
return promise
|
|
1917
|
+
},
|
|
1918
|
+
|
|
1919
|
+
// 处理聚合颜色图标
|
|
1920
|
+
handleClusterColorIcon (color, label, size) {
|
|
1921
|
+
// 创建画布对象
|
|
1922
|
+
let canvas = document.createElement("canvas")
|
|
1923
|
+
canvas.width = size
|
|
1924
|
+
canvas.height = size
|
|
1925
|
+
|
|
1926
|
+
let ctx = canvas.getContext("2d")
|
|
1927
|
+
|
|
1928
|
+
// 绘制圆形
|
|
1929
|
+
ctx.beginPath()
|
|
1930
|
+
ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI)
|
|
1931
|
+
ctx.fillStyle = color // 设置不透明红色
|
|
1932
|
+
ctx.fill()
|
|
1933
|
+
|
|
1934
|
+
// 添加文字
|
|
1935
|
+
ctx.font = "16px Arial"
|
|
1936
|
+
ctx.fillStyle = "white"
|
|
1937
|
+
ctx.textAlign = "center"
|
|
1938
|
+
ctx.textBaseline = "middle"
|
|
1939
|
+
ctx.fillText(label, size / 2, size / 2)
|
|
1940
|
+
|
|
1941
|
+
return canvas.toDataURL()
|
|
1942
|
+
},
|
|
1943
|
+
|
|
1944
|
+
// 判断WKT格式
|
|
1945
|
+
isWkt (str) {
|
|
1946
|
+
// 去除字符串前后的空格并转换为大写
|
|
1947
|
+
const trimmedStr = str.trim().toUpperCase()
|
|
1948
|
+
|
|
1949
|
+
// 定义WKT格式的几何类型前缀
|
|
1950
|
+
const wktTypes = [
|
|
1951
|
+
"POINT",
|
|
1952
|
+
"LINESTRING",
|
|
1953
|
+
"POLYGON",
|
|
1954
|
+
"MULTIPOINT",
|
|
1955
|
+
"MULTILINESTRING",
|
|
1956
|
+
"MULTIPOLYGON",
|
|
1957
|
+
"GEOMETRYCOLLECTION",
|
|
1958
|
+
]
|
|
1959
|
+
|
|
1960
|
+
// 检查字符串是否以任意一个WKT类型开头
|
|
1961
|
+
return wktTypes.some((type) => trimmedStr.startsWith(type))
|
|
1962
|
+
},
|
|
1963
|
+
|
|
1964
|
+
// 显示弹窗
|
|
1965
|
+
addPopup (data) {
|
|
1966
|
+
// console.log('cesium显示弹窗', data);
|
|
1967
|
+
this.selectedEntity = data.coords
|
|
1968
|
+
this.popupVisible = true
|
|
1969
|
+
this.popupData = Object.assign({}, data)
|
|
1970
|
+
this.$nextTick(() => {
|
|
1971
|
+
this.updatePopupPosition(this.selectedEntity)
|
|
1972
|
+
})
|
|
1973
|
+
},
|
|
1974
|
+
|
|
1975
|
+
// 关闭弹窗
|
|
1976
|
+
closePopup () {
|
|
1977
|
+
this.popupVisible = false
|
|
1978
|
+
this.selectedEntity = null
|
|
1979
|
+
|
|
1980
|
+
// 清除点击高亮图层
|
|
1981
|
+
this.delWktLayer("light_layer")
|
|
1982
|
+
},
|
|
1983
|
+
|
|
1984
|
+
// 更新弹窗位置
|
|
1985
|
+
updatePopupPosition (entity, viewer = this.viewer) {
|
|
1986
|
+
// 画布高度
|
|
1987
|
+
const canvasHeight = viewer.scene.canvas.height
|
|
1988
|
+
|
|
1989
|
+
const position = entity.position.getValue(viewer.clock.currentTime)
|
|
1990
|
+
|
|
1991
|
+
// 将 Cesium 世界坐标转换为屏幕坐标。
|
|
1992
|
+
// 这一步是将 3D 世界中的点位转换为 2D 屏幕上的像素位置。
|
|
1993
|
+
const canvasPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
|
|
1994
|
+
viewer.scene,
|
|
1995
|
+
position
|
|
1996
|
+
)
|
|
1997
|
+
|
|
1998
|
+
if (Cesium.defined(canvasPosition)) {
|
|
1999
|
+
// 将实体置于视图中心
|
|
2000
|
+
const heading = viewer.camera.heading
|
|
2001
|
+
const pitch = viewer.camera.pitch
|
|
2002
|
+
const range = viewer.camera.positionCartographic.height
|
|
2003
|
+
viewer.camera.flyToBoundingSphere(new Cesium.BoundingSphere(position, 0), {
|
|
2004
|
+
offset: new Cesium.HeadingPitchRange(heading, pitch, range),
|
|
2005
|
+
duration: 1.5
|
|
2006
|
+
})
|
|
2007
|
+
|
|
2008
|
+
const popupElement = this.$refs.cesiumPopupRef.$refs.popupRef
|
|
2009
|
+
|
|
2010
|
+
viewer.cesiumWidget.container.appendChild(popupElement)
|
|
2011
|
+
viewer.scene.postRender.addEventListener(() => {
|
|
2012
|
+
const windowPosition = new Cesium.Cartesian2()
|
|
2013
|
+
Cesium.SceneTransforms.wgs84ToWindowCoordinates(
|
|
2014
|
+
viewer.scene,
|
|
2015
|
+
position,
|
|
2016
|
+
windowPosition
|
|
2017
|
+
)
|
|
2018
|
+
const popupWidth = popupElement.offsetWidth
|
|
2019
|
+
const popupHeight = popupElement.offsetHeight
|
|
2020
|
+
|
|
2021
|
+
this.popupPosition = {
|
|
2022
|
+
x: windowPosition.x - popupWidth / 2,
|
|
2023
|
+
y: canvasHeight - windowPosition.y + 100,
|
|
2024
|
+
}
|
|
2025
|
+
}, this)
|
|
2026
|
+
}
|
|
2027
|
+
},
|
|
2028
|
+
|
|
2029
|
+
// 缩放聚合数据计算
|
|
2030
|
+
zoomToCluster (clusteredEntities, viewer = this.viewer) {
|
|
2031
|
+
const positions = clusteredEntities.map((entity) =>
|
|
2032
|
+
entity.position.getValue(Cesium.JulianDate.now())
|
|
2033
|
+
)
|
|
2034
|
+
const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
|
|
2035
|
+
|
|
2036
|
+
// 获取当前相机的 heading 和 pitch
|
|
2037
|
+
const currentHeading = viewer.camera.heading
|
|
2038
|
+
const currentPitch = viewer.camera.pitch
|
|
2039
|
+
|
|
2040
|
+
// 定义最小缩放高度,以防止无限放大
|
|
2041
|
+
const minZoomHeight = 2000 // 单位:米,根据实际需求调整
|
|
2042
|
+
|
|
2043
|
+
const targetHeight = Math.max(boundingSphere.radius * 3, minZoomHeight) // 将目标高度增大一些
|
|
2044
|
+
const fovAdjustment = 1.5 // 调整视场角度,视情况增大此值
|
|
2045
|
+
|
|
2046
|
+
viewer.camera.flyToBoundingSphere(boundingSphere, {
|
|
2047
|
+
duration: 2.0, // 缓慢放大的效果
|
|
2048
|
+
// offset: new Cesium.HeadingPitchRange(0, -Math.PI / 2, targetHeight), // 无论当前什么视角,都调整为俯视
|
|
2049
|
+
offset: new Cesium.HeadingPitchRange(
|
|
2050
|
+
currentHeading,
|
|
2051
|
+
currentPitch,
|
|
2052
|
+
targetHeight * fovAdjustment
|
|
2053
|
+
), // 不改变当前朝向和角度
|
|
2054
|
+
complete: () => {
|
|
2055
|
+
// 限制放大级别
|
|
2056
|
+
const cameraHeight = this.viewer.camera.positionCartographic.height
|
|
2057
|
+
// console.log('cameraHeight', cameraHeight);
|
|
2058
|
+
if (cameraHeight < minZoomHeight) {
|
|
2059
|
+
this.viewer.camera.positionCartographic.height = minZoomHeight
|
|
2060
|
+
}
|
|
2061
|
+
},
|
|
2062
|
+
})
|
|
2063
|
+
},
|
|
2064
|
+
|
|
2065
|
+
// 开始环绕
|
|
2066
|
+
startSurround (params = {}, viewer = this.viewer) {
|
|
2067
|
+
this.stopSurround()
|
|
2068
|
+
|
|
2069
|
+
if (!params.center) {
|
|
2070
|
+
// 默认,以点为中心,向外环绕点位
|
|
2071
|
+
const center = viewer.scene.camera.positionCartographic
|
|
2072
|
+
this.surroundHandler = viewer.clock.onTick.addEventListener(() => {
|
|
2073
|
+
const rotationRate = 0.001 // 控制旋转速度
|
|
2074
|
+
const heading = viewer.scene.camera.heading + rotationRate
|
|
2075
|
+
viewer.scene.camera.setView({
|
|
2076
|
+
destination: Cesium.Cartesian3.fromRadians(
|
|
2077
|
+
center.longitude,
|
|
2078
|
+
center.latitude,
|
|
2079
|
+
center.height
|
|
2080
|
+
),
|
|
2081
|
+
orientation: {
|
|
2082
|
+
heading: heading,
|
|
2083
|
+
pitch: viewer.scene.camera.pitch,
|
|
2084
|
+
roll: viewer.scene.camera.roll,
|
|
2085
|
+
},
|
|
2086
|
+
})
|
|
2087
|
+
})
|
|
2088
|
+
} else {
|
|
2089
|
+
// 向内环绕点位
|
|
2090
|
+
let heading = 0 // 初始朝向
|
|
2091
|
+
let center = params.center // 中心点
|
|
2092
|
+
let pitch = params.pitch
|
|
2093
|
+
? Cesium.Math.toRadians(params.pitch)
|
|
2094
|
+
: Cesium.Math.toRadians(-30) // 俯仰角
|
|
2095
|
+
let range = params.range ? params.range : 20000 // 距离,单位米,1000 = 1公里 2万公里
|
|
2096
|
+
let stopAngle = params.stopAngle ? params.stopAngle : 360 // 停止角度,360为转一圈
|
|
2097
|
+
|
|
2098
|
+
this.surroundHandler = () => {
|
|
2099
|
+
// 每次更新的角度
|
|
2100
|
+
heading += 0.1
|
|
2101
|
+
|
|
2102
|
+
// console.log(`Current heading: ${heading}、${stopAngle}`); // 调试日志
|
|
2103
|
+
if (stopAngle && heading >= stopAngle) {
|
|
2104
|
+
// this.stopSurround(viewer);
|
|
2105
|
+
viewer.clock.onTick.removeEventListener(this.surroundHandler)
|
|
2106
|
+
this.surroundHandler = null
|
|
2107
|
+
return
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// 计算新的相机位置,HeadingPitchRange用于改变相机的姿态,HeadingPitchRange跟HeadingPitchRoll的使用方式差不多,不同的是,HeadingPitchRoll只能在原点进行姿态调整,HeadingPitchRange可以对距离目标点进行远近调整,这里我们设置的是距离,同时有个30度的俯角。
|
|
2111
|
+
var offset = new Cesium.HeadingPitchRange(
|
|
2112
|
+
Cesium.Math.toRadians(heading),
|
|
2113
|
+
pitch,
|
|
2114
|
+
range
|
|
2115
|
+
)
|
|
2116
|
+
|
|
2117
|
+
// 设置相机视图
|
|
2118
|
+
viewer.camera.lookAt(center, offset)
|
|
2119
|
+
|
|
2120
|
+
// 解决在使用camera.lookAt之后,造成问题:cesium内置的鼠标控制相机功能发生改变,例如:鼠标右键的挪移后相机是绕中心点旋转,而不是平移,flyto也会变得很诡异(虽然跳到目标点正常,但是相机飞的非常高
|
|
2121
|
+
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
viewer.clock.onTick.addEventListener(this.surroundHandler)
|
|
2125
|
+
|
|
2126
|
+
// 设置点击屏幕停止转动
|
|
2127
|
+
viewer.screenSpaceEventHandler.setInputAction(() => {
|
|
2128
|
+
// console.log('停止');
|
|
2129
|
+
this.stopSurround(viewer)
|
|
2130
|
+
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
|
2131
|
+
}
|
|
2132
|
+
},
|
|
2133
|
+
|
|
2134
|
+
// 停止环绕
|
|
2135
|
+
stopSurround (viewer = this.viewer) {
|
|
2136
|
+
if (this.surroundHandler) {
|
|
2137
|
+
this.surroundHandler()
|
|
2138
|
+
viewer.clock.onTick.removeEventListener(this.surroundHandler)
|
|
2139
|
+
this.surroundHandler = null
|
|
2140
|
+
}
|
|
2141
|
+
},
|
|
2142
|
+
|
|
2143
|
+
// 环绕开关
|
|
2144
|
+
toggleSurround (viewer = this.viewer) {
|
|
2145
|
+
if (this.isSurrounding) {
|
|
2146
|
+
this.stopSurround(viewer)
|
|
2147
|
+
} else {
|
|
2148
|
+
this.startSurround({}, viewer)
|
|
2149
|
+
}
|
|
2150
|
+
this.isSurrounding = !this.isSurrounding
|
|
2151
|
+
},
|
|
2152
|
+
|
|
2153
|
+
// 根据图层名称移除图层
|
|
2154
|
+
removeLayerByName (layerName) {
|
|
2155
|
+
this.delWmsLayer(layerName)
|
|
2156
|
+
this.delWktLayer(layerName)
|
|
2157
|
+
},
|
|
2158
|
+
|
|
2159
|
+
// 根据WKT返回几何对象
|
|
2160
|
+
getFeatureByWKT (wkt) {
|
|
2161
|
+
try {
|
|
2162
|
+
// 使用WKT解析器解析WKT字符串
|
|
2163
|
+
const geoJson = WKT.parse(wkt)
|
|
2164
|
+
// 返回一个具有 getCoordinates 方法的对象,以兼容原有的调用方式
|
|
2165
|
+
return {
|
|
2166
|
+
getCoordinates () {
|
|
2167
|
+
return geoJson.coordinates
|
|
2168
|
+
},
|
|
2169
|
+
// 保留原始数据
|
|
2170
|
+
coordinates: geoJson.coordinates,
|
|
2171
|
+
type: geoJson.type,
|
|
2172
|
+
geometry: geoJson,
|
|
2173
|
+
}
|
|
2174
|
+
} catch (error) {
|
|
2175
|
+
console.error("WKT解析失败:", error)
|
|
2176
|
+
return null
|
|
2177
|
+
}
|
|
2178
|
+
},
|
|
2179
|
+
|
|
2180
|
+
// 根据WKT返回坐标数组
|
|
2181
|
+
getCoordinatesByWkt (wkt) {
|
|
2182
|
+
const feature = this.getFeatureByWKT(wkt)
|
|
2183
|
+
if (feature && feature.coordinates) {
|
|
2184
|
+
return feature.coordinates
|
|
2185
|
+
}
|
|
2186
|
+
return null
|
|
2187
|
+
},
|
|
2188
|
+
|
|
2189
|
+
// 根据WKT返回长度或面积单位米
|
|
2190
|
+
getAreaByWkt (wkt, obj = { radius: 6378137, projection: "EPSG:4326" }) {
|
|
2191
|
+
try {
|
|
2192
|
+
// 解析WKT为GeoJSON
|
|
2193
|
+
const geoJson = WKT.parse(wkt)
|
|
2194
|
+
let output = 0
|
|
2195
|
+
|
|
2196
|
+
if (geoJson.type === "Polygon" || geoJson.type === "MultiPolygon") {
|
|
2197
|
+
// 计算面积
|
|
2198
|
+
output = this.calculatePolygonArea(geoJson)
|
|
2199
|
+
} else if (
|
|
2200
|
+
geoJson.type === "LineString" ||
|
|
2201
|
+
geoJson.type === "MultiLineString"
|
|
2202
|
+
) {
|
|
2203
|
+
// 计算长度
|
|
2204
|
+
output = this.calculateLineLength(geoJson)
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
return output
|
|
2208
|
+
} catch (error) {
|
|
2209
|
+
console.error("WKT解析失败:", error)
|
|
2210
|
+
return 0
|
|
2211
|
+
}
|
|
2212
|
+
},
|
|
2213
|
+
|
|
2214
|
+
// 计算多边形面积(平方米)
|
|
2215
|
+
calculatePolygonArea (geoJson) {
|
|
2216
|
+
let totalArea = 0
|
|
2217
|
+
|
|
2218
|
+
if (geoJson.type === "Polygon") {
|
|
2219
|
+
totalArea = this.calculateSinglePolygonArea(geoJson.coordinates)
|
|
2220
|
+
} else if (geoJson.type === "MultiPolygon") {
|
|
2221
|
+
for (const polygon of geoJson.coordinates) {
|
|
2222
|
+
totalArea += this.calculateSinglePolygonArea(polygon)
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
return totalArea
|
|
2227
|
+
},
|
|
2228
|
+
|
|
2229
|
+
// 计算单个多边形面积
|
|
2230
|
+
calculateSinglePolygonArea (coordinates) {
|
|
2231
|
+
// 使用球面多边形面积计算公式
|
|
2232
|
+
const rings = coordinates
|
|
2233
|
+
let area = 0
|
|
2234
|
+
|
|
2235
|
+
for (let i = 0; i < rings.length; i++) {
|
|
2236
|
+
const ring = rings[i]
|
|
2237
|
+
const ringArea = this.calculateRingArea(ring)
|
|
2238
|
+
if (i === 0) {
|
|
2239
|
+
area += ringArea
|
|
2240
|
+
} else {
|
|
2241
|
+
area -= ringArea // 内环减去面积
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
return Math.abs(area)
|
|
2246
|
+
},
|
|
2247
|
+
|
|
2248
|
+
// 计算环的面积
|
|
2249
|
+
calculateRingArea (coordinates) {
|
|
2250
|
+
let area = 0
|
|
2251
|
+
const len = coordinates.length
|
|
2252
|
+
|
|
2253
|
+
for (let i = 0; i < len; i++) {
|
|
2254
|
+
const j = (i + 1) % len
|
|
2255
|
+
const xi = (coordinates[i][0] * Math.PI) / 180
|
|
2256
|
+
const yi = (coordinates[i][1] * Math.PI) / 180
|
|
2257
|
+
const xj = (coordinates[j][0] * Math.PI) / 180
|
|
2258
|
+
const yj = (coordinates[j][1] * Math.PI) / 180
|
|
2259
|
+
|
|
2260
|
+
area += (xj - xi) * (2 + Math.sin(yi) + Math.sin(yj))
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
area = (area * 6378137 * 6378137) / 2
|
|
2264
|
+
return area
|
|
2265
|
+
},
|
|
2266
|
+
|
|
2267
|
+
// 计算线条长度(米)
|
|
2268
|
+
calculateLineLength (geoJson) {
|
|
2269
|
+
let totalLength = 0
|
|
2270
|
+
|
|
2271
|
+
if (geoJson.type === "LineString") {
|
|
2272
|
+
totalLength = this.calculateSingleLineLength(geoJson.coordinates)
|
|
2273
|
+
} else if (geoJson.type === "MultiLineString") {
|
|
2274
|
+
for (const line of geoJson.coordinates) {
|
|
2275
|
+
totalLength += this.calculateSingleLineLength(line)
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
return totalLength
|
|
2280
|
+
},
|
|
2281
|
+
|
|
2282
|
+
// 计算单条线的长度
|
|
2283
|
+
calculateSingleLineLength (coordinates) {
|
|
2284
|
+
let length = 0
|
|
2285
|
+
|
|
2286
|
+
for (let i = 0; i < coordinates.length - 1; i++) {
|
|
2287
|
+
const p1 = coordinates[i]
|
|
2288
|
+
const p2 = coordinates[i + 1]
|
|
2289
|
+
length += this.calculateDistance(p1, p2)
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
return length
|
|
2293
|
+
},
|
|
2294
|
+
|
|
2295
|
+
// 计算两点间距离(米)
|
|
2296
|
+
calculateDistance (p1, p2) {
|
|
2297
|
+
const lat1 = (p1[1] * Math.PI) / 180
|
|
2298
|
+
const lat2 = (p2[1] * Math.PI) / 180
|
|
2299
|
+
const deltaLat = ((p2[1] - p1[1]) * Math.PI) / 180
|
|
2300
|
+
const deltaLon = ((p2[0] - p1[0]) * Math.PI) / 180
|
|
2301
|
+
|
|
2302
|
+
const a =
|
|
2303
|
+
Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
|
|
2304
|
+
Math.cos(lat1) *
|
|
2305
|
+
Math.cos(lat2) *
|
|
2306
|
+
Math.sin(deltaLon / 2) *
|
|
2307
|
+
Math.sin(deltaLon / 2)
|
|
2308
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
|
|
2309
|
+
|
|
2310
|
+
return 6378137 * c // 地球半径 * c
|
|
2311
|
+
},
|
|
2312
|
+
|
|
2313
|
+
// 获取相机位置到地图中心点距离
|
|
2314
|
+
getCameraDistance () {
|
|
2315
|
+
let ray = this.viewer.camera.getPickRay((new Cesium.Cartesian2(this.viewer.canvas.clientWidth / 2, this.viewer.canvas.clientHeight / 2)))
|
|
2316
|
+
const intersection = this.viewer.scene.globe.pick(ray, this.viewer.scene)
|
|
2317
|
+
let range = 0
|
|
2318
|
+
if (intersection) {
|
|
2319
|
+
const distance = Cesium.Cartesian3.distance(this.viewer.camera.position, intersection)
|
|
2320
|
+
range = parseFloat(distance.toFixed(1))
|
|
2321
|
+
}
|
|
2322
|
+
return range
|
|
2323
|
+
},
|
|
2324
|
+
|
|
2325
|
+
// 三维地图绘制图层方法
|
|
2326
|
+
drawLayer (data, callback) {
|
|
2327
|
+
const { viewer } = this
|
|
2328
|
+
if (!data || !data.type || !data.name) return
|
|
2329
|
+
// 清除测量工具相关的点位
|
|
2330
|
+
if (this.measureTool && this.measureTool._drawLayer) {
|
|
2331
|
+
this.measureTool._drawLayer.entities.removeAll()
|
|
2332
|
+
}
|
|
2333
|
+
// 创建或获取数据源
|
|
2334
|
+
if (!this.drawLayerObj[data.name]) {
|
|
2335
|
+
this.drawLayerObj[data.name] = new Cesium.CustomDataSource(data.name)
|
|
2336
|
+
viewer.dataSources.add(this.drawLayerObj[data.name])
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
// 如果是单一绘制模式,清除之前的要素
|
|
2340
|
+
if (data.single === true) {
|
|
2341
|
+
this.drawLayerObj[data.name].entities.removeAll()
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
// 根据类型进行绘制
|
|
2345
|
+
switch (data.type) {
|
|
2346
|
+
case "Point":
|
|
2347
|
+
this.drawPoint(data, callback)
|
|
2348
|
+
break
|
|
2349
|
+
case "LineString":
|
|
2350
|
+
this.drawLineString(data, callback)
|
|
2351
|
+
break
|
|
2352
|
+
case "Polygon":
|
|
2353
|
+
this.drawPolygon(data, callback)
|
|
2354
|
+
break
|
|
2355
|
+
default:
|
|
2356
|
+
console.warn("不支持的绘制类型:", data.type)
|
|
2357
|
+
}
|
|
2358
|
+
},
|
|
2359
|
+
|
|
2360
|
+
// 绘制点
|
|
2361
|
+
drawPoint: function (data, callback) {
|
|
2362
|
+
const viewer = this.viewer;
|
|
2363
|
+
const _this = this;
|
|
2364
|
+
|
|
2365
|
+
// 设置鼠标样式
|
|
2366
|
+
const style = `url(${require('./imgs/circle.png')}) 8 8, auto`;
|
|
2367
|
+
this.viewer.scene.canvas.style.cursor = style;
|
|
2368
|
+
// 创建点击处理器(场景级)
|
|
2369
|
+
_this.drawHandler = new Cesium.ScreenSpaceEventHandler(
|
|
2370
|
+
viewer.scene.canvas
|
|
2371
|
+
);
|
|
2372
|
+
let heightPoint;
|
|
2373
|
+
_this.drawHandler.setInputAction(async function (click) {
|
|
2374
|
+
let pickedPosition = viewer.scene.pickPosition(click.position);
|
|
2375
|
+
if (!pickedPosition) {
|
|
2376
|
+
pickedPosition = viewer.camera.pickEllipsoid(
|
|
2377
|
+
click.position,
|
|
2378
|
+
viewer.scene.globe.ellipsoid
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2381
|
+
if (!heightPoint) {
|
|
2382
|
+
heightPoint = pickedPosition;
|
|
2383
|
+
}
|
|
2384
|
+
if (!pickedPosition) return;
|
|
2385
|
+
|
|
2386
|
+
// 转换为地理坐标
|
|
2387
|
+
const cartographic = Cesium.Cartographic.fromCartesian(pickedPosition);
|
|
2388
|
+
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
|
2389
|
+
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
|
2390
|
+
// 构造结果
|
|
2391
|
+
const result = {
|
|
2392
|
+
type: "Point",
|
|
2393
|
+
coords: [longitude, latitude],
|
|
2394
|
+
coordinate: `POINT(${longitude} ${latitude})`,
|
|
2395
|
+
layerName: data.name
|
|
2396
|
+
};
|
|
2397
|
+
// 调用回调
|
|
2398
|
+
if (callback && typeof callback === "function") {
|
|
2399
|
+
callback(result);
|
|
2400
|
+
}
|
|
2401
|
+
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
2402
|
+
},
|
|
2403
|
+
|
|
2404
|
+
// 绘制线
|
|
2405
|
+
drawLineString: function (data, callback) {
|
|
2406
|
+
const viewer = this.viewer;
|
|
2407
|
+
const _this = this;
|
|
2408
|
+
|
|
2409
|
+
// 使用现有的测距工具,但去掉测量功能
|
|
2410
|
+
const drawStyle = {
|
|
2411
|
+
line: {
|
|
2412
|
+
width: data.style?.stroke?.width || 2,
|
|
2413
|
+
material: data.style?.stroke?.color
|
|
2414
|
+
? Cesium.Color.fromCssColorString(data.style.stroke.color)
|
|
2415
|
+
: Cesium.Color.fromCssColorString("#0099ff"),
|
|
2416
|
+
},
|
|
2417
|
+
point: {
|
|
2418
|
+
color: data.style?.point?.color ? Cesium.Color.fromCssColorString(data.style.point.color) : Cesium.Color.RED,
|
|
2419
|
+
pixelSize: 10,
|
|
2420
|
+
outlineColor: data.style?.point?.outlineColor ? Cesium.Color.fromCssColorString(data.style.point.outlineColor) : Cesium.Color.WHITE,
|
|
2421
|
+
outlineWidth: 3,
|
|
2422
|
+
show: true,
|
|
2423
|
+
},
|
|
2424
|
+
};
|
|
2425
|
+
this.measureTool.drawLineMeasureGraphics({
|
|
2426
|
+
clampToGround: true,
|
|
2427
|
+
measure: false, // 不显示测量结果
|
|
2428
|
+
layerName: data.name,
|
|
2429
|
+
style: drawStyle,
|
|
2430
|
+
callback: (result) => {
|
|
2431
|
+
if (result && result.points) {
|
|
2432
|
+
// 转换坐标
|
|
2433
|
+
const coordinates = result.points.map((position) => {
|
|
2434
|
+
//将拿到的坐标转换成笛卡尔坐标格式
|
|
2435
|
+
const cartesian = Cesium.Cartesian3.fromDegrees(
|
|
2436
|
+
position.lng, // 必选:经度(角度制)
|
|
2437
|
+
position.lat, // 必选:纬度(角度制)
|
|
2438
|
+
position.alt, // 可选:高程(默认 0 米)
|
|
2439
|
+
Cesium.Ellipsoid.WGS84, // 可选:参考椭球(默认 WGS84,无需修改)
|
|
2440
|
+
undefined // 可选:复用结果对象(优化性能,非必需)
|
|
2441
|
+
);
|
|
2442
|
+
// Cesium.Cartographic.fromCartesian接受的格式是笛卡尔坐标格式,转成大地格式
|
|
2443
|
+
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
|
2444
|
+
return [
|
|
2445
|
+
//将大地格式转成角度制,大地格式是弧度,转成角度制(如 117.28°、27.58°)
|
|
2446
|
+
Cesium.Math.toDegrees(cartographic.longitude),
|
|
2447
|
+
Cesium.Math.toDegrees(cartographic.latitude),
|
|
2448
|
+
];
|
|
2449
|
+
});
|
|
2450
|
+
// 构造WKT
|
|
2451
|
+
const coordString = coordinates
|
|
2452
|
+
.map((coord) => `${coord[0]} ${coord[1]}`)
|
|
2453
|
+
.join(",");
|
|
2454
|
+
const wktValue = `LINESTRING(${coordString})`;
|
|
2455
|
+
|
|
2456
|
+
const finalResult = {
|
|
2457
|
+
type: "LineString",
|
|
2458
|
+
coordinates: coordinates,
|
|
2459
|
+
wktValue: wktValue,
|
|
2460
|
+
entity: result.entity,
|
|
2461
|
+
layerName: data.name,
|
|
2462
|
+
length: result.distance || 0,
|
|
2463
|
+
};
|
|
2464
|
+
|
|
2465
|
+
// 如果指定了图层,将实体移动到指定图层
|
|
2466
|
+
if (_this.drawLayerObj[data.name] && result.entity) {
|
|
2467
|
+
// 从测量图层移除
|
|
2468
|
+
_this.measureTool._drawLayer.entities.remove(result.entity);
|
|
2469
|
+
// 添加到指定图层
|
|
2470
|
+
_this.drawLayerObj[data.name].entities.add(result.entity);
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
if (callback && typeof callback === "function") {
|
|
2474
|
+
callback(
|
|
2475
|
+
finalResult,
|
|
2476
|
+
_this.drawLayerObj[data.name].entities.values
|
|
2477
|
+
);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
},
|
|
2481
|
+
});
|
|
2482
|
+
},
|
|
2483
|
+
|
|
2484
|
+
// 绘制面
|
|
2485
|
+
drawPolygon: function (data, callback) {
|
|
2486
|
+
const viewer = this.viewer;
|
|
2487
|
+
const _this = this;
|
|
2488
|
+
|
|
2489
|
+
// 使用现有的测面积工具,但去掉测量功能
|
|
2490
|
+
const drawStyle = {
|
|
2491
|
+
line: {
|
|
2492
|
+
width: data.style?.stroke?.width || 2,
|
|
2493
|
+
material: data.style?.stroke?.color
|
|
2494
|
+
? Cesium.Color.fromCssColorString(data.style.stroke.color)
|
|
2495
|
+
: Cesium.Color.fromCssColorString("#0099ff"),
|
|
2496
|
+
show: true,
|
|
2497
|
+
},
|
|
2498
|
+
point: {
|
|
2499
|
+
pixelSize: 8,
|
|
2500
|
+
outlineColor: Cesium.Color.WHITE,
|
|
2501
|
+
outlineWidth: 2,
|
|
2502
|
+
show: true,
|
|
2503
|
+
},
|
|
2504
|
+
polygon: {
|
|
2505
|
+
material: data.style?.fill?.color
|
|
2506
|
+
? Cesium.Color.fromCssColorString(data.style.fill.color)
|
|
2507
|
+
: Cesium.Color.fromCssColorString("rgba(255, 255, 255, 0.2)"),
|
|
2508
|
+
},
|
|
2509
|
+
centerPoint: {
|
|
2510
|
+
pixelSize: 5,
|
|
2511
|
+
outlineColor: Cesium.Color.RED,
|
|
2512
|
+
outlineWidth: 2,
|
|
2513
|
+
},
|
|
2514
|
+
};
|
|
2515
|
+
|
|
2516
|
+
this.measureTool.drawAreaMeasureGraphics({
|
|
2517
|
+
clampToGround: true,
|
|
2518
|
+
measure: false, // 不显示测量结果
|
|
2519
|
+
layerName: data.name,
|
|
2520
|
+
style: drawStyle,
|
|
2521
|
+
callback: (result) => {
|
|
2522
|
+
console.log("拖拽了")
|
|
2523
|
+
if (result && result.points.length) {
|
|
2524
|
+
const coordinates = result.points.map((item) => {
|
|
2525
|
+
return [
|
|
2526
|
+
item.lng, // 经度已经是度数格式
|
|
2527
|
+
item.lat, // 纬度已经是度数格式
|
|
2528
|
+
];
|
|
2529
|
+
});
|
|
2530
|
+
|
|
2531
|
+
// 构造WKT
|
|
2532
|
+
const coordString = coordinates
|
|
2533
|
+
.map((coord) => `${coord[0]} ${coord[1]}`)
|
|
2534
|
+
.join(",");
|
|
2535
|
+
const wktValue = `POLYGON((${coordString}))`;
|
|
2536
|
+
|
|
2537
|
+
const finalResult = {
|
|
2538
|
+
type: "Polygon",
|
|
2539
|
+
coords: [coordinates], // 多边形需要嵌套数组
|
|
2540
|
+
wktValue: wktValue,
|
|
2541
|
+
entity: result.entity,
|
|
2542
|
+
layerName: data.name,
|
|
2543
|
+
area: result.area || 0,
|
|
2544
|
+
};
|
|
2545
|
+
|
|
2546
|
+
// 如果指定了图层,将实体移动到指定图层
|
|
2547
|
+
if (_this.drawLayerObj[data.name] && result.entity) {
|
|
2548
|
+
// 从测量图层移除
|
|
2549
|
+
_this.measureTool._drawLayer.entities.remove(result.entity);
|
|
2550
|
+
// 添加到指定图层
|
|
2551
|
+
_this.drawLayerObj[data.name].entities.add(result.entity);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
if (callback && typeof callback === "function") {
|
|
2555
|
+
callback(finalResult);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
},
|
|
2559
|
+
});
|
|
2560
|
+
},
|
|
2561
|
+
// 获取绘制样式
|
|
2562
|
+
getDrawStyle: function (data, type) {
|
|
2563
|
+
const defaultStyles = {
|
|
2564
|
+
point: {
|
|
2565
|
+
pixelSize: 10, //控制点在屏幕上的显示大小,值越大点越大,值越小点越小
|
|
2566
|
+
color:
|
|
2567
|
+
data.style && data.style.color
|
|
2568
|
+
? Cesium.Color.fromCssColorString(data.style.color)
|
|
2569
|
+
: Cesium.Color.fromCssColorString("rgba(255,255,255,0)"),
|
|
2570
|
+
outlineColor:
|
|
2571
|
+
data.style && data.style.color
|
|
2572
|
+
? Cesium.Color.fromCssColorString(data.style.outlineColor)
|
|
2573
|
+
: Cesium.Color.fromCssColorString("rgba(255,255,255,0)"), //设置点的外边框颜色,在点的周围绘制一个边框,增强点的可视性和层次感
|
|
2574
|
+
outlineWidth:
|
|
2575
|
+
data.style && data.style.outlineWidth
|
|
2576
|
+
? data.style.outlineWidth
|
|
2577
|
+
: undefined, //设置点的外边框宽度,默认2
|
|
2578
|
+
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, //设置点的高度参考,默认CLAMP_TO_GROUND
|
|
2579
|
+
disableDepthTestDistance: Number.POSITIVE_INFINITY, //设置点的不进行深度测试的距离,默认无限大
|
|
2580
|
+
},
|
|
2581
|
+
line: {
|
|
2582
|
+
width: 2,
|
|
2583
|
+
material: Cesium.Color.YELLOW,
|
|
2584
|
+
clampToGround: true,
|
|
2585
|
+
},
|
|
2586
|
+
polygon: {
|
|
2587
|
+
material: Cesium.Color.YELLOW.withAlpha(0.3),
|
|
2588
|
+
outline: true,
|
|
2589
|
+
outlineColor: Cesium.Color.YELLOW,
|
|
2590
|
+
outlineWidth: 2,
|
|
2591
|
+
},
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
let style = defaultStyles[type] || {}
|
|
2595
|
+
|
|
2596
|
+
// 应用用户自定义样式
|
|
2597
|
+
if (data.style) {
|
|
2598
|
+
if (type === "point" && data.style.point) {
|
|
2599
|
+
style = Object.assign(style, data.style.point)
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
if (type === "line") {
|
|
2603
|
+
if (data.style.stroke) {
|
|
2604
|
+
if (data.style.stroke.color) {
|
|
2605
|
+
style.material = Cesium.Color.fromCssColorString(
|
|
2606
|
+
data.style.stroke.color
|
|
2607
|
+
)
|
|
2608
|
+
}
|
|
2609
|
+
if (data.style.stroke.width) {
|
|
2610
|
+
style.width = data.style.stroke.width
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
if (type === "polygon") {
|
|
2616
|
+
if (data.style.fill && data.style.fill.color) {
|
|
2617
|
+
style.material = Cesium.Color.fromCssColorString(
|
|
2618
|
+
data.style.fill.color
|
|
2619
|
+
)
|
|
2620
|
+
}
|
|
2621
|
+
if (data.style.stroke) {
|
|
2622
|
+
if (data.style.stroke.color) {
|
|
2623
|
+
style.outlineColor = Cesium.Color.fromCssColorString(
|
|
2624
|
+
data.style.stroke.color
|
|
2625
|
+
)
|
|
2626
|
+
}
|
|
2627
|
+
if (data.style.stroke.width) {
|
|
2628
|
+
style.outlineWidth = data.style.stroke.width
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
return style
|
|
2635
|
+
},
|
|
2636
|
+
|
|
2637
|
+
// 清除绘制图层
|
|
2638
|
+
clearDrawLayer (obj) {
|
|
2639
|
+
const name = obj.name
|
|
2640
|
+
this.viewer.scene.canvas.style.cursor = 'default'
|
|
2641
|
+
// 清除指定图层的所有实体和样式
|
|
2642
|
+
if (this.drawLayerObj[name]) {
|
|
2643
|
+
const dataSource = this.drawLayerObj[name]
|
|
2644
|
+
//清除所有实体
|
|
2645
|
+
dataSource.entities.removeAll()
|
|
2646
|
+
}
|
|
2647
|
+
// 清除测量工具相关的点位
|
|
2648
|
+
if (this.measureTool && this.measureTool._drawLayer) {
|
|
2649
|
+
this.measureTool._drawLayer.entities.removeAll()
|
|
2650
|
+
}
|
|
2651
|
+
// 如果有正在进行的绘制,清除对应的handler
|
|
2652
|
+
if (this.drawHandler && !obj.active) {
|
|
2653
|
+
this.drawHandler.destroy()
|
|
2654
|
+
this.drawHandler = null
|
|
2655
|
+
}
|
|
2656
|
+
},
|
|
2657
|
+
|
|
2658
|
+
// 通过WKT获取中心点
|
|
2659
|
+
getCenterByWkt: function (wkt, only = false) {
|
|
2660
|
+
if (!wkt) {
|
|
2661
|
+
return null
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
try {
|
|
2665
|
+
// 解析WKT为GeoJSON
|
|
2666
|
+
const geoJson = WKT.parse(wkt)
|
|
2667
|
+
return this.calculateGeometryCenter(geoJson, only)
|
|
2668
|
+
} catch (error) {
|
|
2669
|
+
console.error("WKT解析失败:", error)
|
|
2670
|
+
return null
|
|
2671
|
+
}
|
|
2672
|
+
},
|
|
2673
|
+
|
|
2674
|
+
// 计算几何图形中心点(三维版本)
|
|
2675
|
+
calculateGeometryCenter: function (geoJson, only = false) {
|
|
2676
|
+
const type = geoJson.type
|
|
2677
|
+
const coordinates = geoJson.coordinates
|
|
2678
|
+
|
|
2679
|
+
switch (type) {
|
|
2680
|
+
case "Point":
|
|
2681
|
+
// 点类型直接返回坐标
|
|
2682
|
+
return coordinates
|
|
2683
|
+
|
|
2684
|
+
case "MultiPoint":
|
|
2685
|
+
// 多点类型
|
|
2686
|
+
if (only) {
|
|
2687
|
+
return this.calculatePointsBoundsCenter(coordinates)
|
|
2688
|
+
} else {
|
|
2689
|
+
return coordinates // 返回所有点坐标
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
case "LineString":
|
|
2693
|
+
// 线类型
|
|
2694
|
+
if (only) {
|
|
2695
|
+
return this.calculatePointsBoundsCenter(coordinates)
|
|
2696
|
+
} else {
|
|
2697
|
+
return [
|
|
2698
|
+
coordinates[0], // 起点
|
|
2699
|
+
coordinates[coordinates.length - 1], // 终点
|
|
2700
|
+
]
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2703
|
+
case "MultiLineString":
|
|
2704
|
+
// 多线类型
|
|
2705
|
+
const allPoints = []
|
|
2706
|
+
coordinates.forEach((line) => {
|
|
2707
|
+
if (only) {
|
|
2708
|
+
allPoints.push(...line)
|
|
2709
|
+
} else {
|
|
2710
|
+
allPoints.push(line[0]) // 起点
|
|
2711
|
+
allPoints.push(line[line.length - 1]) // 终点
|
|
2712
|
+
}
|
|
2713
|
+
})
|
|
2714
|
+
return only ? this.calculatePointsBoundsCenter(allPoints) : allPoints
|
|
2715
|
+
|
|
2716
|
+
case "Polygon":
|
|
2717
|
+
// 多边形返回内部点或几何中心
|
|
2718
|
+
return this.calculatePolygonCenter(coordinates, only)
|
|
2719
|
+
|
|
2720
|
+
case "MultiPolygon":
|
|
2721
|
+
if (only) {
|
|
2722
|
+
// 返回整体边界框的中心点
|
|
2723
|
+
return this.calculateMultiPolygonBoundsCenter(coordinates)
|
|
2724
|
+
} else {
|
|
2725
|
+
// 返回每个多边形的中心点
|
|
2726
|
+
return coordinates.map((polygon) =>
|
|
2727
|
+
this.calculatePolygonCenter(polygon, true)
|
|
2728
|
+
)
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
default:
|
|
2732
|
+
console.warn("不支持的几何类型:", type)
|
|
2733
|
+
return null
|
|
2734
|
+
}
|
|
2735
|
+
},
|
|
2736
|
+
|
|
2737
|
+
// 计算多边形中心点
|
|
2738
|
+
calculatePolygonCenter: function (coordinates, only = false) {
|
|
2739
|
+
if (only) {
|
|
2740
|
+
// 返回边界框中心点
|
|
2741
|
+
return this.calculatePointsBoundsCenter(coordinates[0]) // 外环
|
|
2742
|
+
} else {
|
|
2743
|
+
// 返回多边形的几何重心
|
|
2744
|
+
return this.calculatePolygonCentroid(coordinates[0]) // 外环
|
|
2745
|
+
}
|
|
2746
|
+
},
|
|
2747
|
+
|
|
2748
|
+
// 计算多多边形边界框中心
|
|
2749
|
+
calculateMultiPolygonBoundsCenter: function (coordinates) {
|
|
2750
|
+
const allPoints = []
|
|
2751
|
+
// 收集所有多边形的所有点
|
|
2752
|
+
coordinates.forEach((polygon) => {
|
|
2753
|
+
polygon[0].forEach((point) => {
|
|
2754
|
+
// 外环
|
|
2755
|
+
allPoints.push(point)
|
|
2756
|
+
})
|
|
2757
|
+
})
|
|
2758
|
+
return this.calculatePointsBoundsCenter(allPoints)
|
|
2759
|
+
},
|
|
2760
|
+
|
|
2761
|
+
// 计算点集的边界框中心点
|
|
2762
|
+
calculatePointsBoundsCenter: function (coordinates) {
|
|
2763
|
+
if (!coordinates || coordinates.length === 0) {
|
|
2764
|
+
return null
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
let minLng = Infinity,
|
|
2768
|
+
minLat = Infinity
|
|
2769
|
+
let maxLng = -Infinity,
|
|
2770
|
+
maxLat = -Infinity
|
|
2771
|
+
|
|
2772
|
+
coordinates.forEach((point) => {
|
|
2773
|
+
const lng = point[0]
|
|
2774
|
+
const lat = point[1]
|
|
2775
|
+
minLng = Math.min(minLng, lng)
|
|
2776
|
+
maxLng = Math.max(maxLng, lng)
|
|
2777
|
+
minLat = Math.min(minLat, lat)
|
|
2778
|
+
maxLat = Math.max(maxLat, lat)
|
|
2779
|
+
})
|
|
2780
|
+
|
|
2781
|
+
return [(minLng + maxLng) / 2, (minLat + maxLat) / 2]
|
|
2782
|
+
},
|
|
2783
|
+
|
|
2784
|
+
// 计算多边形重心(使用面积加权的方法)
|
|
2785
|
+
calculatePolygonCentroid: function (coordinates) {
|
|
2786
|
+
if (!coordinates || coordinates.length < 3) {
|
|
2787
|
+
return this.calculatePointsBoundsCenter(coordinates)
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
let area = 0
|
|
2791
|
+
let centroidX = 0
|
|
2792
|
+
let centroidY = 0
|
|
2793
|
+
|
|
2794
|
+
// 使用shoelace公式计算面积和重心
|
|
2795
|
+
for (let i = 0; i < coordinates.length - 1; i++) {
|
|
2796
|
+
const x0 = coordinates[i][0]
|
|
2797
|
+
const y0 = coordinates[i][1]
|
|
2798
|
+
const x1 = coordinates[i + 1][0]
|
|
2799
|
+
const y1 = coordinates[i + 1][1]
|
|
2800
|
+
|
|
2801
|
+
const cross = x0 * y1 - x1 * y0
|
|
2802
|
+
area += cross
|
|
2803
|
+
centroidX += (x0 + x1) * cross
|
|
2804
|
+
centroidY += (y0 + y1) * cross
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
area = area / 2
|
|
2808
|
+
if (Math.abs(area) < 1e-10) {
|
|
2809
|
+
// 如果面积太小,返回边界框中心
|
|
2810
|
+
return this.calculatePointsBoundsCenter(coordinates)
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
centroidX = centroidX / (6 * area)
|
|
2814
|
+
centroidY = centroidY / (6 * area)
|
|
2815
|
+
|
|
2816
|
+
return [centroidX, centroidY]
|
|
2817
|
+
},
|
|
2818
|
+
|
|
2819
|
+
// 根据坐标生成WKT(三维版本)
|
|
2820
|
+
getWKTByCoordinates: function (coordinates, isArray = false) {
|
|
2821
|
+
// 如果是Cesium.Cartesian3对象(三维笛卡尔坐标)
|
|
2822
|
+
if (
|
|
2823
|
+
coordinates &&
|
|
2824
|
+
coordinates.x !== undefined &&
|
|
2825
|
+
coordinates.y !== undefined &&
|
|
2826
|
+
coordinates.z !== undefined
|
|
2827
|
+
) {
|
|
2828
|
+
// 转换为地理坐标(弧度)
|
|
2829
|
+
const cartographic = Cesium.Cartographic.fromCartesian(coordinates)
|
|
2830
|
+
// 转换为度数
|
|
2831
|
+
const lng = Cesium.Math.toDegrees(cartographic.longitude)
|
|
2832
|
+
const lat = Cesium.Math.toDegrees(cartographic.latitude)
|
|
2833
|
+
if (isArray) {
|
|
2834
|
+
return [lng, lat]
|
|
2835
|
+
} else {
|
|
2836
|
+
return `POINT(${lng} ${lat})`
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
// 如果是二维数组格式 [lng, lat]
|
|
2841
|
+
if (Array.isArray(coordinates) && coordinates.length >= 2) {
|
|
2842
|
+
const lng = coordinates[0]
|
|
2843
|
+
const lat = coordinates[1]
|
|
2844
|
+
return `POINT(${lng} ${lat})`
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
console.error("getWKTByCoordinates: 坐标参数格式不正确")
|
|
2848
|
+
return null
|
|
2849
|
+
},
|
|
2850
|
+
|
|
2851
|
+
// 更新地图宽度
|
|
2852
|
+
updateSizeFunction () {
|
|
2853
|
+
setTimeout(() => {
|
|
2854
|
+
if (this.viewer) {
|
|
2855
|
+
// 强制重新计算容器大小
|
|
2856
|
+
this.viewer.container.getBoundingClientRect()
|
|
2857
|
+
// 请求场景重新渲染
|
|
2858
|
+
this.viewer.scene.requestRender()
|
|
2859
|
+
// 刷新相机
|
|
2860
|
+
this.viewer.camera.moveEnd.raiseEvent()
|
|
2861
|
+
}
|
|
2862
|
+
}, 10)
|
|
2863
|
+
},
|
|
2864
|
+
|
|
2865
|
+
// 获取根据wkt值返回缓冲区的wkt
|
|
2866
|
+
getBufferWkt: function (wkt, buffer, mapType) {
|
|
2867
|
+
try {
|
|
2868
|
+
// 解析WKT获取坐标
|
|
2869
|
+
const geoJson = WKT.parse(wkt)
|
|
2870
|
+
const coordinates = geoJson.coordinates
|
|
2871
|
+
|
|
2872
|
+
if (mapType === "circle") {
|
|
2873
|
+
// 如果是圆形,直接返回圆形的WKT
|
|
2874
|
+
const center = coordinates
|
|
2875
|
+
return `CIRCLE(${center[0]} ${center[1]} ${buffer})`
|
|
2876
|
+
} else {
|
|
2877
|
+
// 计算缓冲区的点
|
|
2878
|
+
const bufferPoints = this.calculateBufferPoints(coordinates, buffer)
|
|
2879
|
+
|
|
2880
|
+
// 根据几何类型生成不同的WKT
|
|
2881
|
+
if (geoJson.type === "Point") {
|
|
2882
|
+
// 点的缓冲区是一个圆,返回多边形WKT
|
|
2883
|
+
const polygonCoords = bufferPoints
|
|
2884
|
+
.map((point) => `${point[0]} ${point[1]}`)
|
|
2885
|
+
.join(",")
|
|
2886
|
+
return `POLYGON((${polygonCoords}))`
|
|
2887
|
+
} else if (geoJson.type === "LineString") {
|
|
2888
|
+
// 线的缓冲区是一个带状多边形
|
|
2889
|
+
const leftPoints = bufferPoints.left
|
|
2890
|
+
const rightPoints = bufferPoints.right.reverse()
|
|
2891
|
+
const allPoints = [...leftPoints, ...rightPoints, leftPoints[0]]
|
|
2892
|
+
const polygonCoords = allPoints
|
|
2893
|
+
.map((point) => `${point[0]} ${point[1]}`)
|
|
2894
|
+
.join(",")
|
|
2895
|
+
return `POLYGON((${polygonCoords}))`
|
|
2896
|
+
} else if (geoJson.type === "Polygon") {
|
|
2897
|
+
// 多边形的缓冲区是一个更大的多边形
|
|
2898
|
+
const outerRing = this.calculatePolygonBuffer(
|
|
2899
|
+
coordinates[0],
|
|
2900
|
+
buffer
|
|
2901
|
+
)
|
|
2902
|
+
const polygonCoords = outerRing
|
|
2903
|
+
.map((point) => `${point[0]} ${point[1]}`)
|
|
2904
|
+
.join(",")
|
|
2905
|
+
return `POLYGON((${polygonCoords}))`
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
} catch (error) {
|
|
2909
|
+
console.error("计算缓冲区失败:", error)
|
|
2910
|
+
return wkt
|
|
2911
|
+
}
|
|
2912
|
+
},
|
|
2913
|
+
|
|
2914
|
+
// 计算缓冲区的点
|
|
2915
|
+
calculateBufferPoints (coordinates, buffer) {
|
|
2916
|
+
if (Array.isArray(coordinates[0])) {
|
|
2917
|
+
// 线或多边形
|
|
2918
|
+
const points = []
|
|
2919
|
+
const leftPoints = []
|
|
2920
|
+
const rightPoints = []
|
|
2921
|
+
|
|
2922
|
+
for (let i = 0; i < coordinates.length - 1; i++) {
|
|
2923
|
+
const start = coordinates[i]
|
|
2924
|
+
const end = coordinates[i + 1]
|
|
2925
|
+
|
|
2926
|
+
// 计算垂直于线段的向量
|
|
2927
|
+
const dx = end[0] - start[0]
|
|
2928
|
+
const dy = end[1] - start[1]
|
|
2929
|
+
const length = Math.sqrt(dx * dx + dy * dy)
|
|
2930
|
+
|
|
2931
|
+
// 归一化并乘以缓冲区距离
|
|
2932
|
+
const normalX = (-dy / length) * buffer
|
|
2933
|
+
const normalY = (dx / length) * buffer
|
|
2934
|
+
|
|
2935
|
+
// 添加左右两侧的点
|
|
2936
|
+
leftPoints.push([start[0] + normalX, start[1] + normalY])
|
|
2937
|
+
rightPoints.push([start[0] - normalX, start[1] - normalY])
|
|
2938
|
+
|
|
2939
|
+
if (i === coordinates.length - 2) {
|
|
2940
|
+
leftPoints.push([end[0] + normalX, end[1] + normalY])
|
|
2941
|
+
rightPoints.push([end[0] - normalX, end[1] - normalY])
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
return { left: leftPoints, right: rightPoints }
|
|
2946
|
+
} else {
|
|
2947
|
+
// 单点
|
|
2948
|
+
const points = []
|
|
2949
|
+
const segments = 32 // 圆形的分段数
|
|
2950
|
+
|
|
2951
|
+
for (let i = 0; i <= segments; i++) {
|
|
2952
|
+
const angle = (i / segments) * Math.PI * 2
|
|
2953
|
+
const x = coordinates[0] + Math.cos(angle) * buffer
|
|
2954
|
+
const y = coordinates[1] + Math.sin(angle) * buffer
|
|
2955
|
+
points.push([x, y])
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
return points
|
|
2959
|
+
}
|
|
2960
|
+
},
|
|
2961
|
+
|
|
2962
|
+
// 计算多边形缓冲区
|
|
2963
|
+
calculatePolygonBuffer (ring, buffer) {
|
|
2964
|
+
const bufferPoints = []
|
|
2965
|
+
const segments = 32 // 圆角的分段数
|
|
2966
|
+
|
|
2967
|
+
for (let i = 0; i < ring.length - 1; i++) {
|
|
2968
|
+
const current = ring[i]
|
|
2969
|
+
const next = ring[i + 1]
|
|
2970
|
+
const prev = i > 0 ? ring[i - 1] : ring[ring.length - 2]
|
|
2971
|
+
|
|
2972
|
+
// 计算当前边的方向向量
|
|
2973
|
+
const dx = next[0] - current[0]
|
|
2974
|
+
const dy = next[1] - current[1]
|
|
2975
|
+
const length = Math.sqrt(dx * dx + dy * dy)
|
|
2976
|
+
|
|
2977
|
+
// 计算法向量
|
|
2978
|
+
const normalX = (-dy / length) * buffer
|
|
2979
|
+
const normalY = (dx / length) * buffer
|
|
2980
|
+
|
|
2981
|
+
// 添加缓冲点
|
|
2982
|
+
bufferPoints.push([current[0] + normalX, current[1] + normalY])
|
|
2983
|
+
|
|
2984
|
+
// 在角点添加圆弧
|
|
2985
|
+
if (i < ring.length - 2) {
|
|
2986
|
+
const nextDx = ring[i + 2][0] - next[0]
|
|
2987
|
+
const nextDy = ring[i + 2][1] - next[1]
|
|
2988
|
+
const nextLength = Math.sqrt(nextDx * nextDx + nextDy * nextDy)
|
|
2989
|
+
const nextNormalX = (-nextDy / nextLength) * buffer
|
|
2990
|
+
const nextNormalY = (nextDx / nextLength) * buffer
|
|
2991
|
+
|
|
2992
|
+
// 添加圆弧点
|
|
2993
|
+
for (let j = 1; j < segments; j++) {
|
|
2994
|
+
const t = j / segments
|
|
2995
|
+
const x = next[0] + normalX * (1 - t) + nextNormalX * t
|
|
2996
|
+
const y = next[1] + normalY * (1 - t) + nextNormalY * t
|
|
2997
|
+
bufferPoints.push([x, y])
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
// 闭合多边形
|
|
3003
|
+
bufferPoints.push(bufferPoints[0])
|
|
3004
|
+
|
|
3005
|
+
return bufferPoints
|
|
3006
|
+
},
|
|
3007
|
+
|
|
3008
|
+
// 加载轨迹回放 trackData轨迹数据 obj 轨迹回放配置 frustumOption视椎体配置
|
|
3009
|
+
loadTrackPlayBackData (trackData, obj = {}, frustumOption) {
|
|
3010
|
+
const sampledPosition = new Cesium.SampledPositionProperty() //位置采样
|
|
3011
|
+
const sampledHeading = new Cesium.SampledProperty(Number) //偏航角采样
|
|
3012
|
+
const sampledPitch = new Cesium.SampledProperty(Number) //俯仰角
|
|
3013
|
+
const startTime = new Date(trackData[0].receiveTime)
|
|
3014
|
+
const endTime = new Date(trackData[trackData.length - 1].receiveTime)
|
|
3015
|
+
const start = Cesium.JulianDate.fromDate(startTime)
|
|
3016
|
+
const stop = Cesium.JulianDate.fromDate(endTime)
|
|
3017
|
+
|
|
3018
|
+
this.viewer.clock.startTime = start.clone()
|
|
3019
|
+
this.viewer.clock.stopTime = stop.clone()
|
|
3020
|
+
this.viewer.clock.currentTime = start.clone()
|
|
3021
|
+
this.viewer.clock.multiplier = obj.speed || 5
|
|
3022
|
+
this.viewer.clock.shouldAnimate = true
|
|
3023
|
+
this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED
|
|
3024
|
+
this.viewer.timeline.zoomTo(start, stop)
|
|
3025
|
+
|
|
3026
|
+
// 创建自定义姿态属性,处理垂直运动时的姿态计算
|
|
3027
|
+
const orientationProperty = new Cesium.CallbackProperty((time) => {
|
|
3028
|
+
const position = sampledPosition.getValue(time)
|
|
3029
|
+
if (!position) return
|
|
3030
|
+
|
|
3031
|
+
// 获取当前和前一个时间点的位置来计算速度
|
|
3032
|
+
const currentTime = Cesium.JulianDate.toDate(time)
|
|
3033
|
+
const prevTime = new Date(currentTime.getTime() - 1000) // 1秒前
|
|
3034
|
+
const prevJulianTime = Cesium.JulianDate.fromDate(prevTime)
|
|
3035
|
+
const prevPosition = sampledPosition.getValue(prevJulianTime)
|
|
3036
|
+
|
|
3037
|
+
if (!prevPosition) {
|
|
3038
|
+
// 如果没有前一个位置,使用正北方向
|
|
3039
|
+
return Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(0, 0, 0))
|
|
3040
|
+
}
|
|
3041
|
+
// 计算偏航角
|
|
3042
|
+
let prev = [Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(prevPosition).longitude), Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(prevPosition).latitude)]
|
|
3043
|
+
let cur = [Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(position).longitude), Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(position).latitude)]
|
|
3044
|
+
let heading = Math.round(turf.bearing(prev, cur))
|
|
3045
|
+
|
|
3046
|
+
// 计算速度向量
|
|
3047
|
+
const velocity = Cesium.Cartesian3.subtract(position, prevPosition, new Cesium.Cartesian3())
|
|
3048
|
+
const velocityMagnitude = Cesium.Cartesian3.magnitude(velocity)
|
|
3049
|
+
|
|
3050
|
+
// 计算水平速度和垂直速度
|
|
3051
|
+
const up = Cesium.Cartesian3.normalize(position, new Cesium.Cartesian3())
|
|
3052
|
+
const dotProduct = Cesium.Cartesian3.dot(velocity, up)
|
|
3053
|
+
const verticalComponent = Cesium.Cartesian3.multiplyByScalar(up, dotProduct, new Cesium.Cartesian3())
|
|
3054
|
+
const velocityHorizontal = Cesium.Cartesian3.subtract(velocity, verticalComponent, new Cesium.Cartesian3())
|
|
3055
|
+
const horizontalMagnitude = Cesium.Cartesian3.magnitude(velocityHorizontal)
|
|
3056
|
+
|
|
3057
|
+
// 垂直运动,偏航角设为0(正北)
|
|
3058
|
+
if (horizontalMagnitude < velocityMagnitude * 0.1) {
|
|
3059
|
+
return Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(0, 0, 0))
|
|
3060
|
+
} else {
|
|
3061
|
+
return Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(heading), 0, 0))
|
|
3062
|
+
}
|
|
3063
|
+
}, false)
|
|
3064
|
+
trackData.forEach(point => {
|
|
3065
|
+
const time = Cesium.JulianDate.fromDate(new Date(point.receiveTime))
|
|
3066
|
+
let position = Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.height)
|
|
3067
|
+
sampledPosition.addSample(time, position)
|
|
3068
|
+
sampledHeading.addSample(time, point.gimbalYawDegree)
|
|
3069
|
+
sampledPitch.addSample(time, point.gimbalPitchDegree)
|
|
3070
|
+
})
|
|
3071
|
+
// 创建无人机实体
|
|
3072
|
+
this.droneEntity = this.viewer.entities.add({
|
|
3073
|
+
name: 'uav',
|
|
3074
|
+
position: sampledPosition,
|
|
3075
|
+
orientation: orientationProperty,
|
|
3076
|
+
model: {
|
|
3077
|
+
uri: '/dajiang.glb',
|
|
3078
|
+
minimumPixelSize: 78,
|
|
3079
|
+
maximumScale: 20000,
|
|
3080
|
+
scale: 0.5,
|
|
3081
|
+
color: Cesium.Color.WHITE.withAlpha(1)
|
|
3082
|
+
},
|
|
3083
|
+
path: new Cesium.PathGraphics({
|
|
3084
|
+
material: Cesium.Color.fromCssColorString('#16f8a7'),
|
|
3085
|
+
width: 3,
|
|
3086
|
+
leadTime: 0,
|
|
3087
|
+
trailTime: Infinity,
|
|
3088
|
+
})
|
|
3089
|
+
})
|
|
3090
|
+
const range = 150
|
|
3091
|
+
this.droneEntity.viewFrom = new Cesium.Cartesian3(0, -range, range)
|
|
3092
|
+
this.viewer.trackedEntity = this.droneEntity
|
|
3093
|
+
this.viewer.clock.onTick.addEventListener(() => {
|
|
3094
|
+
let { currentTime } = this.viewer.clock
|
|
3095
|
+
// 回放完成
|
|
3096
|
+
if (Cesium.JulianDate.greaterThan(currentTime, stop)) return
|
|
3097
|
+
if (frustumOption) {
|
|
3098
|
+
const position = this.droneEntity.position.getValue(currentTime)
|
|
3099
|
+
const cartographic = Cesium.Cartographic.fromCartesian(position)
|
|
3100
|
+
const longitude = Cesium.Math.toDegrees(cartographic.longitude)
|
|
3101
|
+
const latitude = Cesium.Math.toDegrees(cartographic.latitude)
|
|
3102
|
+
const height = cartographic.height
|
|
3103
|
+
let option = {
|
|
3104
|
+
...frustumOption,
|
|
3105
|
+
longitude,
|
|
3106
|
+
latitude,
|
|
3107
|
+
height,
|
|
3108
|
+
heading: sampledHeading.getValue(currentTime),
|
|
3109
|
+
pitch: sampledPitch.getValue(currentTime)
|
|
3110
|
+
}
|
|
3111
|
+
this.addFrustum(option)
|
|
3112
|
+
}
|
|
3113
|
+
})
|
|
3114
|
+
},
|
|
3115
|
+
clearTrackPlayback () {
|
|
3116
|
+
this.clearFrustum()
|
|
3117
|
+
this.viewer.trackedEntity = null
|
|
3118
|
+
this.viewer.entities.remove(this.droneEntity)
|
|
3119
|
+
},
|
|
3120
|
+
// 设置时钟当前时间
|
|
3121
|
+
setClockTime (time) {
|
|
3122
|
+
const currentTime = Cesium.JulianDate.fromDate(new Date(time))
|
|
3123
|
+
this.viewer.clock.currentTime = currentTime
|
|
3124
|
+
},
|
|
3125
|
+
// 开启时钟
|
|
3126
|
+
startClock () {
|
|
3127
|
+
this.viewer.clock.shouldAnimate = true
|
|
3128
|
+
this.viewer.trackedEntity = this.droneEntity
|
|
3129
|
+
},
|
|
3130
|
+
// 暂停时钟
|
|
3131
|
+
stopClock () {
|
|
3132
|
+
this.viewer.clock.shouldAnimate = false
|
|
3133
|
+
this.viewer.trackedEntity = null
|
|
3134
|
+
},
|
|
3135
|
+
async loadGeoJson (geojson) {
|
|
3136
|
+
if (this.geoDataSource) this.viewer.dataSources.remove(this.geoDataSource)
|
|
3137
|
+
this.geoDataSource = await Cesium.GeoJsonDataSource.load(geojson, {
|
|
3138
|
+
stroke: Cesium.Color.BLUE,
|
|
3139
|
+
strokeWidth: 2,
|
|
3140
|
+
fill: Cesium.Color.RED.withAlpha(0.5),
|
|
3141
|
+
clampToGround: true
|
|
3142
|
+
})
|
|
3143
|
+
this.viewer.dataSources.add(this.geoDataSource)
|
|
3144
|
+
},
|
|
3145
|
+
// 绘制视椎体
|
|
3146
|
+
addFrustum ({ longitude, latitude, height, heading, pitch, roll = 0, fov, near = 0.001, far = 150, aspectRatio }) {
|
|
3147
|
+
let position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
|
|
3148
|
+
// 转换角度
|
|
3149
|
+
let radYaw = Cesium.Math.toRadians(90 + heading)
|
|
3150
|
+
let radPitch = -Cesium.Math.toRadians(pitch)
|
|
3151
|
+
let radRoll = Cesium.Math.toRadians(roll)
|
|
3152
|
+
let hpr = new Cesium.HeadingPitchRoll(radYaw, radPitch, radRoll)
|
|
3153
|
+
const hprq = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
|
|
3154
|
+
// 创建修正旋转:绕 Y 轴 (北) 旋转 -90度 (-π/2),将默认前向从 -Z(下) 转向 +Y(北)
|
|
3155
|
+
const correctionQuaternion = Cesium.Quaternion.fromAxisAngle(
|
|
3156
|
+
Cesium.Cartesian3.UNIT_Y, // Y轴 (北)
|
|
3157
|
+
-Cesium.Math.PI_OVER_TWO // -90 degrees
|
|
3158
|
+
)
|
|
3159
|
+
let orientation = Cesium.Quaternion.multiply(hprq, correctionQuaternion, new Cesium.Quaternion())
|
|
3160
|
+
if (this.frustumPrimitive) this.clearFrustum()
|
|
3161
|
+
let frustum = new Cesium.PerspectiveFrustum({
|
|
3162
|
+
fov: Cesium.Math.toRadians(fov),
|
|
3163
|
+
// 视锥体的宽度/高度
|
|
3164
|
+
aspectRatio: 1 / aspectRatio,
|
|
3165
|
+
// 近面距视点的距离
|
|
3166
|
+
near,
|
|
3167
|
+
// 远面距视点的距离
|
|
3168
|
+
far,
|
|
3169
|
+
})
|
|
3170
|
+
|
|
3171
|
+
let frustumGeo = new Cesium.GeometryInstance({
|
|
3172
|
+
geometry: new Cesium.FrustumGeometry({
|
|
3173
|
+
frustum: frustum,
|
|
3174
|
+
origin: position,
|
|
3175
|
+
orientation: orientation,
|
|
3176
|
+
vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
|
|
3177
|
+
}),
|
|
3178
|
+
attributes: {
|
|
3179
|
+
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
|
3180
|
+
new Cesium.Color(0.0, 1.0, 0.0, 0.2)
|
|
3181
|
+
),
|
|
3182
|
+
},
|
|
3183
|
+
})
|
|
3184
|
+
let frustumLineGeo = new Cesium.GeometryInstance({
|
|
3185
|
+
geometry: new Cesium.FrustumOutlineGeometry({
|
|
3186
|
+
frustum: frustum,
|
|
3187
|
+
origin: position,
|
|
3188
|
+
orientation: orientation,
|
|
3189
|
+
}),
|
|
3190
|
+
attributes: {
|
|
3191
|
+
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
|
3192
|
+
new Cesium.Color(0.0, 1.0, 0.0, 0.5)
|
|
3193
|
+
),
|
|
3194
|
+
},
|
|
3195
|
+
})
|
|
3196
|
+
|
|
3197
|
+
const frustumPrimitive = new Cesium.Primitive({
|
|
3198
|
+
geometryInstances: [frustumGeo],
|
|
3199
|
+
appearance: new Cesium.PerInstanceColorAppearance({
|
|
3200
|
+
closed: true,
|
|
3201
|
+
flat: true,
|
|
3202
|
+
}),
|
|
3203
|
+
asynchronous: false,
|
|
3204
|
+
})
|
|
3205
|
+
|
|
3206
|
+
const frustumLinPrimitive = new Cesium.Primitive({
|
|
3207
|
+
geometryInstances: [frustumLineGeo],
|
|
3208
|
+
appearance: new Cesium.PerInstanceColorAppearance({
|
|
3209
|
+
closed: true,
|
|
3210
|
+
flat: true,
|
|
3211
|
+
}),
|
|
3212
|
+
asynchronous: false,
|
|
3213
|
+
})
|
|
3214
|
+
this.frustumPrimitive = this.viewer.scene.primitives.add(frustumPrimitive)
|
|
3215
|
+
this.frustumLinPrimitive = this.viewer.scene.primitives.add(frustumLinPrimitive)
|
|
3216
|
+
},
|
|
3217
|
+
// 清除视椎体
|
|
3218
|
+
clearFrustum () {
|
|
3219
|
+
this.viewer.scene.primitives.remove(this.frustumPrimitive)
|
|
3220
|
+
this.viewer.scene.primitives.remove(this.frustumLinPrimitive)
|
|
3221
|
+
},
|
|
3222
|
+
// 修改实体位置
|
|
3223
|
+
updatePosition (heading, key, step) {
|
|
3224
|
+
const position = this.selectedEntity.position.getValue(Cesium.JulianDate.now())
|
|
3225
|
+
const hpr = new Cesium.HeadingPitchRoll(
|
|
3226
|
+
Cesium.Math.toRadians(heading),
|
|
3227
|
+
Cesium.Math.toRadians(0),
|
|
3228
|
+
Cesium.Math.toRadians(0)
|
|
3229
|
+
)
|
|
3230
|
+
const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
|
|
3231
|
+
// 从四元数转换为旋转矩阵
|
|
3232
|
+
const rotationMatrix = Cesium.Matrix3.fromQuaternion(orientation)
|
|
3233
|
+
const forward = Cesium.Matrix3.getColumn(rotationMatrix, 0, new Cesium.Cartesian3()) // +X 前
|
|
3234
|
+
const right = Cesium.Matrix3.getColumn(rotationMatrix, 1, new Cesium.Cartesian3()) // +Y 右
|
|
3235
|
+
const up = Cesium.Matrix3.getColumn(rotationMatrix, 2, new Cesium.Cartesian3()) // +Z 上
|
|
3236
|
+
let newPosition
|
|
3237
|
+
switch (key) {
|
|
3238
|
+
case 'W':
|
|
3239
|
+
newPosition = Cesium.Cartesian3.add(
|
|
3240
|
+
position,
|
|
3241
|
+
Cesium.Cartesian3.multiplyByScalar(right, step, new Cesium.Cartesian3()),
|
|
3242
|
+
new Cesium.Cartesian3()
|
|
3243
|
+
)
|
|
3244
|
+
break
|
|
3245
|
+
case 'A':
|
|
3246
|
+
newPosition = Cesium.Cartesian3.add(
|
|
3247
|
+
position,
|
|
3248
|
+
Cesium.Cartesian3.multiplyByScalar(forward, -step, new Cesium.Cartesian3()),
|
|
3249
|
+
new Cesium.Cartesian3()
|
|
3250
|
+
)
|
|
3251
|
+
break
|
|
3252
|
+
case 'S':
|
|
3253
|
+
newPosition = Cesium.Cartesian3.add(
|
|
3254
|
+
position,
|
|
3255
|
+
Cesium.Cartesian3.multiplyByScalar(right, -step, new Cesium.Cartesian3()),
|
|
3256
|
+
new Cesium.Cartesian3()
|
|
3257
|
+
)
|
|
3258
|
+
break
|
|
3259
|
+
case 'D':
|
|
3260
|
+
newPosition = Cesium.Cartesian3.add(
|
|
3261
|
+
position,
|
|
3262
|
+
Cesium.Cartesian3.multiplyByScalar(forward, step, new Cesium.Cartesian3()),
|
|
3263
|
+
new Cesium.Cartesian3()
|
|
3264
|
+
)
|
|
3265
|
+
break
|
|
3266
|
+
case 'C':
|
|
3267
|
+
newPosition = Cesium.Cartesian3.add(
|
|
3268
|
+
position,
|
|
3269
|
+
Cesium.Cartesian3.multiplyByScalar(up, step, new Cesium.Cartesian3()),
|
|
3270
|
+
new Cesium.Cartesian3()
|
|
3271
|
+
)
|
|
3272
|
+
break
|
|
3273
|
+
case 'Z':
|
|
3274
|
+
newPosition = Cesium.Cartesian3.add(
|
|
3275
|
+
position,
|
|
3276
|
+
Cesium.Cartesian3.multiplyByScalar(up, -step, new Cesium.Cartesian3()),
|
|
3277
|
+
new Cesium.Cartesian3()
|
|
3278
|
+
)
|
|
3279
|
+
break
|
|
3280
|
+
}
|
|
3281
|
+
this.selectedEntity.position.setValue(newPosition)
|
|
3282
|
+
const cartographic = Cesium.Cartographic.fromCartesian(newPosition)
|
|
3283
|
+
// 获取经纬度和高度
|
|
3284
|
+
const longitude = Cesium.Math.toDegrees(cartographic.longitude)
|
|
3285
|
+
const latitude = Cesium.Math.toDegrees(cartographic.latitude)
|
|
3286
|
+
const terrainHeight = this.viewer.scene.globe.getHeight(cartographic)
|
|
3287
|
+
const height = cartographic.height
|
|
3288
|
+
return {
|
|
3289
|
+
longitude, latitude, height, terrainHeight
|
|
3290
|
+
}
|
|
3291
|
+
},
|
|
3292
|
+
|
|
3293
|
+
drawBtnClick(index) {
|
|
3294
|
+
this.btnSelectIndex = index;
|
|
3295
|
+
this.mangAddLayer({ state: 'add' });
|
|
3296
|
+
},
|
|
3297
|
+
//管理后台绘制
|
|
3298
|
+
mangAddLayer(obj) {
|
|
3299
|
+
// this.change3D();
|
|
3300
|
+
const _this = this;
|
|
3301
|
+
this.initDrawBtn();
|
|
3302
|
+
this.dataState = obj.state;
|
|
3303
|
+
if (obj.state == "add") {
|
|
3304
|
+
// this.dataState = 'add';
|
|
3305
|
+
let style = {};
|
|
3306
|
+
style.name = this.mangDrawLayerName;
|
|
3307
|
+
style.type = this.mangDrawType;
|
|
3308
|
+
style.single = true;
|
|
3309
|
+
style.style = {};
|
|
3310
|
+
if (this.mangDrawType == 'LineString' || this.mangDrawType == 'Polygon') {
|
|
3311
|
+
style.style.stroke = {
|
|
3312
|
+
width: 2,
|
|
3313
|
+
color: "#03A2FF",
|
|
3314
|
+
};
|
|
3315
|
+
style.style.point = {
|
|
3316
|
+
color: "#03A2FF",
|
|
3317
|
+
outlineColor: "white"
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
if (this.mangDrawType == 'Polygon') {
|
|
3321
|
+
style.style.fill = {
|
|
3322
|
+
color: "rgba(232, 232, 232,0.2)"
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
this.drawLayer(style, (params, value) => {
|
|
3327
|
+
if(this.mangDrawType == 'Point'){
|
|
3328
|
+
params.wktValue = params.coordinate;
|
|
3329
|
+
}
|
|
3330
|
+
//将拿到的坐标数据传给管理后台
|
|
3331
|
+
this.$emit("mapCallback", params);
|
|
3332
|
+
//对经纬度赋值
|
|
3333
|
+
this.setPositioningData(params, value);
|
|
3334
|
+
[{ boundaryLine: params.coordinate }]
|
|
3335
|
+
if (this.mangDrawType == 'Point') {
|
|
3336
|
+
let obj = [{ boundaryLine: params.coordinate }];
|
|
3337
|
+
this.mangDefaultLayer(obj);
|
|
3338
|
+
}
|
|
3339
|
+
});
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
let list = [{ boundaryLine: obj.wktString }];
|
|
3344
|
+
if(list){
|
|
3345
|
+
this.mangDefaultLayer(list);
|
|
3346
|
+
//计算绘制图层的四至范围
|
|
3347
|
+
if(this.mangDrawType == 'Point'){
|
|
3348
|
+
let point = this.formatWktToPoint(obj.wktString)
|
|
3349
|
+
this.positioningData.longitude = point[0];
|
|
3350
|
+
this.positioningData.latitude = point[1];
|
|
3351
|
+
}else{
|
|
3352
|
+
let bounds = this.extentFromWkt(obj.wktString);
|
|
3353
|
+
this.positioningData.longitude = bounds.west;
|
|
3354
|
+
this.positioningData.longitudeEnd = bounds.east;
|
|
3355
|
+
this.positioningData.latitude = bounds.south;
|
|
3356
|
+
this.positioningData.latitudeEnd = bounds.north;
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
},
|
|
3360
|
+
|
|
3361
|
+
//绘制管理后台默认的图层
|
|
3362
|
+
async mangDefaultLayer(params) {
|
|
3363
|
+
//先清理其他所有点线面实体
|
|
3364
|
+
let pbj = {
|
|
3365
|
+
name: this.mangDrawLayerName,
|
|
3366
|
+
type: this.mangDrawLayerName,
|
|
3367
|
+
clampToGround:true,//贴地
|
|
3368
|
+
single:true,//单一绘制
|
|
3369
|
+
getData: (type, active) => {
|
|
3370
|
+
this.changeMap(type, active);
|
|
3371
|
+
},
|
|
3372
|
+
list: params,
|
|
3373
|
+
coordField: "boundaryLine",
|
|
3374
|
+
coordsStyle: {
|
|
3375
|
+
image: {
|
|
3376
|
+
src: require("./imgs/icon-position-blue.png"),
|
|
3377
|
+
scale: 1,
|
|
3378
|
+
anchor: [0.5, 1],
|
|
3379
|
+
cesiumOffsetX: 0,
|
|
3380
|
+
cesiumOffsetY: 10,
|
|
3381
|
+
},
|
|
3382
|
+
stroke:{
|
|
3383
|
+
width: 2,
|
|
3384
|
+
color: "#03A2FF",
|
|
3385
|
+
},
|
|
3386
|
+
fill : {
|
|
3387
|
+
width:5,
|
|
3388
|
+
color: "rgba(232, 232, 232,0.2)"
|
|
3389
|
+
}
|
|
3390
|
+
},
|
|
3391
|
+
active: true,
|
|
3392
|
+
};
|
|
3393
|
+
await this.addWktLayer(pbj);
|
|
3394
|
+
//添加到drawLayerObj中,drawLayerObj是用来存储绘制的图层对象的
|
|
3395
|
+
//this.viewer.dataSources.getByName返回的是数组,即使只有一个图层,也是数组,所以需要取[0]
|
|
3396
|
+
this.drawLayerObj[this.mangDrawLayerName] = this.viewer.dataSources.getByName(this.mangDrawLayerName)[0]
|
|
3397
|
+
console.log(this.drawLayerObj[this.mangDrawLayerName],'drawLayerObj',this.viewer)
|
|
3398
|
+
},
|
|
3399
|
+
// 处理经纬度精度
|
|
3400
|
+
handleCoordinateLength(data) {
|
|
3401
|
+
if (data) {
|
|
3402
|
+
return data.toFixed(8);
|
|
3403
|
+
}
|
|
3404
|
+
},
|
|
3405
|
+
//给管理后台的经纬度赋值
|
|
3406
|
+
setPositioningData(params, Entity) {
|
|
3407
|
+
console.log(params,'params',this.viewer)
|
|
3408
|
+
if (this.dratTypeArr.includes("Point")) {
|
|
3409
|
+
this.positioningData.longitude = params.coords[0];
|
|
3410
|
+
this.positioningData.latitude = params.coords[1];
|
|
3411
|
+
} else {
|
|
3412
|
+
//拿到实体的所有点,以此来算出四至
|
|
3413
|
+
let position;
|
|
3414
|
+
if (this.dratTypeArr.includes("LineString")) {
|
|
3415
|
+
position = Entity[0].polyline.positions.getValue(Cesium.JulianDate.now());
|
|
3416
|
+
} else {
|
|
3417
|
+
let en = params.entity?params.entity:this.viewer.dataSources.getByName(this.mangDrawLayerName)[0].entities.values[0]
|
|
3418
|
+
position = en.polyline.positions.getValue(Cesium.JulianDate.now());
|
|
3419
|
+
}
|
|
3420
|
+
const rectangle = Cesium.Rectangle.fromCartesianArray(position);
|
|
3421
|
+
// 获取四至信息
|
|
3422
|
+
const bounds = {
|
|
3423
|
+
west: Cesium.Math.toDegrees(rectangle.west), // 最西经度
|
|
3424
|
+
east: Cesium.Math.toDegrees(rectangle.east), // 最东经度
|
|
3425
|
+
south: Cesium.Math.toDegrees(rectangle.south), // 最南纬度
|
|
3426
|
+
north: Cesium.Math.toDegrees(rectangle.north) // 最北纬度
|
|
3427
|
+
};
|
|
3428
|
+
this.positioningData.longitude = bounds.west;
|
|
3429
|
+
this.positioningData.longitudeEnd = bounds.east;
|
|
3430
|
+
this.positioningData.latitude = bounds.south;
|
|
3431
|
+
this.positioningData.latitudeEnd = bounds.north;
|
|
3432
|
+
}
|
|
3433
|
+
},
|
|
3434
|
+
//删除管理后台当前绘制的图层
|
|
3435
|
+
deleteMangLayerClick() {
|
|
3436
|
+
this.positioningData = {
|
|
3437
|
+
// 坐标采集的经纬度
|
|
3438
|
+
longitude: undefined,
|
|
3439
|
+
latitude: undefined,
|
|
3440
|
+
longitudeEnd: undefined,
|
|
3441
|
+
latitudeEnd: undefined,
|
|
3442
|
+
};
|
|
3443
|
+
let obj;
|
|
3444
|
+
if (this.btnSelectIndex == 3) {
|
|
3445
|
+
this.btnSelectIndex = -1;
|
|
3446
|
+
return;
|
|
3447
|
+
} else {
|
|
3448
|
+
this.btnSelectIndex = 3;
|
|
3449
|
+
obj = this.drawLayerObj[this.mangDrawLayerName]
|
|
3450
|
+
if(obj) this.clearDrawLayer(obj)
|
|
3451
|
+
let dataSource = this.viewer.dataSources.getByName(this.mangDrawLayerName)
|
|
3452
|
+
if(dataSource && dataSource.length > 0){
|
|
3453
|
+
dataSource[0].entities.removeAll();
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
},
|
|
3457
|
+
|
|
3458
|
+
// 初始化绘制按钮状态
|
|
3459
|
+
initDrawBtn() {
|
|
3460
|
+
if (this.dataState != "details") {
|
|
3461
|
+
if (this.dratTypeArr.includes("Point")) {
|
|
3462
|
+
this.btnSelectIndex = 0;
|
|
3463
|
+
}
|
|
3464
|
+
if (this.dratTypeArr.includes("LineString")) {
|
|
3465
|
+
this.btnSelectIndex = 1;
|
|
3466
|
+
}
|
|
3467
|
+
if (this.dratTypeArr.includes("Polygon")) {
|
|
3468
|
+
this.btnSelectIndex = 2;
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
},
|
|
3472
|
+
|
|
3473
|
+
//修改管理后台点图层的坐标,只修改管理后台的点实体
|
|
3474
|
+
async updateMapBtn(){
|
|
3475
|
+
const { longitude: lon, latitude: lat } = this.positioningData || {};
|
|
3476
|
+
if (lon == null || lat == null) return;
|
|
3477
|
+
|
|
3478
|
+
const pointWkt = `POINT(${lon} ${lat})`;
|
|
3479
|
+
|
|
3480
|
+
let cart; // 目标笛卡尔坐标
|
|
3481
|
+
try {
|
|
3482
|
+
const positions = await this.getTerrainHeight([[lon, lat]]);
|
|
3483
|
+
console.log(positions,'positions')
|
|
3484
|
+
const p0 = positions?.[0];
|
|
3485
|
+
if (p0) {
|
|
3486
|
+
cart = Cesium.Cartesian3.fromRadians(p0.longitude, p0.latitude, p0.height ?? 0);
|
|
3487
|
+
}
|
|
3488
|
+
} catch (e) {
|
|
3489
|
+
// 获取地形高程失败时不更新位置,但照常发事件与渲染
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
const layer = this.drawLayerObj?.[this.mangDrawLayerName];
|
|
3493
|
+
const entity = layer?.entities?.values?.[0];
|
|
3494
|
+
|
|
3495
|
+
if (entity && cart) {
|
|
3496
|
+
if (entity.position && typeof entity.position.setValue === 'function') {
|
|
3497
|
+
entity.position.setValue(cart);
|
|
3498
|
+
} else {
|
|
3499
|
+
entity.position = cart;
|
|
3500
|
+
}
|
|
3501
|
+
} else if (!entity) {
|
|
3502
|
+
this.mangDefaultLayer([{ boundaryLine: pointWkt }]);
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
this.$emit('mapCallback', {wktValue:pointWkt});
|
|
3506
|
+
this.viewer?.scene?.requestRender?.();
|
|
3507
|
+
},
|
|
3508
|
+
//根据wkt格式数据计算四至范围
|
|
3509
|
+
extentFromWkt(wkt) {
|
|
3510
|
+
if (!wkt || typeof wkt !== "string") return null;
|
|
3511
|
+
const clean = wkt.trim().replace(/^SRID=\d+;/i, "");
|
|
3512
|
+
let geom;
|
|
3513
|
+
try {
|
|
3514
|
+
geom = WKT.parse(clean); // 得到 GeoJSON Geometry 或 GeometryCollection
|
|
3515
|
+
} catch (e) {
|
|
3516
|
+
console.error("WKT 解析失败:", e);
|
|
3517
|
+
return null;
|
|
3518
|
+
}
|
|
3519
|
+
// 扁平化提取所有 [x,y,(z)] 坐标
|
|
3520
|
+
const flattenCoords = (input, out) => {
|
|
3521
|
+
if (!input) return;
|
|
3522
|
+
if (Array.isArray(input)) {
|
|
3523
|
+
if (typeof input[0] === "number") {
|
|
3524
|
+
out.push(input); // [x,y,(z)]
|
|
3525
|
+
} else {
|
|
3526
|
+
input.forEach(i => flattenCoords(i, out));
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
};
|
|
3530
|
+
const coords = [];
|
|
3531
|
+
const collect = (g) => {
|
|
3532
|
+
if (!g) return;
|
|
3533
|
+
if (g.type === "GeometryCollection") {
|
|
3534
|
+
(g.geometries || []).forEach(collect);
|
|
3535
|
+
} else {
|
|
3536
|
+
flattenCoords(g.coordinates, coords);
|
|
3537
|
+
}
|
|
3538
|
+
};
|
|
3539
|
+
collect(geom);
|
|
3540
|
+
if (!coords.length) return null;
|
|
3541
|
+
let west = Infinity, south = Infinity, east = -Infinity, north = -Infinity;
|
|
3542
|
+
for (const c of coords) {
|
|
3543
|
+
const x = c[0]; // lon
|
|
3544
|
+
const y = c[1]; // lat
|
|
3545
|
+
if (x < west) west = x;
|
|
3546
|
+
if (y < south) south = y;
|
|
3547
|
+
if (x > east) east = x;
|
|
3548
|
+
if (y > north) north = y;
|
|
3549
|
+
}
|
|
3550
|
+
return { west, south, east, north, width: east - west, height: north - south };
|
|
3551
|
+
},
|
|
3552
|
+
// 转化格式,如:POINT(114.191176383988 26.527678043101847),转化为:['114.191176383988', '26.527678043101847']
|
|
3553
|
+
formatWktToPoint: function (wkt) {
|
|
3554
|
+
if (!wkt) return [];
|
|
3555
|
+
if (wkt.includes("POINT")) {
|
|
3556
|
+
const modifiedString = wkt
|
|
3557
|
+
.replace("POINT", "")
|
|
3558
|
+
.slice(0, -1)
|
|
3559
|
+
.split("(")[1];
|
|
3560
|
+
const specialArray = modifiedString.split(" ");
|
|
3561
|
+
return specialArray;
|
|
3562
|
+
} else {
|
|
3563
|
+
return [];
|
|
3564
|
+
}
|
|
3565
|
+
},
|
|
3566
|
+
//转化格式,将度分秒格式的经纬度坐标[117° 47' 16.85",26° 53' 4.84"]转换为角格式['114.191176383988', '26.527678043101847']
|
|
3567
|
+
dmsToDegrees(dmsStr) {
|
|
3568
|
+
// 判断是否已经是角度格式(纯数字或可转换为数字的字符串)
|
|
3569
|
+
if (typeof dmsStr === 'number') {
|
|
3570
|
+
return dmsStr;
|
|
3571
|
+
}
|
|
3572
|
+
if (typeof dmsStr === 'string') {
|
|
3573
|
+
// 移除了不必要的空格并检查是否为有效的数字字符串
|
|
3574
|
+
const trimmedStr = dmsStr.trim();
|
|
3575
|
+
if (!isNaN(parseFloat(trimmedStr)) && isFinite(trimmedStr)) {
|
|
3576
|
+
return parseFloat(trimmedStr);
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3580
|
+
// 正则匹配度分秒(支持°、′、″符号或d、m、s,支持空格分隔)
|
|
3581
|
+
const regex = /(\d+)\s*[°d]\s*(\d+)\s*['′m]\s*(\d+\.?\d*)\s*["″s]?\s*([EWNS])?/i;
|
|
3582
|
+
const match = dmsStr.match(regex);
|
|
3583
|
+
|
|
3584
|
+
if (!match) {
|
|
3585
|
+
// 如果正则不匹配,但仍然可以解析为数字,则返回该数字,以增加容错性
|
|
3586
|
+
const parsed = parseFloat(dmsStr);
|
|
3587
|
+
if (!isNaN(parsed) && isFinite(parsed)) {
|
|
3588
|
+
return parsed;
|
|
3589
|
+
}
|
|
3590
|
+
console.error(`无效的度分秒格式或十进制度格式:${dmsStr}`);
|
|
3591
|
+
return 0; // 或者返回 null, NaN 等表示无效
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
// 提取度、分、秒和方向
|
|
3595
|
+
const degrees = parseInt(match[1], 10);
|
|
3596
|
+
const minutes = parseInt(match[2], 10);
|
|
3597
|
+
const seconds = parseFloat(match[3]);
|
|
3598
|
+
const direction = match[4]?.toUpperCase();
|
|
3599
|
+
|
|
3600
|
+
// 计算十进制度
|
|
3601
|
+
let decimal = degrees + minutes / 60 + seconds / 3600;
|
|
3602
|
+
|
|
3603
|
+
// 根据方向调整正负(西经W和南纬S为负)
|
|
3604
|
+
if (direction === 'W' || direction === 'S') {
|
|
3605
|
+
decimal = -decimal;
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3608
|
+
return decimal;
|
|
3609
|
+
},
|
|
3610
|
+
},
|
|
3611
|
+
}
|
|
3612
|
+
</script>
|
|
3613
|
+
|
|
3614
|
+
<style lang="scss" scoped>
|
|
3615
|
+
$app-list-primary-color: #0052d9;
|
|
3616
|
+
|
|
3617
|
+
.cesium {
|
|
3618
|
+
width: 100%;
|
|
3619
|
+
height: 100%;
|
|
3620
|
+
position: relative;
|
|
3621
|
+
.coordinate-collection {
|
|
3622
|
+
position: absolute;
|
|
3623
|
+
top: 16px;
|
|
3624
|
+
right: 16px;
|
|
3625
|
+
width: 268px;
|
|
3626
|
+
// height: 196px;
|
|
3627
|
+
border-radius: 8px;
|
|
3628
|
+
background-color: #fff;
|
|
3629
|
+
padding: 12px 16px 2px 16px;
|
|
3630
|
+
max-height: 300px;
|
|
3631
|
+
.title {
|
|
3632
|
+
font-size: 16px;
|
|
3633
|
+
color: rgba(0, 0, 0, 0.9);
|
|
3634
|
+
font-weight: 600;
|
|
3635
|
+
}
|
|
3636
|
+
.btn-box {
|
|
3637
|
+
display: flex;
|
|
3638
|
+
margin-top: 12px;
|
|
3639
|
+
.btn-item {
|
|
3640
|
+
width: 32px;
|
|
3641
|
+
height: 32px;
|
|
3642
|
+
background-color: rgba(231, 231, 231, 1);
|
|
3643
|
+
display: flex;
|
|
3644
|
+
align-items: center;
|
|
3645
|
+
justify-content: center;
|
|
3646
|
+
margin-right: 12px;
|
|
3647
|
+
border-radius: 3px;
|
|
3648
|
+
cursor: pointer;
|
|
3649
|
+
}
|
|
3650
|
+
.btn-item-click {
|
|
3651
|
+
background-color: $app-list-primary-color;
|
|
3652
|
+
color: #fff;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
.coordinate-form {
|
|
3656
|
+
margin-top: 12px;
|
|
3657
|
+
::v-deep .el-form-item {
|
|
3658
|
+
margin-bottom: 5px;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
.navigation-container {
|
|
3663
|
+
display: flex;
|
|
3664
|
+
flex-direction: column;
|
|
3665
|
+
align-items: center;
|
|
3666
|
+
gap: 10px;
|
|
3667
|
+
position: absolute;
|
|
3668
|
+
bottom: 60px;
|
|
3669
|
+
right: 30px;
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3672
|
+
.navigation-item {
|
|
3673
|
+
|
|
3674
|
+
.compass-outer {
|
|
3675
|
+
background-image: url('./imgs/orientation.svg');
|
|
3676
|
+
background-position: 50%;
|
|
3677
|
+
background-repeat: no-repeat;
|
|
3678
|
+
background-size: contain;
|
|
3679
|
+
border-radius: 50%;
|
|
3680
|
+
width: 56px;
|
|
3681
|
+
height: 56px;
|
|
3682
|
+
box-shadow: 0 0 8px rgba(0, 0, 0, .5);
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
.compass-inner {
|
|
3686
|
+
cursor: pointer;
|
|
3687
|
+
text-align: center;
|
|
3688
|
+
background: url('./imgs/orientation-bg.svg') 50% / contain no-repeat;
|
|
3689
|
+
flex-direction: column;
|
|
3690
|
+
justify-content: center;
|
|
3691
|
+
align-items: center;
|
|
3692
|
+
width: 42px;
|
|
3693
|
+
height: 42px;
|
|
3694
|
+
font-weight: 700;
|
|
3695
|
+
display: flex;
|
|
3696
|
+
position: absolute;
|
|
3697
|
+
top: 6px;
|
|
3698
|
+
left: 7px;
|
|
3699
|
+
|
|
3700
|
+
.compass-orientation {
|
|
3701
|
+
color: #595959;
|
|
3702
|
+
margin-top: 6px;
|
|
3703
|
+
font-size: 16px;
|
|
3704
|
+
line-height: 18px;
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
.compass-angle {
|
|
3708
|
+
color: #595959;
|
|
3709
|
+
font-size: 12px;
|
|
3710
|
+
line-height: 14px;
|
|
3711
|
+
position: relative;
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
|
|
3716
|
+
.map-action-container {
|
|
3717
|
+
cursor: pointer;
|
|
3718
|
+
background: #fff;
|
|
3719
|
+
color: #4e4e4e;
|
|
3720
|
+
font-weight: 600;
|
|
3721
|
+
justify-content: center;
|
|
3722
|
+
align-items: center;
|
|
3723
|
+
width: 28px;
|
|
3724
|
+
height: 28px;
|
|
3725
|
+
display: flex;
|
|
3726
|
+
box-sizing: border-box;
|
|
3727
|
+
border-radius: 2px;
|
|
3728
|
+
position: relative;
|
|
3729
|
+
box-shadow: 0 0 4px rgba(0, 0, 0, .6);
|
|
3730
|
+
|
|
3731
|
+
&:first-child {
|
|
3732
|
+
border-top-left-radius: 2px;
|
|
3733
|
+
border-top-right-radius: 2px;
|
|
3734
|
+
}
|
|
3735
|
+
|
|
3736
|
+
&:last-child {
|
|
3737
|
+
border-bottom-left-radius: 2px;
|
|
3738
|
+
border-bottom-right-radius: 2px;
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
&:not(:last-child) {
|
|
3742
|
+
border-bottom: 1px solid #dcdee2;
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
.widget-zoom {
|
|
3747
|
+
border-radius: 2px;
|
|
3748
|
+
box-shadow: 0 0 4px rgba(0, 0, 0, .6);
|
|
3749
|
+
font-size: 22px;
|
|
3750
|
+
|
|
3751
|
+
.map-action-container {
|
|
3752
|
+
box-shadow: none;
|
|
3753
|
+
box-sizing: border-box;
|
|
3754
|
+
border-radius: 0;
|
|
3755
|
+
position: relative;
|
|
3756
|
+
|
|
3757
|
+
img {
|
|
3758
|
+
width: 16px;
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3763
|
+
.map-switch {
|
|
3764
|
+
box-sizing: border-box;
|
|
3765
|
+
background: #fff;
|
|
3766
|
+
border-radius: 50%;
|
|
3767
|
+
width: 40px;
|
|
3768
|
+
height: 40px;
|
|
3769
|
+
margin-top: 10px;
|
|
3770
|
+
padding: 3px;
|
|
3771
|
+
position: relative;
|
|
3772
|
+
box-shadow: 0 0 4px rgba(0, 0, 0, .6);
|
|
3773
|
+
|
|
3774
|
+
.img-satellite {
|
|
3775
|
+
cursor: pointer;
|
|
3776
|
+
border-radius: 50%;
|
|
3777
|
+
justify-content: center;
|
|
3778
|
+
align-items: center;
|
|
3779
|
+
width: 34px;
|
|
3780
|
+
height: 34px;
|
|
3781
|
+
display: flex;
|
|
3782
|
+
box-shadow: 0 0 1px #000;
|
|
3783
|
+
background: url('./imgs/map.png') 50% no-repeat;
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3787
|
+
.base-maps {
|
|
3788
|
+
.base-map-tdt {
|
|
3789
|
+
position: relative;
|
|
3790
|
+
cursor: pointer;
|
|
3791
|
+
border: 2px solid #2d8cf0;
|
|
3792
|
+
width: 122px;
|
|
3793
|
+
height: 80px;
|
|
3794
|
+
margin: 2px;
|
|
3795
|
+
background: url('./imgs/map.png') 50% no-repeat;
|
|
3796
|
+
|
|
3797
|
+
.street-selector {
|
|
3798
|
+
background: rgba(0, 0, 0, .5);
|
|
3799
|
+
width: 100%;
|
|
3800
|
+
padding: 0 4px;
|
|
3801
|
+
line-height: 20px;
|
|
3802
|
+
|
|
3803
|
+
::v-deep .el-checkbox__label {
|
|
3804
|
+
color: #fff;
|
|
3805
|
+
font-size: 12px;
|
|
3806
|
+
padding-left: 4px;
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
|
|
3810
|
+
.base-map-txt {
|
|
3811
|
+
height: 20px;
|
|
3812
|
+
line-height: 20px;
|
|
3813
|
+
position: absolute;
|
|
3814
|
+
right: 0;
|
|
3815
|
+
bottom: 0;
|
|
3816
|
+
padding: 0 4px;
|
|
3817
|
+
background-color: #2d8cf0;
|
|
3818
|
+
color: #fff;
|
|
3819
|
+
font-size: 14px;
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
::v-deep .cesium-timeline-bar {
|
|
3826
|
+
.cesium-timeline-ticLabel {
|
|
3827
|
+
display: none;
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
</style>
|