huweili-cesium 1.2.95 → 1.2.97

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.
@@ -8,7 +8,14 @@ import * as Cesium from 'cesium'
8
8
  import { getDroneLabelBatchManager } from './cardPool'
9
9
 
10
10
  const STYLE_ID = 'glb-info-badge-style'
11
- const DEFAULT_LABEL_OFFSET = 4
11
+ const DEFAULT_LABEL_OFFSET = 1
12
+ const DEFAULT_PIXEL_GAP = 2
13
+
14
+ const scratchBoundingSphere = new Cesium.BoundingSphere()
15
+ const scratchEnu = new Cesium.Matrix4()
16
+ const scratchUp = new Cesium.Cartesian3()
17
+ const scratchOffset = new Cesium.Cartesian3()
18
+ const scratchTop = new Cesium.Cartesian3()
12
19
 
13
20
  const ensureGlbInfoBadgeStyle = () => {
14
21
  if (document.getElementById(STYLE_ID)) return
@@ -19,9 +26,10 @@ const ensureGlbInfoBadgeStyle = () => {
19
26
  .glb-info-badge {
20
27
  position: absolute;
21
28
  pointer-events: none;
22
- transform: translateX(-50%);
29
+ transform: translate(-50%, -100%);
23
30
  z-index: 10000;
24
31
  filter: drop-shadow(0 0 8px rgba(0, 229, 255, 0.35));
32
+ will-change: left, top;
25
33
  }
26
34
  .glb-info-badge-inner {
27
35
  position: relative;
@@ -132,24 +140,108 @@ const ensureGlbInfoBadgeStyle = () => {
132
140
  document.head.appendChild(style)
133
141
  }
134
142
 
143
+ /**
144
+ * 读取 Entity 模型 minimumPixelSize(远景会被放大显示,需同步到屏幕偏移)
145
+ * @param {Cesium.Entity} entity
146
+ * @param {Cesium.JulianDate} time
147
+ * @returns {number}
148
+ */
149
+ const getEntityMinimumPixelSize = (entity, time) => {
150
+ const model = entity?.model
151
+ if (!model) return 0
152
+
153
+ const value = typeof model.minimumPixelSize?.getValue === 'function'
154
+ ? model.minimumPixelSize.getValue(time)
155
+ : model.minimumPixelSize
156
+
157
+ return Number.isFinite(value) ? value : 0
158
+ }
159
+
160
+ /**
161
+ * 根据模型包围球估算车顶世界坐标
162
+ * @param {Cesium.Viewer} map
163
+ * @param {Cesium.Entity} entity
164
+ * @param {Cesium.Cartesian3} basePos
165
+ * @param {number} fallbackOffset
166
+ * @returns {Cesium.Cartesian3|null}
167
+ */
168
+ const resolveModelTopWorldPosition = (map, entity, basePos, fallbackOffset) => {
169
+ Cesium.Transforms.eastNorthUpToFixedFrame(basePos, undefined, scratchEnu)
170
+ Cesium.Matrix4.getColumn(scratchEnu, 2, scratchUp)
171
+ Cesium.Cartesian3.normalize(scratchUp, scratchUp)
172
+
173
+ const dataSourceDisplay = map._dataSourceDisplay || map.dataSourceDisplay
174
+ if (entity && dataSourceDisplay?.getBoundingSphere) {
175
+ const ok = dataSourceDisplay.getBoundingSphere(entity, true, scratchBoundingSphere)
176
+ if (ok && scratchBoundingSphere.radius > 0) {
177
+ Cesium.Cartesian3.multiplyByScalar(scratchUp, scratchBoundingSphere.radius, scratchOffset)
178
+ return Cesium.Cartesian3.add(scratchBoundingSphere.center, scratchOffset, scratchTop)
179
+ }
180
+ }
181
+
182
+ Cesium.Cartesian3.multiplyByScalar(scratchUp, fallbackOffset, scratchOffset)
183
+ return Cesium.Cartesian3.add(basePos, scratchOffset, scratchTop)
184
+ }
185
+
186
+ /**
187
+ * 将车顶世界坐标转为屏幕坐标,并兼顾 minimumPixelSize 的可视高度
188
+ * @param {Cesium.Scene} scene
189
+ * @param {Cesium.Cartesian3} basePos
190
+ * @param {Cesium.Cartesian3} topWorldPos
191
+ * @param {number} minPixelSize
192
+ * @param {number} pixelGap
193
+ * @returns {{ x: number, y: number }|null}
194
+ */
195
+ const resolveBadgeScreenPosition = (scene, basePos, topWorldPos, minPixelSize, pixelGap) => {
196
+ const anchorWin = scene.cartesianToCanvasCoordinates(basePos)
197
+ if (!anchorWin || Number.isNaN(anchorWin.x) || Number.isNaN(anchorWin.y)) {
198
+ return null
199
+ }
200
+
201
+ const topWin = scene.cartesianToCanvasCoordinates(topWorldPos)
202
+ let offsetY = 0
203
+
204
+ if (topWin && !Number.isNaN(topWin.y)) {
205
+ offsetY = anchorWin.y - topWin.y
206
+ }
207
+
208
+ // minimumPixelSize 会让远景模型在屏幕上被抬高,仅用世界高度会产生“飘”感
209
+ if (minPixelSize > 0) {
210
+ offsetY = Math.max(offsetY, minPixelSize * 0.55)
211
+ }
212
+
213
+ if (offsetY <= 0) {
214
+ offsetY = minPixelSize > 0 ? minPixelSize * 0.55 : 28
215
+ }
216
+
217
+ return {
218
+ x: anchorWin.x,
219
+ y: anchorWin.y - offsetY - pixelGap,
220
+ }
221
+ }
222
+
135
223
  /**
136
224
  * 在模型头顶创建在线/离线信息牌
137
225
  * @param {Object} options
138
226
  * @param {Cesium.Viewer} options.map
227
+ * @param {Cesium.Entity} [options.entity] GLB 实体(用于读取包围球与 minimumPixelSize)
139
228
  * @param {string|number} [options.mapId]
140
229
  * @param {Function} options.getPosition 获取模型世界坐标
141
230
  * @param {Function} [options.getVisible] 是否显示信息牌
142
231
  * @param {boolean} [options.isOffline=false] true=离线,false=在线
143
- * @param {number} [options.labelOffset=4] 信息牌相对模型的高度偏移(米)
232
+ * @param {number} [options.labelOffset=1] 模型未加载完成时的兜底高度(米)
233
+ * @param {number} [options.pixelGap=2] 信息牌与车顶之间的像素间距
144
234
  */
145
235
  export function createGlbInfoBadge(options = {}) {
146
236
  const {
147
237
  map,
238
+ entity,
148
239
  mapId,
149
240
  getPosition,
150
241
  getVisible,
151
242
  isOffline = false,
152
243
  labelOffset = DEFAULT_LABEL_OFFSET,
244
+ pixelGap = DEFAULT_PIXEL_GAP,
153
245
  } = options
154
246
 
155
247
  if (!map || typeof getPosition !== 'function') {
@@ -172,27 +264,6 @@ export function createGlbInfoBadge(options = {}) {
172
264
 
173
265
  const textEl = badgeEl.querySelector('.glb-info-badge-text')
174
266
 
175
- const getBadgePosition = () => {
176
- const basePos = getPosition()
177
- if (!basePos) return null
178
-
179
- if (!(basePos instanceof Cesium.Cartesian3)) {
180
- return null
181
- }
182
-
183
- const cartographic = Cesium.Cartographic.fromCartesian(basePos)
184
- if (!Number.isFinite(cartographic.longitude) || !Number.isFinite(cartographic.latitude)) {
185
- return null
186
- }
187
-
188
- const height = Number.isFinite(cartographic.height) ? cartographic.height : 0
189
- return Cesium.Cartesian3.fromRadians(
190
- cartographic.longitude,
191
- cartographic.latitude,
192
- height + labelOffset
193
- )
194
- }
195
-
196
267
  const batchEntry = {
197
268
  updatePosition(hideByHeight) {
198
269
  if (!map?.scene) {
@@ -210,34 +281,38 @@ export function createGlbInfoBadge(options = {}) {
210
281
  return
211
282
  }
212
283
 
213
- let badgePosition
284
+ let basePos
214
285
  try {
215
- badgePosition = getBadgePosition()
286
+ basePos = getPosition()
216
287
  } catch (err) {
217
288
  badgeEl.style.display = 'none'
218
289
  return
219
290
  }
220
291
 
221
- if (!badgePosition) {
292
+ if (!basePos || !(basePos instanceof Cesium.Cartesian3)) {
222
293
  badgeEl.style.display = 'none'
223
294
  return
224
295
  }
225
296
 
226
- let canvasPos
227
- try {
228
- canvasPos = map.scene.cartesianToCanvasCoordinates(badgePosition)
229
- } catch (err) {
297
+ const time = map.clock.currentTime
298
+ const minPixelSize = getEntityMinimumPixelSize(entity, time)
299
+ const topWorldPos = resolveModelTopWorldPosition(map, entity, basePos, labelOffset)
300
+ const screenPos = resolveBadgeScreenPosition(
301
+ map.scene,
302
+ basePos,
303
+ topWorldPos,
304
+ minPixelSize,
305
+ pixelGap
306
+ )
307
+
308
+ if (!screenPos) {
230
309
  badgeEl.style.display = 'none'
231
310
  return
232
311
  }
233
312
 
234
- if (canvasPos && !Number.isNaN(canvasPos.x) && !Number.isNaN(canvasPos.y)) {
235
- badgeEl.style.left = `${canvasPos.x}px`
236
- badgeEl.style.top = `${canvasPos.y - 8}px`
237
- badgeEl.style.display = 'block'
238
- } else {
239
- badgeEl.style.display = 'none'
240
- }
313
+ badgeEl.style.left = `${screenPos.x}px`
314
+ badgeEl.style.top = `${screenPos.y}px`
315
+ badgeEl.style.display = 'block'
241
316
  }
242
317
  }
243
318
 
package/js/setPoint.js CHANGED
@@ -230,6 +230,7 @@ export function setPoint(baseUrl) {
230
230
  * @param options.height 高度(可选,默认0)
231
231
  * @param options.heading 朝向(可选,默认0)
232
232
  * @param options.isOffline 是否离线(true=离线,false=在线,默认 false)
233
+ * @param options.modelHeight 模型高度兜底值(米,可选,默认 1;未加载包围球时使用)
233
234
  * @param options.mapId 地图ID
234
235
  * @returns 创建的点位包装对象
235
236
  */
@@ -303,7 +304,9 @@ export function setPoint(baseUrl) {
303
304
  glbPoint.infoBadge = createGlbInfoBadge({
304
305
  map,
305
306
  mapId,
307
+ entity: modelEntity,
306
308
  isOffline,
309
+ labelOffset: Number(options.modelHeight) > 0 ? Number(options.modelHeight) : undefined,
307
310
  getPosition: () => {
308
311
  const posProp = modelEntity.position
309
312
  if (posProp instanceof Cesium.Cartesian3) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huweili-cesium",
3
- "version": "1.2.95",
3
+ "version": "1.2.97",
4
4
  "description": "基于 Cesium 的地图工具库(无人机态势、轨迹、围栏、工具栏等)",
5
5
  "type": "module",
6
6
  "main": "./index.js",