huweili-cesium 1.3.1 → 1.3.3

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.
Files changed (2) hide show
  1. package/js/drawFenceNew.js +165 -31
  2. package/package.json +1 -1
@@ -1002,10 +1002,20 @@ export function drawFenceNew() {
1002
1002
  let centerPointEntity = null
1003
1003
  // activePointEntity:活动点实体(跟随鼠标的边界点)
1004
1004
  let activePointEntity = null
1005
- // ellipseEntity:椭圆实体(用于显示圆形围栏的预览)
1006
- let ellipseEntity = null
1005
+ // circleWallEntity / circleOutlineEntity / radiusLineEntity:绘制过程预览(与最终围栏一致的贴地圆边)
1006
+ let circleWallEntity = null
1007
+ let circleOutlineEntity = null
1008
+ let radiusLineEntity = null
1009
+ // 预览几何缓存,仅在鼠标移动时更新,避免每帧重复计算
1010
+ let previewCirclePositions = []
1011
+ let previewWallMaxHeights = []
1012
+ let previewWallMinHeights = []
1013
+ let previewRadiusLinePositions = []
1014
+ let previewRenderFrameId = null
1007
1015
  // tipEntity:提示文字实体(显示"点击确定圆心"等提示)
1008
1016
  let tipEntity = null
1017
+ // radiusLabelEntity:绘制过程中显示当前半径的标签
1018
+ let radiusLabelEntity = null
1009
1019
  // nameLabelEntity:围栏名称标签实体
1010
1020
  let nameLabelEntity = null
1011
1021
  // handler:屏幕事件处理器(监听鼠标点击、移动等)
@@ -1016,10 +1026,13 @@ export function drawFenceNew() {
1016
1026
  // ==================== 临时实体ID配置 ====================
1017
1027
  // 这些是绘制过程中临时显示的实体,绘制完成后会清理
1018
1028
  const tempIds = {
1019
- ellipse: `${options.id}_draw_circle_wall`, // 临时椭圆围栏
1029
+ circleWall: `${options.id}_draw_circle_wall`,
1030
+ circleOutline: `${options.id}_draw_circle_outline`,
1031
+ radiusLine: `${options.id}_draw_radius_line`,
1020
1032
  center: `${options.id}_draw_center_point`, // 临时圆心点
1021
1033
  active: `${options.id}_draw_active_point`, // 临时边界点
1022
- tip: `${options.id}_draw_tip` // 临时提示文字
1034
+ tip: `${options.id}_draw_tip`, // 临时提示文字
1035
+ radiusLabel: `${options.id}_draw_radius_label`, // 临时半径标签
1023
1036
  }
1024
1037
 
1025
1038
  // ==================== 辅助函数部分 ====================
@@ -1078,6 +1091,23 @@ export function drawFenceNew() {
1078
1091
  return geodesic.surfaceDistance
1079
1092
  }
1080
1093
 
1094
+ const formatRadius = (radius) => {
1095
+ if (!Number.isFinite(radius)) return '0 m'
1096
+ if (radius >= 1000) return `${(radius / 1000).toFixed(2)} km`
1097
+ return `${radius.toFixed(2)} m`
1098
+ }
1099
+
1100
+ const getRadiusLabelPosition = () => {
1101
+ if (!centerPosition || !edgePosition) return null
1102
+ const centerCartographic = Cesium.Cartographic.fromCartesian(centerPosition)
1103
+ const edgeCartographic = Cesium.Cartographic.fromCartesian(edgePosition)
1104
+ return Cesium.Cartesian3.fromRadians(
1105
+ (centerCartographic.longitude + edgeCartographic.longitude) / 2,
1106
+ (centerCartographic.latitude + edgeCartographic.latitude) / 2,
1107
+ 0,
1108
+ )
1109
+ }
1110
+
1081
1111
  /**
1082
1112
  * 获取提示文字的位置(鼠标位置优先)
1083
1113
  *
@@ -1099,7 +1129,7 @@ export function drawFenceNew() {
1099
1129
  }
1100
1130
 
1101
1131
  // 已经有圆心 → 提示用户"移动调整半径,点击或右键完成"
1102
- return '移动鼠标调整半径,点击或右键完成绘制'
1132
+ return `移动鼠标调整半径,当前半径:${formatRadius(getRadius())},点击或右键完成绘制`
1103
1133
  }
1104
1134
 
1105
1135
  /**
@@ -1119,6 +1149,42 @@ export function drawFenceNew() {
1119
1149
  })
1120
1150
  }
1121
1151
 
1152
+ const updatePreviewGeometry = () => {
1153
+ if (!centerPosition || !edgePosition) {
1154
+ previewCirclePositions = []
1155
+ previewWallMaxHeights = []
1156
+ previewWallMinHeights = []
1157
+ previewRadiusLinePositions = []
1158
+ return
1159
+ }
1160
+
1161
+ const radius = getRadius()
1162
+ previewRadiusLinePositions = [centerPosition, edgePosition]
1163
+
1164
+ if (radius <= 0) {
1165
+ previewCirclePositions = []
1166
+ previewWallMaxHeights = []
1167
+ previewWallMinHeights = []
1168
+ return
1169
+ }
1170
+
1171
+ const centerData = cartesianToLngLatHeight(centerPosition)
1172
+ const circleLngLats = generateCircleLngLatPositions(centerData.lng, centerData.lat, radius)
1173
+ const footprint = circleLngLats.map(cartesianFootprintFromLngLat)
1174
+ previewCirclePositions = [...footprint, footprint[0]]
1175
+ previewWallMaxHeights = previewCirclePositions.map(() => height)
1176
+ previewWallMinHeights = previewCirclePositions.map(() => 0)
1177
+ }
1178
+
1179
+ const schedulePreviewUpdate = () => {
1180
+ if (previewRenderFrameId !== null) return
1181
+ previewRenderFrameId = requestAnimationFrame(() => {
1182
+ previewRenderFrameId = null
1183
+ updatePreviewGeometry()
1184
+ map.scene.requestRender()
1185
+ })
1186
+ }
1187
+
1122
1188
  // ==================== 核心业务函数 ====================
1123
1189
 
1124
1190
  /**
@@ -1142,25 +1208,45 @@ export function drawFenceNew() {
1142
1208
  })
1143
1209
  }
1144
1210
 
1145
- // 2. 创建椭圆实体(如果还没有)
1146
- if (!ellipseEntity) {
1147
- ellipseEntity = map.entities.add({
1148
- id: tempIds.ellipse,
1149
- // 使用CallbackProperty实现动态更新:当centerPosition或radius变化时自动更新
1150
- position: new Cesium.CallbackProperty(() => centerPosition, false),
1151
- ellipse: {
1152
- // 椭圆的长半轴(动态半径)
1153
- semiMajorAxis: new Cesium.CallbackProperty(() => getRadius(), false),
1154
- // 椭圆的短半轴(与长半轴相同 = 圆形)
1155
- semiMinorAxis: new Cesium.CallbackProperty(() => getRadius(), false),
1156
- height: 0, // 椭圆底部高度(地面)
1157
- extrudedHeight: height, // 椭圆顶部高度(围栏高度)
1158
- material: color.withAlpha(0), // 填充材质(透明)
1159
- outline: true, // 显示边框
1160
- outlineColor: color, // 边框颜色
1161
- outlineWidth, // 边框宽度
1162
- numberOfVerticalLines: 64 // 垂直线条数,数值越大圆形越圆滑
1163
- }
1211
+ // 2. 创建贴地圆边预览(确定圆心后显示,与最终围栏 polyline / wall 一致)
1212
+ if (centerPosition && !circleWallEntity) {
1213
+ circleWallEntity = map.entities.add({
1214
+ id: tempIds.circleWall,
1215
+ wall: {
1216
+ positions: new Cesium.CallbackProperty(() => previewCirclePositions, false),
1217
+ maximumHeights: new Cesium.CallbackProperty(() => previewWallMaxHeights, false),
1218
+ minimumHeights: new Cesium.CallbackProperty(() => previewWallMinHeights, false),
1219
+ material: color.withAlpha(0.18),
1220
+ outline: true,
1221
+ outlineColor: color,
1222
+ outlineWidth,
1223
+ },
1224
+ })
1225
+ }
1226
+
1227
+ if (centerPosition && !circleOutlineEntity) {
1228
+ circleOutlineEntity = map.entities.add({
1229
+ id: tempIds.circleOutline,
1230
+ polyline: {
1231
+ positions: new Cesium.CallbackProperty(() => previewCirclePositions, false),
1232
+ width: outlineWidth,
1233
+ material: color,
1234
+ clampToGround: true,
1235
+ arcType: Cesium.ArcType.GEODESIC,
1236
+ },
1237
+ })
1238
+ }
1239
+
1240
+ if (centerPosition && !radiusLineEntity) {
1241
+ radiusLineEntity = map.entities.add({
1242
+ id: tempIds.radiusLine,
1243
+ polyline: {
1244
+ positions: new Cesium.CallbackProperty(() => previewRadiusLinePositions, false),
1245
+ width: Math.max(1, outlineWidth - 1),
1246
+ material: color.withAlpha(0.85),
1247
+ clampToGround: true,
1248
+ arcType: Cesium.ArcType.GEODESIC,
1249
+ },
1164
1250
  })
1165
1251
  }
1166
1252
 
@@ -1180,7 +1266,32 @@ export function drawFenceNew() {
1180
1266
  })
1181
1267
  }
1182
1268
 
1183
- // 4. 创建提示文字实体(如果还没有)
1269
+ // 4. 创建半径标签(确定圆心后显示)
1270
+ if (!radiusLabelEntity && centerPosition) {
1271
+ radiusLabelEntity = map.entities.add({
1272
+ id: tempIds.radiusLabel,
1273
+ position: new Cesium.CallbackProperty(() => getRadiusLabelPosition(), false),
1274
+ label: {
1275
+ text: new Cesium.CallbackProperty(() => formatRadius(getRadius()), false),
1276
+ font: 'bold 10px Microsoft YaHei',
1277
+ fillColor: Cesium.Color.WHITE,
1278
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
1279
+ outlineColor: Cesium.Color.BLACK,
1280
+ outlineWidth: 2,
1281
+ showBackground: true,
1282
+ backgroundColor: Cesium.Color.fromCssColorString('rgba(0, 0, 0, 0.75)'),
1283
+ backgroundPadding: new Cesium.Cartesian2(4, 4),
1284
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
1285
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
1286
+ pixelOffset: new Cesium.Cartesian2(0, -16),
1287
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
1288
+ distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 2000000),
1289
+ scaleByDistance: new Cesium.NearFarScalar(1000, 1.2, 5000000, 0.6),
1290
+ },
1291
+ })
1292
+ }
1293
+
1294
+ // 5. 创建提示文字实体(如果还没有)
1184
1295
  if (!tipEntity) {
1185
1296
  tipEntity = createMouseTipEntity(map, {
1186
1297
  id: tempIds.tip,
@@ -1196,16 +1307,28 @@ export function drawFenceNew() {
1196
1307
  */
1197
1308
  const cleanupTempEntities = () => {
1198
1309
  // 遍历所有临时实体,如果存在就从地图上移除
1199
- ;[ellipseEntity, centerPointEntity, activePointEntity, tipEntity, nameLabelEntity].forEach((entity) => {
1310
+ ;[circleWallEntity, circleOutlineEntity, radiusLineEntity, centerPointEntity, activePointEntity, radiusLabelEntity, tipEntity, nameLabelEntity].forEach((entity) => {
1200
1311
  if (entity) {
1201
1312
  map.entities.remove(entity)
1202
1313
  }
1203
1314
  })
1204
1315
  // 将变量清空,防止内存泄露
1205
- ellipseEntity = null
1316
+ circleWallEntity = null
1317
+ circleOutlineEntity = null
1318
+ radiusLineEntity = null
1206
1319
  centerPointEntity = null
1207
1320
  activePointEntity = null
1321
+ radiusLabelEntity = null
1322
+ tipEntity = null
1208
1323
  nameLabelEntity = null
1324
+ previewCirclePositions = []
1325
+ previewWallMaxHeights = []
1326
+ previewWallMinHeights = []
1327
+ previewRadiusLinePositions = []
1328
+ if (previewRenderFrameId !== null) {
1329
+ cancelAnimationFrame(previewRenderFrameId)
1330
+ previewRenderFrameId = null
1331
+ }
1209
1332
  }
1210
1333
 
1211
1334
  /**
@@ -1245,9 +1368,17 @@ export function drawFenceNew() {
1245
1368
  const footprint = circleLngLats.map(cartesianFootprintFromLngLat)
1246
1369
  const wallPositions = [...footprint, footprint[0]]
1247
1370
 
1248
- if (ellipseEntity) {
1249
- map.entities.remove(ellipseEntity)
1250
- ellipseEntity = null
1371
+ if (circleWallEntity) {
1372
+ map.entities.remove(circleWallEntity)
1373
+ circleWallEntity = null
1374
+ }
1375
+ if (circleOutlineEntity) {
1376
+ map.entities.remove(circleOutlineEntity)
1377
+ circleOutlineEntity = null
1378
+ }
1379
+ if (radiusLineEntity) {
1380
+ map.entities.remove(radiusLineEntity)
1381
+ radiusLineEntity = null
1251
1382
  }
1252
1383
 
1253
1384
  const fenceEntity = map.entities.add({
@@ -1273,13 +1404,14 @@ export function drawFenceNew() {
1273
1404
 
1274
1405
  await applyTerrainWallToEntity(map, fenceEntity, circleLngLats, height)
1275
1406
 
1276
- ;[centerPointEntity, activePointEntity, tipEntity].forEach((entity) => {
1407
+ ;[centerPointEntity, activePointEntity, radiusLabelEntity, tipEntity].forEach((entity) => {
1277
1408
  if (entity) {
1278
1409
  map.entities.remove(entity)
1279
1410
  }
1280
1411
  })
1281
1412
  centerPointEntity = null
1282
1413
  activePointEntity = null
1414
+ radiusLabelEntity = null
1283
1415
  tipEntity = null
1284
1416
 
1285
1417
  const centerHeights = await sampleTerrainHeights(map, [centerData])
@@ -1404,6 +1536,7 @@ export function drawFenceNew() {
1404
1536
  centerPosition = Cesium.Cartesian3.clone(cartesian)
1405
1537
  edgePosition = Cesium.Cartesian3.clone(cartesian) // 边界点初始化为圆心
1406
1538
  ensurePreviewEntities() // 确保预览实体都创建好
1539
+ schedulePreviewUpdate()
1407
1540
  emitChange() // 触发变化回调
1408
1541
  return
1409
1542
  }
@@ -1426,6 +1559,7 @@ export function drawFenceNew() {
1426
1559
  // 如果已经确定了圆心 → 实时更新边界点,让预览跟随鼠标
1427
1560
  if (!centerPosition) return
1428
1561
  edgePosition = Cesium.Cartesian3.clone(cartesian)
1562
+ schedulePreviewUpdate()
1429
1563
  }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
1430
1564
 
1431
1565
  // ==================== 事件3:鼠标右键点击 ====================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huweili-cesium",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "基于 Cesium 的地图工具库(无人机态势、轨迹、围栏、工具栏等)",
5
5
  "type": "module",
6
6
  "main": "./index.js",