cesium-xs-sdk 1.0.8 → 1.0.9

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.
@@ -3047,6 +3047,9 @@ class GraphicModule {
3047
3047
  return false;
3048
3048
  }
3049
3049
 
3050
+ // 清理可能存在的 outline 等关联 entity
3051
+ this.removeOutline(graphicId);
3052
+
3050
3053
  const entity = this.graphicMap.get(graphicId);
3051
3054
  this.viewer.entities.remove(entity);
3052
3055
  this.graphicMap.delete(graphicId);
@@ -3062,6 +3065,160 @@ class GraphicModule {
3062
3065
  getGraphic(graphicId) {
3063
3066
  return this.graphicMap.get(graphicId);
3064
3067
  }
3068
+
3069
+ /**
3070
+ * 从 entity 中提取顶点坐标
3071
+ * 支持 point / polyline / polygon
3072
+ * @private
3073
+ * @param {Cesium.Entity} entity 图元实体
3074
+ * @returns {Cesium.Cartesian3[]} 顶点坐标数组
3075
+ */
3076
+ _getEntityPositions(entity) {
3077
+ const getValue = (prop) => {
3078
+ if (prop == null) return undefined;
3079
+ return typeof prop.getValue === 'function' ? prop.getValue(Cesium$e.JulianDate.now()) : prop;
3080
+ };
3081
+
3082
+ // 优先按 geometry 类型取顶点,避免 polygon/polyline 也带 position 时只拿到中心点
3083
+ if (entity.polygon?.hierarchy) {
3084
+ const hierarchy = getValue(entity.polygon.hierarchy);
3085
+ if (Array.isArray(hierarchy)) return hierarchy;
3086
+ if (Array.isArray(hierarchy?.positions)) return hierarchy.positions;
3087
+ return [];
3088
+ }
3089
+ if (entity.polyline?.positions) {
3090
+ return getValue(entity.polyline.positions) ?? [];
3091
+ }
3092
+ if (entity.position) {
3093
+ const p = getValue(entity.position);
3094
+ return p ? [p] : [];
3095
+ }
3096
+ return [];
3097
+ }
3098
+
3099
+ /**
3100
+ * 获取图元顶点坐标
3101
+ * @param {string|Cesium.Entity} graphicIdOrEntity 图元ID或实体
3102
+ * @returns {Cesium.Cartesian3[]} 顶点坐标数组
3103
+ */
3104
+ getPositions(graphicIdOrEntity) {
3105
+ const entity = typeof graphicIdOrEntity === 'string'
3106
+ ? this.getGraphic(graphicIdOrEntity)
3107
+ : graphicIdOrEntity;
3108
+ return entity ? this._getEntityPositions(entity) : [];
3109
+ }
3110
+
3111
+ /**
3112
+ * 获取图元包围球
3113
+ * @param {string|Cesium.Entity} graphicIdOrEntity 图元ID或实体
3114
+ * @returns {Cesium.BoundingSphere|null} 包围球
3115
+ */
3116
+ getBoundingSphere(graphicIdOrEntity) {
3117
+ const positions = this.getPositions(graphicIdOrEntity);
3118
+ if (!positions.length) return null;
3119
+
3120
+ const sphere = Cesium$e.BoundingSphere.fromPoints(positions);
3121
+ // 点图元只有单个坐标,radius 为 0,给一个最小可视半径避免相机钻进点里
3122
+ if (positions.length === 1 && sphere.radius === 0) {
3123
+ sphere.radius = 5000;
3124
+ }
3125
+ return sphere;
3126
+ }
3127
+
3128
+ /**
3129
+ * 飞行定位到图元
3130
+ * @param {string|Cesium.Entity} graphicIdOrEntity 图元ID或实体
3131
+ * @param {object} options 配置项 { pitch?, padding?, duration? }
3132
+ * @returns {Promise<boolean>}
3133
+ */
3134
+ flyTo(graphicIdOrEntity, options = {}) {
3135
+ const entity = typeof graphicIdOrEntity === 'string'
3136
+ ? this.getGraphic(graphicIdOrEntity)
3137
+ : graphicIdOrEntity;
3138
+ if (!entity || !this.viewer) return Promise.resolve(false);
3139
+
3140
+ const sphere = this.getBoundingSphere(entity);
3141
+ if (!sphere) return Promise.resolve(false);
3142
+
3143
+ const pitch = options.pitch ?? -Cesium$e.Math.PI_OVER_TWO;
3144
+ const padding = options.padding ?? 2;
3145
+ const fovy = this.viewer.camera.frustum.fovy;
3146
+ const range = (sphere.radius / Math.tan(fovy / 2)) * padding;
3147
+
3148
+ return this.viewer.camera.flyToBoundingSphere(sphere, {
3149
+ duration: options.duration ?? 1.5,
3150
+ offset: new Cesium$e.HeadingPitchRange(0, pitch, range),
3151
+ }).then(() => true).catch((err) => {
3152
+ console.error('图元定位失败', err);
3153
+ return false;
3154
+ });
3155
+ }
3156
+
3157
+ /**
3158
+ * 瞬间定位到图元(无动画)
3159
+ * @param {string|Cesium.Entity} graphicIdOrEntity 图元ID或实体
3160
+ * @param {object} options 配置项 { pitch?, padding? }
3161
+ */
3162
+ zoomTo(graphicIdOrEntity, options = {}) {
3163
+ const entity = typeof graphicIdOrEntity === 'string'
3164
+ ? this.getGraphic(graphicIdOrEntity)
3165
+ : graphicIdOrEntity;
3166
+ if (!entity || !this.viewer) return;
3167
+
3168
+ const sphere = this.getBoundingSphere(entity);
3169
+ if (!sphere) return;
3170
+
3171
+ const pitch = options.pitch ?? -Cesium$e.Math.PI_OVER_TWO;
3172
+ const padding = options.padding ?? 2;
3173
+ const fovy = this.viewer.camera.frustum.fovy;
3174
+ const range = (sphere.radius / Math.tan(fovy / 2)) * padding;
3175
+
3176
+ this.viewer.camera.viewBoundingSphere(sphere, new Cesium$e.HeadingPitchRange(0, pitch, range));
3177
+ }
3178
+
3179
+ /**
3180
+ * 为面图元设置/移除外边框(用 polyline 模拟,解决 Cesium 贴地 polygon 不支持 outline 的问题)
3181
+ * @param {string} graphicId 图元ID
3182
+ * @param {object} options 配置项 { enabled, color, width }
3183
+ * @returns {boolean} 是否设置成功
3184
+ */
3185
+ setOutline(graphicId, options = {}) {
3186
+ const entity = this.getGraphic(graphicId);
3187
+ if (!entity || !entity.polygon || !this.viewer) return false;
3188
+
3189
+ // 先移除旧的
3190
+ this.removeOutline(graphicId);
3191
+
3192
+ if (!options.enabled) return true;
3193
+
3194
+ const positions = this._getEntityPositions(entity);
3195
+ if (positions.length < 3) return false;
3196
+
3197
+ const closedPositions = [...positions, positions[0]];
3198
+ const outlineId = `${graphicId}_outline`;
3199
+ const color = options.color ?? Cesium$e.Color.WHITE;
3200
+ const width = options.width ?? 2;
3201
+
3202
+ this.viewer.entities.add({
3203
+ id: outlineId,
3204
+ polyline: {
3205
+ positions: closedPositions,
3206
+ width,
3207
+ material: color,
3208
+ clampToGround: true,
3209
+ },
3210
+ });
3211
+ return true;
3212
+ }
3213
+
3214
+ /**
3215
+ * 移除面图元的外边框
3216
+ * @param {string} graphicId 图元ID
3217
+ */
3218
+ removeOutline(graphicId) {
3219
+ if (!this.viewer) return;
3220
+ this.viewer.entities.removeById(`${graphicId}_outline`);
3221
+ }
3065
3222
  }
3066
3223
 
3067
3224
  const Cesium$d = getCesium();
@@ -6886,563 +7043,563 @@ class GeoCalcUtil {
6886
7043
  }
6887
7044
  }
6888
7045
 
6889
- const Cesium$6 = getCesium();
6890
-
6891
- /**
6892
- * 测量类型枚举
6893
- */
6894
- const MeasureType = {
6895
- DISTANCE: 'distance',
6896
- AREA: 'area'
6897
- };
6898
-
6899
- /**
6900
- * 测量工具
6901
- * 支持交互式测距(折线)和测面积(多边形)。
6902
- * 左键加点,右键 / Enter 完成,Esc 取消,Backspace 撤销上一点。
6903
- */
6904
- class MeasureTool extends BaseTool {
6905
- constructor(editorModule, options = {}) {
6906
- super(editorModule, options);
6907
-
6908
- this._measureType = options.measureType || MeasureType.DISTANCE;
6909
- this._positions = []; // 已点击的经纬度点
6910
- this._mousePos = null; // 当前鼠标位置(经纬度)
6911
- this._isMeasuring = false; // 是否正在一次测量中
6912
- this._lastResult = null; // 最后一次测量结果
6913
-
6914
- // 临时图元引用
6915
- this._pointEntities = []; // 点击点标记
6916
- this._previewLine = null; // 测距预览线
6917
- this._previewPolygon = null; // 测面积预览面
6918
- this._previewOutline = null; // 测面积预览轮廓线
6919
- this._previewCartesians = []; // 当前预览几何的笛卡尔坐标数组(CallbackProperty 共享引用)
6920
- this._segmentLabelEntities = []; // 测距每段标注
6921
- this._totalLabelEntity = null; // 总距离 / 面积标注
6922
-
6923
- // 已完成的测量结果图元分组,支持一次激活中保留多个测量结果
6924
- this._finishedResults = [];
6925
- }
6926
-
6927
- /**
6928
- * 设置测量类型
6929
- * @param {string} type MeasureType
6930
- */
6931
- setMeasureType(type) {
6932
- this._measureType = type;
6933
- return this;
6934
- }
6935
-
6936
- /**
6937
- * 获取当前测量类型
6938
- */
6939
- getMeasureType() {
6940
- return this._measureType;
6941
- }
6942
-
6943
- /**
6944
- * 获取最后一次测量结果
6945
- */
6946
- getLastResult() {
6947
- return this._lastResult;
6948
- }
6949
-
6950
- onActivate() {
6951
- this.registerObserver('mouse:click', this._onClick.bind(this));
6952
- this.registerObserver('mouse:move', this._onMove.bind(this));
6953
- this.registerObserver('mouse:rightclick', this._onRightClick.bind(this));
6954
- this.registerObserver('keyboard:keydown', this._onKeyDown.bind(this));
6955
- }
6956
-
6957
- onDeactivate() {
6958
- this.clearMeasurements();
6959
- }
6960
-
6961
- _onClick(lngLat) {
6962
- if (!lngLat) return;
6963
-
6964
- this._isMeasuring = true;
6965
- this._positions.push(lngLat);
6966
- this._addPointMarker(lngLat);
6967
- this._updateMeasurement();
6968
- }
6969
-
6970
- _onMove(data) {
6971
- const screenPos = data.position || data.endPosition;
6972
- if (!screenPos) return;
6973
-
6974
- const lonLat = this.screenToLonLat({ x: screenPos.x, y: screenPos.y });
6975
- if (!lonLat) return;
6976
-
6977
- this._mousePos = lonLat;
6978
- if (this._isMeasuring) {
6979
- this._updateMeasurement();
6980
- }
6981
- }
6982
-
6983
- _onRightClick() {
6984
- this._finishMeasurement();
6985
- }
6986
-
6987
- _onKeyDown(data) {
6988
- if (data.key === 'Escape') {
6989
- this.cancelMeasurement();
6990
- return;
6991
- }
6992
-
6993
- if (data.key === 'Enter') {
6994
- this._finishMeasurement();
6995
- return;
6996
- }
6997
-
6998
- if (data.key === 'Backspace' && this._isMeasuring && this._positions.length > 0) {
6999
- this._positions.pop();
7000
- const lastPoint = this._pointEntities.pop();
7001
- if (lastPoint) {
7002
- this.removeTempEntity(lastPoint);
7003
- }
7004
- this._updateMeasurement();
7005
- }
7006
- }
7007
-
7008
- /**
7009
- * 完成当前测量
7010
- */
7011
- _finishMeasurement() {
7012
- const minPoints = this._measureType === MeasureType.AREA ? 3 : 2;
7013
- if (this._positions.length < minPoints) return;
7014
-
7015
- this._isMeasuring = false;
7016
- this._mousePos = null;
7017
- this._updateMeasurement();
7018
-
7019
- const result = this._computeResult(this._positions);
7020
- this._lastResult = result;
7021
-
7022
- // 将当前测量结果保留在场景中,并清空工作态以便继续下一次测量
7023
- this._commitCurrentResult();
7024
-
7025
- this._emitMeasureComplete(result);
7026
- }
7027
-
7028
- /**
7029
- * 把当前测量图元归档为已完成结果,重置工作态
7030
- */
7031
- _commitCurrentResult() {
7032
- const finalCartesians = this._previewCartesians.slice();
7033
-
7034
- // 将动态 CallbackProperty 替换为常量几何,避免归档后随 _previewCartesians 变化而消失
7035
- if (this._previewLine) {
7036
- this._previewLine.polyline.positions = finalCartesians.length > 0 ? finalCartesians : [];
7037
- }
7038
- if (this._previewPolygon) {
7039
- this._previewPolygon.polygon.hierarchy = finalCartesians.length >= 3
7040
- ? new Cesium$6.PolygonHierarchy(finalCartesians)
7041
- : new Cesium$6.PolygonHierarchy([]);
7042
- }
7043
- if (this._previewOutline) {
7044
- this._previewOutline.polyline.positions = finalCartesians.length >= 3
7045
- ? [...finalCartesians, finalCartesians[0]]
7046
- : [];
7047
- }
7048
-
7049
- this._finishedResults.push({
7050
- pointEntities: this._pointEntities,
7051
- segmentLabels: this._segmentLabelEntities,
7052
- totalLabel: this._totalLabelEntity,
7053
- previewLine: this._previewLine,
7054
- previewPolygon: this._previewPolygon,
7055
- previewOutline: this._previewOutline
7056
- });
7057
-
7058
- this._positions = [];
7059
- this._mousePos = null;
7060
- this._isMeasuring = false;
7061
- this._previewCartesians = [];
7062
-
7063
- this._pointEntities = [];
7064
- this._segmentLabelEntities = [];
7065
- this._totalLabelEntity = null;
7066
- this._previewLine = null;
7067
- this._previewPolygon = null;
7068
- this._previewOutline = null;
7069
- }
7070
-
7071
- /**
7072
- * 取消当前测量
7073
- */
7074
- cancelMeasurement() {
7075
- this.clearMeasurements();
7076
- this._emitMeasureCancel();
7077
- }
7078
-
7079
- /**
7080
- * 清空所有测量结果与临时图元
7081
- */
7082
- clearMeasurements() {
7083
- this._positions = [];
7084
- this._mousePos = null;
7085
- this._isMeasuring = false;
7086
- this._lastResult = null;
7087
- this._previewCartesians = [];
7088
-
7089
- this._pointEntities.forEach(e => this.removeTempEntity(e));
7090
- this._pointEntities = [];
7091
-
7092
- this._segmentLabelEntities.forEach(e => this.removeTempEntity(e));
7093
- this._segmentLabelEntities = [];
7094
-
7095
- if (this._totalLabelEntity) {
7096
- this.removeTempEntity(this._totalLabelEntity);
7097
- this._totalLabelEntity = null;
7098
- }
7099
-
7100
- if (this._previewLine) {
7101
- this.removeTempEntity(this._previewLine);
7102
- this._previewLine = null;
7103
- }
7104
-
7105
- if (this._previewPolygon) {
7106
- this.removeTempEntity(this._previewPolygon);
7107
- this._previewPolygon = null;
7108
- }
7109
-
7110
- if (this._previewOutline) {
7111
- this.removeTempEntity(this._previewOutline);
7112
- this._previewOutline = null;
7113
- }
7114
-
7115
- this._finishedResults.forEach(group => {
7116
- group.pointEntities.forEach(e => this.removeTempEntity(e));
7117
- group.segmentLabels.forEach(e => this.removeTempEntity(e));
7118
- if (group.totalLabel) this.removeTempEntity(group.totalLabel);
7119
- if (group.previewLine) this.removeTempEntity(group.previewLine);
7120
- if (group.previewPolygon) this.removeTempEntity(group.previewPolygon);
7121
- if (group.previewOutline) this.removeTempEntity(group.previewOutline);
7122
- });
7123
- this._finishedResults = [];
7124
- }
7125
-
7126
- /**
7127
- * 更新预览几何与标注
7128
- */
7129
- _updateMeasurement() {
7130
- if (this._positions.length === 0) return;
7131
-
7132
- const positions = [...this._positions];
7133
- if (this._isMeasuring && this._mousePos) {
7134
- positions.push(this._mousePos);
7135
- }
7136
-
7137
- if (this._measureType === MeasureType.DISTANCE) {
7138
- this._updateDistancePreview(positions);
7139
- } else {
7140
- this._updateAreaPreview(positions);
7141
- }
7142
-
7143
- if (this._isMeasuring) {
7144
- this._emitMeasureProgress(this._computeResult(positions));
7145
- }
7146
- }
7147
-
7148
- _updateDistancePreview(positions) {
7149
- const cartesians = positions.length >= 2
7150
- ? positions.map(p => Cesium$6.Cartesian3.fromDegrees(...p))
7151
- : [];
7152
- this._previewCartesians = cartesians;
7153
-
7154
- // 预览线
7155
- if (!this._previewLine) {
7156
- this._previewLine = this.createTempEntity(this.generateId('measure_line'), {
7157
- polyline: {
7158
- positions: new Cesium$6.CallbackProperty(() => this._previewCartesians, false),
7159
- material: Cesium$6.Color.YELLOW,
7160
- width: 2,
7161
- clampToGround: true
7162
- }
7163
- });
7164
- }
7165
- this._previewLine.show = cartesians.length >= 2;
7166
-
7167
- // 每段标注
7168
- const segmentCount = Math.max(0, positions.length - 1);
7169
- this._ensureSegmentLabels(segmentCount);
7170
-
7171
- let total = 0;
7172
- for (let i = 0; i < segmentCount; i++) {
7173
- const p1 = positions[i];
7174
- const p2 = positions[i + 1];
7175
- const dist = GeoCalcUtil.calcDistance(p1, p2);
7176
- total += dist;
7177
-
7178
- const mid = Cesium$6.Cartesian3.midpoint(
7179
- Cesium$6.Cartesian3.fromDegrees(...p1),
7180
- Cesium$6.Cartesian3.fromDegrees(...p2),
7181
- new Cesium$6.Cartesian3()
7182
- );
7183
-
7184
- const label = this._segmentLabelEntities[i];
7185
- label.position = mid;
7186
- label.label.text = this._formatDistance(dist).text;
7187
- label.show = true;
7188
- }
7189
-
7190
- // 总距离标注
7191
- const lastCartesian = cartesians[cartesians.length - 1];
7192
- if (positions.length >= 2) {
7193
- if (!this._totalLabelEntity) {
7194
- this._totalLabelEntity = this._createLabelEntity(
7195
- this.generateId('measure_total'),
7196
- lastCartesian,
7197
- `总长:${this._formatDistance(total).text}`
7198
- );
7199
- } else {
7200
- this._totalLabelEntity.position = lastCartesian;
7201
- this._totalLabelEntity.label.text = `总长:${this._formatDistance(total).text}`;
7202
- this._totalLabelEntity.show = true;
7203
- }
7204
- } else if (this._totalLabelEntity) {
7205
- this._totalLabelEntity.show = false;
7206
- }
7207
- }
7208
-
7209
- _updateAreaPreview(positions) {
7210
- const cartesians = positions.map(p => Cesium$6.Cartesian3.fromDegrees(...p));
7211
- this._previewCartesians = cartesians;
7212
-
7213
- if (positions.length >= 3) {
7214
- // 显示面 + 闭合轮廓
7215
- if (!this._previewPolygon) {
7216
- this._previewPolygon = this.createTempEntity(this.generateId('measure_polygon'), {
7217
- polygon: {
7218
- hierarchy: new Cesium$6.CallbackProperty(() => new Cesium$6.PolygonHierarchy(this._previewCartesians), false),
7219
- material: Cesium$6.Color.YELLOW.withAlpha(0.2),
7220
- outline: false
7221
- }
7222
- });
7223
- this._previewOutline = this.createTempEntity(this.generateId('measure_outline'), {
7224
- polyline: {
7225
- positions: new Cesium$6.CallbackProperty(() => this._previewCartesians.length > 0
7226
- ? [...this._previewCartesians, this._previewCartesians[0]]
7227
- : [], false),
7228
- material: Cesium$6.Color.YELLOW,
7229
- width: 2,
7230
- clampToGround: true
7231
- }
7232
- });
7233
- }
7234
- this._previewPolygon.show = true;
7235
- this._previewOutline.show = true;
7236
- if (this._previewLine) {
7237
- this._previewLine.show = false;
7238
- }
7239
-
7240
- const area = GeoCalcUtil.calcArea(positions);
7241
- const centroid = this._computeCentroid(cartesians);
7242
-
7243
- if (!this._totalLabelEntity) {
7244
- this._totalLabelEntity = this._createLabelEntity(
7245
- this.generateId('measure_area'),
7246
- centroid,
7247
- `面积:${this._formatArea(area).text}`
7248
- );
7249
- } else {
7250
- this._totalLabelEntity.position = centroid;
7251
- this._totalLabelEntity.label.text = `面积:${this._formatArea(area).text}`;
7252
- this._totalLabelEntity.show = true;
7253
- }
7254
- } else {
7255
- // 不足 3 点时只显示连线,并隐藏已有的面、轮廓、面积标注
7256
- if (this._previewPolygon) {
7257
- this._previewPolygon.show = false;
7258
- }
7259
- if (this._previewOutline) {
7260
- this._previewOutline.show = false;
7261
- }
7262
- if (this._totalLabelEntity) {
7263
- this._totalLabelEntity.show = false;
7264
- }
7265
-
7266
- if (!this._previewLine) {
7267
- this._previewLine = this.createTempEntity(this.generateId('measure_line'), {
7268
- polyline: {
7269
- positions: new Cesium$6.CallbackProperty(() => this._previewCartesians, false),
7270
- material: Cesium$6.Color.YELLOW,
7271
- width: 2,
7272
- clampToGround: true
7273
- }
7274
- });
7275
- }
7276
- this._previewLine.show = positions.length >= 2;
7277
- }
7278
- }
7279
-
7280
- _ensureSegmentLabels(count) {
7281
- // 移除多余的标注
7282
- while (this._segmentLabelEntities.length > count) {
7283
- const label = this._segmentLabelEntities.pop();
7284
- this.removeTempEntity(label);
7285
- }
7286
-
7287
- // 创建缺少的标注
7288
- while (this._segmentLabelEntities.length < count) {
7289
- const label = this._createLabelEntity(
7290
- this.generateId('measure_segment'),
7291
- Cesium$6.Cartesian3.ZERO,
7292
- ''
7293
- );
7294
- this._segmentLabelEntities.push(label);
7295
- }
7296
- }
7297
-
7298
- _createLabelEntity(id, position, text) {
7299
- return this.createTempEntity(id, {
7300
- position,
7301
- label: {
7302
- text,
7303
- font: '14px Microsoft YaHei',
7304
- fillColor: Cesium$6.Color.WHITE,
7305
- outlineColor: Cesium$6.Color.BLACK,
7306
- outlineWidth: 2,
7307
- style: Cesium$6.LabelStyle.FILL_AND_OUTLINE,
7308
- verticalOrigin: Cesium$6.VerticalOrigin.BOTTOM,
7309
- horizontalOrigin: Cesium$6.HorizontalOrigin.CENTER,
7310
- heightReference: Cesium$6.HeightReference.CLAMP_TO_GROUND,
7311
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
7312
- pixelOffset: new Cesium$6.Cartesian2(0, -10)
7313
- }
7314
- });
7315
- }
7316
-
7317
- _addPointMarker(lonLat) {
7318
- const point = this.createTempEntity(this.generateId('measure_point'), {
7319
- position: Cesium$6.Cartesian3.fromDegrees(...lonLat),
7320
- point: {
7321
- color: Cesium$6.Color.YELLOW,
7322
- pixelSize: 8,
7323
- outlineColor: Cesium$6.Color.BLACK,
7324
- outlineWidth: 1,
7325
- heightReference: Cesium$6.HeightReference.CLAMP_TO_GROUND,
7326
- disableDepthTestDistance: Number.POSITIVE_INFINITY
7327
- }
7328
- });
7329
- this._pointEntities.push(point);
7330
- }
7331
-
7332
- _computeResult(positions) {
7333
- if (this._measureType === MeasureType.DISTANCE) {
7334
- let total = 0;
7335
- for (let i = 0; i < positions.length - 1; i++) {
7336
- total += GeoCalcUtil.calcDistance(positions[i], positions[i + 1]);
7337
- }
7338
- const fmt = this._formatDistance(total);
7339
- return {
7340
- type: MeasureType.DISTANCE,
7341
- value: total,
7342
- unit: fmt.unit,
7343
- formatted: fmt.text,
7344
- positions: positions.slice()
7345
- };
7346
- }
7347
-
7348
- const area = GeoCalcUtil.calcArea(positions);
7349
- const fmt = this._formatArea(area);
7350
- return {
7351
- type: MeasureType.AREA,
7352
- value: area,
7353
- unit: fmt.unit,
7354
- formatted: fmt.text,
7355
- positions: positions.slice()
7356
- };
7357
- }
7358
-
7359
- _computeCentroid(cartesians) {
7360
- const center = new Cesium$6.Cartesian3(0, 0, 0);
7361
- for (const c of cartesians) {
7362
- Cesium$6.Cartesian3.add(center, c, center);
7363
- }
7364
- return Cesium$6.Cartesian3.multiplyByScalar(center, 1 / cartesians.length, center);
7365
- }
7366
-
7367
- _formatDistance(meters) {
7368
- if (meters >= 1000) {
7369
- return { text: `${(meters / 1000).toFixed(2)} km`, unit: 'km' };
7370
- }
7371
- return { text: `${meters.toFixed(2)} m`, unit: 'm' };
7372
- }
7373
-
7374
- _formatArea(squareMeters) {
7375
- if (squareMeters >= 1000000) {
7376
- return { text: `${(squareMeters / 1000000).toFixed(3)} km²`, unit: 'km²' };
7377
- }
7378
- return { text: `${squareMeters.toFixed(2)} m²`, unit: 'm²' };
7379
- }
7380
-
7381
- _emitMeasureProgress(result) {
7382
- if (this._options.onMeasureProgress) {
7383
- this._options.onMeasureProgress(result);
7384
- }
7385
- }
7386
-
7387
- _emitMeasureComplete(result) {
7388
- if (this._options.onMeasureComplete) {
7389
- this._options.onMeasureComplete(result);
7390
- }
7391
- }
7392
-
7393
- _emitMeasureCancel() {
7394
- if (this._options.onMeasureCancel) {
7395
- this._options.onMeasureCancel();
7396
- }
7397
- }
7398
- }
7399
-
7046
+ const Cesium$6 = getCesium();
7047
+
7400
7048
  /**
7401
- * 工具名称枚举
7049
+ * 测量类型枚举
7402
7050
  */
7403
- const ToolName = {
7404
- DRAW: 'draw',
7405
- EDIT: 'edit',
7406
- SELECT: 'select',
7407
- DELETE: 'delete',
7408
- MEASURE: 'measure'
7051
+ const MeasureType = {
7052
+ DISTANCE: 'distance',
7053
+ AREA: 'area'
7409
7054
  };
7410
7055
 
7411
7056
  /**
7412
- * 编辑器模块
7413
- * 负责工具的注册、切换、激活管理
7057
+ * 测量工具
7058
+ * 支持交互式测距(折线)和测面积(多边形)。
7059
+ * 左键加点,右键 / Enter 完成,Esc 取消,Backspace 撤销上一点。
7414
7060
  */
7415
- class EditorModule {
7416
- constructor(viewer, options = {}) {
7417
- this.viewer = viewer;
7418
- this.config = Config.getInstance().getConfig();
7419
- this.graphicModule = null; // 依赖注入
7420
- this.eventModule = null; // 依赖注入
7421
- this.toolMap = new Map(); // 工具缓存
7422
- this.currentTool = null; // 当前激活的工具
7423
- this.currentToolName = null;
7424
- this._isActive = false;
7061
+ class MeasureTool extends BaseTool {
7062
+ constructor(editorModule, options = {}) {
7063
+ super(editorModule, options);
7064
+
7065
+ this._measureType = options.measureType || MeasureType.DISTANCE;
7066
+ this._positions = []; // 已点击的经纬度点
7067
+ this._mousePos = null; // 当前鼠标位置(经纬度)
7068
+ this._isMeasuring = false; // 是否正在一次测量中
7069
+ this._lastResult = null; // 最后一次测量结果
7070
+
7071
+ // 临时图元引用
7072
+ this._pointEntities = []; // 点击点标记
7073
+ this._previewLine = null; // 测距预览线
7074
+ this._previewPolygon = null; // 测面积预览面
7075
+ this._previewOutline = null; // 测面积预览轮廓线
7076
+ this._previewCartesians = []; // 当前预览几何的笛卡尔坐标数组(CallbackProperty 共享引用)
7077
+ this._segmentLabelEntities = []; // 测距每段标注
7078
+ this._totalLabelEntity = null; // 总距离 / 面积标注
7079
+
7080
+ // 已完成的测量结果图元分组,支持一次激活中保留多个测量结果
7081
+ this._finishedResults = [];
7425
7082
  }
7426
7083
 
7427
7084
  /**
7428
- * 设置依赖模块
7429
- * @param {object} deps 依赖模块 { graphicModule, eventModule }
7085
+ * 设置测量类型
7086
+ * @param {string} type MeasureType
7430
7087
  */
7431
- setDependencies(deps) {
7432
- this.graphicModule = deps.graphicModule;
7433
- this.eventModule = deps.eventModule;
7088
+ setMeasureType(type) {
7089
+ this._measureType = type;
7434
7090
  return this;
7435
7091
  }
7436
7092
 
7437
7093
  /**
7438
- * 初始化编辑器,注册所有内置工具
7094
+ * 获取当前测量类型
7439
7095
  */
7440
- init() {
7441
- // 注册内置工具
7442
- this.registerTool(ToolName.DRAW, new DrawTool(this));
7443
- this.registerTool(ToolName.EDIT, new EditTool(this));
7444
- this.registerTool(ToolName.SELECT, new SelectTool(this));
7445
- this.registerTool(ToolName.DELETE, new DeleteTool(this));
7096
+ getMeasureType() {
7097
+ return this._measureType;
7098
+ }
7099
+
7100
+ /**
7101
+ * 获取最后一次测量结果
7102
+ */
7103
+ getLastResult() {
7104
+ return this._lastResult;
7105
+ }
7106
+
7107
+ onActivate() {
7108
+ this.registerObserver('mouse:click', this._onClick.bind(this));
7109
+ this.registerObserver('mouse:move', this._onMove.bind(this));
7110
+ this.registerObserver('mouse:rightclick', this._onRightClick.bind(this));
7111
+ this.registerObserver('keyboard:keydown', this._onKeyDown.bind(this));
7112
+ }
7113
+
7114
+ onDeactivate() {
7115
+ this.clearMeasurements();
7116
+ }
7117
+
7118
+ _onClick(lngLat) {
7119
+ if (!lngLat) return;
7120
+
7121
+ this._isMeasuring = true;
7122
+ this._positions.push(lngLat);
7123
+ this._addPointMarker(lngLat);
7124
+ this._updateMeasurement();
7125
+ }
7126
+
7127
+ _onMove(data) {
7128
+ const screenPos = data.position || data.endPosition;
7129
+ if (!screenPos) return;
7130
+
7131
+ const lonLat = this.screenToLonLat({ x: screenPos.x, y: screenPos.y });
7132
+ if (!lonLat) return;
7133
+
7134
+ this._mousePos = lonLat;
7135
+ if (this._isMeasuring) {
7136
+ this._updateMeasurement();
7137
+ }
7138
+ }
7139
+
7140
+ _onRightClick() {
7141
+ this._finishMeasurement();
7142
+ }
7143
+
7144
+ _onKeyDown(data) {
7145
+ if (data.key === 'Escape') {
7146
+ this.cancelMeasurement();
7147
+ return;
7148
+ }
7149
+
7150
+ if (data.key === 'Enter') {
7151
+ this._finishMeasurement();
7152
+ return;
7153
+ }
7154
+
7155
+ if (data.key === 'Backspace' && this._isMeasuring && this._positions.length > 0) {
7156
+ this._positions.pop();
7157
+ const lastPoint = this._pointEntities.pop();
7158
+ if (lastPoint) {
7159
+ this.removeTempEntity(lastPoint);
7160
+ }
7161
+ this._updateMeasurement();
7162
+ }
7163
+ }
7164
+
7165
+ /**
7166
+ * 完成当前测量
7167
+ */
7168
+ _finishMeasurement() {
7169
+ const minPoints = this._measureType === MeasureType.AREA ? 3 : 2;
7170
+ if (this._positions.length < minPoints) return;
7171
+
7172
+ this._isMeasuring = false;
7173
+ this._mousePos = null;
7174
+ this._updateMeasurement();
7175
+
7176
+ const result = this._computeResult(this._positions);
7177
+ this._lastResult = result;
7178
+
7179
+ // 将当前测量结果保留在场景中,并清空工作态以便继续下一次测量
7180
+ this._commitCurrentResult();
7181
+
7182
+ this._emitMeasureComplete(result);
7183
+ }
7184
+
7185
+ /**
7186
+ * 把当前测量图元归档为已完成结果,重置工作态
7187
+ */
7188
+ _commitCurrentResult() {
7189
+ const finalCartesians = this._previewCartesians.slice();
7190
+
7191
+ // 将动态 CallbackProperty 替换为常量几何,避免归档后随 _previewCartesians 变化而消失
7192
+ if (this._previewLine) {
7193
+ this._previewLine.polyline.positions = finalCartesians.length > 0 ? finalCartesians : [];
7194
+ }
7195
+ if (this._previewPolygon) {
7196
+ this._previewPolygon.polygon.hierarchy = finalCartesians.length >= 3
7197
+ ? new Cesium$6.PolygonHierarchy(finalCartesians)
7198
+ : new Cesium$6.PolygonHierarchy([]);
7199
+ }
7200
+ if (this._previewOutline) {
7201
+ this._previewOutline.polyline.positions = finalCartesians.length >= 3
7202
+ ? [...finalCartesians, finalCartesians[0]]
7203
+ : [];
7204
+ }
7205
+
7206
+ this._finishedResults.push({
7207
+ pointEntities: this._pointEntities,
7208
+ segmentLabels: this._segmentLabelEntities,
7209
+ totalLabel: this._totalLabelEntity,
7210
+ previewLine: this._previewLine,
7211
+ previewPolygon: this._previewPolygon,
7212
+ previewOutline: this._previewOutline
7213
+ });
7214
+
7215
+ this._positions = [];
7216
+ this._mousePos = null;
7217
+ this._isMeasuring = false;
7218
+ this._previewCartesians = [];
7219
+
7220
+ this._pointEntities = [];
7221
+ this._segmentLabelEntities = [];
7222
+ this._totalLabelEntity = null;
7223
+ this._previewLine = null;
7224
+ this._previewPolygon = null;
7225
+ this._previewOutline = null;
7226
+ }
7227
+
7228
+ /**
7229
+ * 取消当前测量
7230
+ */
7231
+ cancelMeasurement() {
7232
+ this.clearMeasurements();
7233
+ this._emitMeasureCancel();
7234
+ }
7235
+
7236
+ /**
7237
+ * 清空所有测量结果与临时图元
7238
+ */
7239
+ clearMeasurements() {
7240
+ this._positions = [];
7241
+ this._mousePos = null;
7242
+ this._isMeasuring = false;
7243
+ this._lastResult = null;
7244
+ this._previewCartesians = [];
7245
+
7246
+ this._pointEntities.forEach(e => this.removeTempEntity(e));
7247
+ this._pointEntities = [];
7248
+
7249
+ this._segmentLabelEntities.forEach(e => this.removeTempEntity(e));
7250
+ this._segmentLabelEntities = [];
7251
+
7252
+ if (this._totalLabelEntity) {
7253
+ this.removeTempEntity(this._totalLabelEntity);
7254
+ this._totalLabelEntity = null;
7255
+ }
7256
+
7257
+ if (this._previewLine) {
7258
+ this.removeTempEntity(this._previewLine);
7259
+ this._previewLine = null;
7260
+ }
7261
+
7262
+ if (this._previewPolygon) {
7263
+ this.removeTempEntity(this._previewPolygon);
7264
+ this._previewPolygon = null;
7265
+ }
7266
+
7267
+ if (this._previewOutline) {
7268
+ this.removeTempEntity(this._previewOutline);
7269
+ this._previewOutline = null;
7270
+ }
7271
+
7272
+ this._finishedResults.forEach(group => {
7273
+ group.pointEntities.forEach(e => this.removeTempEntity(e));
7274
+ group.segmentLabels.forEach(e => this.removeTempEntity(e));
7275
+ if (group.totalLabel) this.removeTempEntity(group.totalLabel);
7276
+ if (group.previewLine) this.removeTempEntity(group.previewLine);
7277
+ if (group.previewPolygon) this.removeTempEntity(group.previewPolygon);
7278
+ if (group.previewOutline) this.removeTempEntity(group.previewOutline);
7279
+ });
7280
+ this._finishedResults = [];
7281
+ }
7282
+
7283
+ /**
7284
+ * 更新预览几何与标注
7285
+ */
7286
+ _updateMeasurement() {
7287
+ if (this._positions.length === 0) return;
7288
+
7289
+ const positions = [...this._positions];
7290
+ if (this._isMeasuring && this._mousePos) {
7291
+ positions.push(this._mousePos);
7292
+ }
7293
+
7294
+ if (this._measureType === MeasureType.DISTANCE) {
7295
+ this._updateDistancePreview(positions);
7296
+ } else {
7297
+ this._updateAreaPreview(positions);
7298
+ }
7299
+
7300
+ if (this._isMeasuring) {
7301
+ this._emitMeasureProgress(this._computeResult(positions));
7302
+ }
7303
+ }
7304
+
7305
+ _updateDistancePreview(positions) {
7306
+ const cartesians = positions.length >= 2
7307
+ ? positions.map(p => Cesium$6.Cartesian3.fromDegrees(...p))
7308
+ : [];
7309
+ this._previewCartesians = cartesians;
7310
+
7311
+ // 预览线
7312
+ if (!this._previewLine) {
7313
+ this._previewLine = this.createTempEntity(this.generateId('measure_line'), {
7314
+ polyline: {
7315
+ positions: new Cesium$6.CallbackProperty(() => this._previewCartesians, false),
7316
+ material: Cesium$6.Color.YELLOW,
7317
+ width: 2,
7318
+ clampToGround: true
7319
+ }
7320
+ });
7321
+ }
7322
+ this._previewLine.show = cartesians.length >= 2;
7323
+
7324
+ // 每段标注
7325
+ const segmentCount = Math.max(0, positions.length - 1);
7326
+ this._ensureSegmentLabels(segmentCount);
7327
+
7328
+ let total = 0;
7329
+ for (let i = 0; i < segmentCount; i++) {
7330
+ const p1 = positions[i];
7331
+ const p2 = positions[i + 1];
7332
+ const dist = GeoCalcUtil.calcDistance(p1, p2);
7333
+ total += dist;
7334
+
7335
+ const mid = Cesium$6.Cartesian3.midpoint(
7336
+ Cesium$6.Cartesian3.fromDegrees(...p1),
7337
+ Cesium$6.Cartesian3.fromDegrees(...p2),
7338
+ new Cesium$6.Cartesian3()
7339
+ );
7340
+
7341
+ const label = this._segmentLabelEntities[i];
7342
+ label.position = mid;
7343
+ label.label.text = this._formatDistance(dist).text;
7344
+ label.show = true;
7345
+ }
7346
+
7347
+ // 总距离标注
7348
+ const lastCartesian = cartesians[cartesians.length - 1];
7349
+ if (positions.length >= 2) {
7350
+ if (!this._totalLabelEntity) {
7351
+ this._totalLabelEntity = this._createLabelEntity(
7352
+ this.generateId('measure_total'),
7353
+ lastCartesian,
7354
+ `总长:${this._formatDistance(total).text}`
7355
+ );
7356
+ } else {
7357
+ this._totalLabelEntity.position = lastCartesian;
7358
+ this._totalLabelEntity.label.text = `总长:${this._formatDistance(total).text}`;
7359
+ this._totalLabelEntity.show = true;
7360
+ }
7361
+ } else if (this._totalLabelEntity) {
7362
+ this._totalLabelEntity.show = false;
7363
+ }
7364
+ }
7365
+
7366
+ _updateAreaPreview(positions) {
7367
+ const cartesians = positions.map(p => Cesium$6.Cartesian3.fromDegrees(...p));
7368
+ this._previewCartesians = cartesians;
7369
+
7370
+ if (positions.length >= 3) {
7371
+ // 显示面 + 闭合轮廓
7372
+ if (!this._previewPolygon) {
7373
+ this._previewPolygon = this.createTempEntity(this.generateId('measure_polygon'), {
7374
+ polygon: {
7375
+ hierarchy: new Cesium$6.CallbackProperty(() => new Cesium$6.PolygonHierarchy(this._previewCartesians), false),
7376
+ material: Cesium$6.Color.YELLOW.withAlpha(0.2),
7377
+ outline: false
7378
+ }
7379
+ });
7380
+ this._previewOutline = this.createTempEntity(this.generateId('measure_outline'), {
7381
+ polyline: {
7382
+ positions: new Cesium$6.CallbackProperty(() => this._previewCartesians.length > 0
7383
+ ? [...this._previewCartesians, this._previewCartesians[0]]
7384
+ : [], false),
7385
+ material: Cesium$6.Color.YELLOW,
7386
+ width: 2,
7387
+ clampToGround: true
7388
+ }
7389
+ });
7390
+ }
7391
+ this._previewPolygon.show = true;
7392
+ this._previewOutline.show = true;
7393
+ if (this._previewLine) {
7394
+ this._previewLine.show = false;
7395
+ }
7396
+
7397
+ const area = GeoCalcUtil.calcArea(positions);
7398
+ const centroid = this._computeCentroid(cartesians);
7399
+
7400
+ if (!this._totalLabelEntity) {
7401
+ this._totalLabelEntity = this._createLabelEntity(
7402
+ this.generateId('measure_area'),
7403
+ centroid,
7404
+ `面积:${this._formatArea(area).text}`
7405
+ );
7406
+ } else {
7407
+ this._totalLabelEntity.position = centroid;
7408
+ this._totalLabelEntity.label.text = `面积:${this._formatArea(area).text}`;
7409
+ this._totalLabelEntity.show = true;
7410
+ }
7411
+ } else {
7412
+ // 不足 3 点时只显示连线,并隐藏已有的面、轮廓、面积标注
7413
+ if (this._previewPolygon) {
7414
+ this._previewPolygon.show = false;
7415
+ }
7416
+ if (this._previewOutline) {
7417
+ this._previewOutline.show = false;
7418
+ }
7419
+ if (this._totalLabelEntity) {
7420
+ this._totalLabelEntity.show = false;
7421
+ }
7422
+
7423
+ if (!this._previewLine) {
7424
+ this._previewLine = this.createTempEntity(this.generateId('measure_line'), {
7425
+ polyline: {
7426
+ positions: new Cesium$6.CallbackProperty(() => this._previewCartesians, false),
7427
+ material: Cesium$6.Color.YELLOW,
7428
+ width: 2,
7429
+ clampToGround: true
7430
+ }
7431
+ });
7432
+ }
7433
+ this._previewLine.show = positions.length >= 2;
7434
+ }
7435
+ }
7436
+
7437
+ _ensureSegmentLabels(count) {
7438
+ // 移除多余的标注
7439
+ while (this._segmentLabelEntities.length > count) {
7440
+ const label = this._segmentLabelEntities.pop();
7441
+ this.removeTempEntity(label);
7442
+ }
7443
+
7444
+ // 创建缺少的标注
7445
+ while (this._segmentLabelEntities.length < count) {
7446
+ const label = this._createLabelEntity(
7447
+ this.generateId('measure_segment'),
7448
+ Cesium$6.Cartesian3.ZERO,
7449
+ ''
7450
+ );
7451
+ this._segmentLabelEntities.push(label);
7452
+ }
7453
+ }
7454
+
7455
+ _createLabelEntity(id, position, text) {
7456
+ return this.createTempEntity(id, {
7457
+ position,
7458
+ label: {
7459
+ text,
7460
+ font: '14px Microsoft YaHei',
7461
+ fillColor: Cesium$6.Color.WHITE,
7462
+ outlineColor: Cesium$6.Color.BLACK,
7463
+ outlineWidth: 2,
7464
+ style: Cesium$6.LabelStyle.FILL_AND_OUTLINE,
7465
+ verticalOrigin: Cesium$6.VerticalOrigin.BOTTOM,
7466
+ horizontalOrigin: Cesium$6.HorizontalOrigin.CENTER,
7467
+ heightReference: Cesium$6.HeightReference.CLAMP_TO_GROUND,
7468
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
7469
+ pixelOffset: new Cesium$6.Cartesian2(0, -10)
7470
+ }
7471
+ });
7472
+ }
7473
+
7474
+ _addPointMarker(lonLat) {
7475
+ const point = this.createTempEntity(this.generateId('measure_point'), {
7476
+ position: Cesium$6.Cartesian3.fromDegrees(...lonLat),
7477
+ point: {
7478
+ color: Cesium$6.Color.YELLOW,
7479
+ pixelSize: 8,
7480
+ outlineColor: Cesium$6.Color.BLACK,
7481
+ outlineWidth: 1,
7482
+ heightReference: Cesium$6.HeightReference.CLAMP_TO_GROUND,
7483
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
7484
+ }
7485
+ });
7486
+ this._pointEntities.push(point);
7487
+ }
7488
+
7489
+ _computeResult(positions) {
7490
+ if (this._measureType === MeasureType.DISTANCE) {
7491
+ let total = 0;
7492
+ for (let i = 0; i < positions.length - 1; i++) {
7493
+ total += GeoCalcUtil.calcDistance(positions[i], positions[i + 1]);
7494
+ }
7495
+ const fmt = this._formatDistance(total);
7496
+ return {
7497
+ type: MeasureType.DISTANCE,
7498
+ value: total,
7499
+ unit: fmt.unit,
7500
+ formatted: fmt.text,
7501
+ positions: positions.slice()
7502
+ };
7503
+ }
7504
+
7505
+ const area = GeoCalcUtil.calcArea(positions);
7506
+ const fmt = this._formatArea(area);
7507
+ return {
7508
+ type: MeasureType.AREA,
7509
+ value: area,
7510
+ unit: fmt.unit,
7511
+ formatted: fmt.text,
7512
+ positions: positions.slice()
7513
+ };
7514
+ }
7515
+
7516
+ _computeCentroid(cartesians) {
7517
+ const center = new Cesium$6.Cartesian3(0, 0, 0);
7518
+ for (const c of cartesians) {
7519
+ Cesium$6.Cartesian3.add(center, c, center);
7520
+ }
7521
+ return Cesium$6.Cartesian3.multiplyByScalar(center, 1 / cartesians.length, center);
7522
+ }
7523
+
7524
+ _formatDistance(meters) {
7525
+ if (meters >= 1000) {
7526
+ return { text: `${(meters / 1000).toFixed(2)} km`, unit: 'km' };
7527
+ }
7528
+ return { text: `${meters.toFixed(2)} m`, unit: 'm' };
7529
+ }
7530
+
7531
+ _formatArea(squareMeters) {
7532
+ if (squareMeters >= 1000000) {
7533
+ return { text: `${(squareMeters / 1000000).toFixed(3)} km²`, unit: 'km²' };
7534
+ }
7535
+ return { text: `${squareMeters.toFixed(2)} m²`, unit: 'm²' };
7536
+ }
7537
+
7538
+ _emitMeasureProgress(result) {
7539
+ if (this._options.onMeasureProgress) {
7540
+ this._options.onMeasureProgress(result);
7541
+ }
7542
+ }
7543
+
7544
+ _emitMeasureComplete(result) {
7545
+ if (this._options.onMeasureComplete) {
7546
+ this._options.onMeasureComplete(result);
7547
+ }
7548
+ }
7549
+
7550
+ _emitMeasureCancel() {
7551
+ if (this._options.onMeasureCancel) {
7552
+ this._options.onMeasureCancel();
7553
+ }
7554
+ }
7555
+ }
7556
+
7557
+ /**
7558
+ * 工具名称枚举
7559
+ */
7560
+ const ToolName = {
7561
+ DRAW: 'draw',
7562
+ EDIT: 'edit',
7563
+ SELECT: 'select',
7564
+ DELETE: 'delete',
7565
+ MEASURE: 'measure'
7566
+ };
7567
+
7568
+ /**
7569
+ * 编辑器模块
7570
+ * 负责工具的注册、切换、激活管理
7571
+ */
7572
+ class EditorModule {
7573
+ constructor(viewer, options = {}) {
7574
+ this.viewer = viewer;
7575
+ this.config = Config.getInstance().getConfig();
7576
+ this.graphicModule = null; // 依赖注入
7577
+ this.eventModule = null; // 依赖注入
7578
+ this.toolMap = new Map(); // 工具缓存
7579
+ this.currentTool = null; // 当前激活的工具
7580
+ this.currentToolName = null;
7581
+ this._isActive = false;
7582
+ }
7583
+
7584
+ /**
7585
+ * 设置依赖模块
7586
+ * @param {object} deps 依赖模块 { graphicModule, eventModule }
7587
+ */
7588
+ setDependencies(deps) {
7589
+ this.graphicModule = deps.graphicModule;
7590
+ this.eventModule = deps.eventModule;
7591
+ return this;
7592
+ }
7593
+
7594
+ /**
7595
+ * 初始化编辑器,注册所有内置工具
7596
+ */
7597
+ init() {
7598
+ // 注册内置工具
7599
+ this.registerTool(ToolName.DRAW, new DrawTool(this));
7600
+ this.registerTool(ToolName.EDIT, new EditTool(this));
7601
+ this.registerTool(ToolName.SELECT, new SelectTool(this));
7602
+ this.registerTool(ToolName.DELETE, new DeleteTool(this));
7446
7603
  this.registerTool(ToolName.MEASURE, new MeasureTool(this));
7447
7604
 
7448
7605
  return this;
@@ -8266,137 +8423,137 @@ class LocationBar extends BaseControl {
8266
8423
  }
8267
8424
  }
8268
8425
 
8269
- const Cesium$4 = getCesium();
8270
-
8271
- /**
8272
- * 缩放控件
8273
- * 提供放大/缩小两个按钮,默认挂到 SDK 统一工具栏(xs3d-viewer-toolbar)。
8274
- */
8275
- class Zoom extends BaseControl {
8276
- /**
8277
- * @param {object} options
8278
- * @param {number} [options.duration=0.5] 缩放动画时长(秒)
8279
- */
8280
- constructor(options = {}) {
8281
- super({
8282
- type: 'zoom',
8283
- ...options
8284
- });
8285
-
8286
- this._duration = options.duration ?? 0.5;
8287
- }
8288
-
8289
- _createContainer() {
8290
- const div = super._createContainer();
8291
- div.className += ' xs3d-zoom';
8292
- // 只有在统一工具栏内才使用 display: contents,让按钮直接参与工具栏 flex 布局;
8293
- // 自定义容器挂载时保留正常 absolute 定位,支持 top/left/right/bottom。
8294
- if (this._parentContainer?.classList?.contains('xs3d-viewer-toolbar')) {
8295
- div.style.display = 'contents';
8296
- }
8297
- return div;
8298
- }
8299
-
8300
- _getParentContainer() {
8301
- if (this._options.container) {
8302
- return super._getParentContainer();
8303
- }
8304
- return this._getOrCreateToolbar();
8305
- }
8306
-
8307
- _mount() {
8308
- this._clearAbsolutePositionIfInToolbar();
8309
-
8310
- this._zoomInBtn = this._createButton('+', '放大');
8311
- this._zoomOutBtn = this._createButton('-', '缩小');
8312
-
8313
- this._container.appendChild(this._zoomInBtn);
8314
- this._container.appendChild(this._zoomOutBtn);
8315
- }
8316
-
8317
- _createButton(label, title) {
8318
- const btn = document.createElement('button');
8319
- btn.type = 'button';
8320
- btn.title = title;
8321
- btn.className = 'cesium-button cesium-toolbar-button xs3d-zoom-button';
8322
- btn.textContent = label;
8323
- btn.style.width = '32px';
8324
- btn.style.height = '32px';
8325
- btn.style.lineHeight = '1';
8326
- btn.style.fontSize = '18px';
8327
- btn.style.fontWeight = 'bold';
8328
- btn.style.display = 'flex';
8329
- btn.style.alignItems = 'center';
8330
- btn.style.justifyContent = 'center';
8331
- btn.style.cursor = 'pointer';
8332
- // 在统一工具栏内按 order 排列:指南针 -1,放大 1,缩小 2
8333
- btn.style.order = label === '+' ? '1' : '2';
8334
- return btn;
8335
- }
8336
-
8337
- _bindEvents() {
8338
- this._addDomEvent(this._zoomInBtn, 'click', (e) => {
8339
- e.stopPropagation();
8340
- this._zoomIn();
8341
- });
8342
-
8343
- this._addDomEvent(this._zoomOutBtn, 'click', (e) => {
8344
- e.stopPropagation();
8345
- this._zoomOut();
8346
- });
8347
- }
8348
-
8349
- _zoomIn() {
8350
- this._zoomBy(1);
8351
- }
8352
-
8353
- _zoomOut() {
8354
- this._zoomBy(-1);
8355
- }
8356
-
8357
- /**
8358
- * 按地图层级步进缩放
8359
- * @param {number} levelDelta 层级变化量,+1 放大一级,-1 缩小一级
8360
- */
8361
- _zoomBy(levelDelta) {
8362
- const camera = this._viewer.camera;
8363
- const cartographic = camera.positionCartographic;
8364
- const currentLevel = this._getZoomLevel();
8365
- const targetLevel = Math.max(0, Math.min(24, currentLevel + levelDelta));
8366
- const targetHeight = this._getHeightByZoomLevel(targetLevel);
8367
-
8368
- camera.flyTo({
8369
- destination: Cesium$4.Cartesian3.fromRadians(
8370
- cartographic.longitude,
8371
- cartographic.latitude,
8372
- targetHeight
8373
- ),
8374
- orientation: {
8375
- heading: camera.heading,
8376
- pitch: camera.pitch,
8377
- roll: camera.roll
8378
- },
8379
- duration: this._duration
8380
- });
8381
- }
8382
-
8383
- _getZoomLevel() {
8384
- const positionCartographic = this._viewer.camera.positionCartographic;
8385
- const surfaceCartesian = Cesium$4.Cartesian3.fromRadians(
8386
- positionCartographic.longitude,
8387
- positionCartographic.latitude,
8388
- 0
8389
- );
8390
- const distance = Cesium$4.Cartesian3.distance(this._viewer.camera.position, surfaceCartesian);
8391
- if (!distance || distance <= 0 || !Number.isFinite(distance)) return 0;
8392
- const equatorCircumference = 6378137 * 2 * Math.PI;
8393
- return Math.max(0, Math.floor(Math.log2(equatorCircumference / distance)) + 1);
8394
- }
8395
-
8396
- _getHeightByZoomLevel(level) {
8397
- const equatorCircumference = 6378137 * 2 * Math.PI;
8398
- return equatorCircumference / Math.pow(2, Math.max(0, level - 1));
8399
- }
8426
+ const Cesium$4 = getCesium();
8427
+
8428
+ /**
8429
+ * 缩放控件
8430
+ * 提供放大/缩小两个按钮,默认挂到 SDK 统一工具栏(xs3d-viewer-toolbar)。
8431
+ */
8432
+ class Zoom extends BaseControl {
8433
+ /**
8434
+ * @param {object} options
8435
+ * @param {number} [options.duration=0.5] 缩放动画时长(秒)
8436
+ */
8437
+ constructor(options = {}) {
8438
+ super({
8439
+ type: 'zoom',
8440
+ ...options
8441
+ });
8442
+
8443
+ this._duration = options.duration ?? 0.5;
8444
+ }
8445
+
8446
+ _createContainer() {
8447
+ const div = super._createContainer();
8448
+ div.className += ' xs3d-zoom';
8449
+ // 只有在统一工具栏内才使用 display: contents,让按钮直接参与工具栏 flex 布局;
8450
+ // 自定义容器挂载时保留正常 absolute 定位,支持 top/left/right/bottom。
8451
+ if (this._parentContainer?.classList?.contains('xs3d-viewer-toolbar')) {
8452
+ div.style.display = 'contents';
8453
+ }
8454
+ return div;
8455
+ }
8456
+
8457
+ _getParentContainer() {
8458
+ if (this._options.container) {
8459
+ return super._getParentContainer();
8460
+ }
8461
+ return this._getOrCreateToolbar();
8462
+ }
8463
+
8464
+ _mount() {
8465
+ this._clearAbsolutePositionIfInToolbar();
8466
+
8467
+ this._zoomInBtn = this._createButton('+', '放大');
8468
+ this._zoomOutBtn = this._createButton('-', '缩小');
8469
+
8470
+ this._container.appendChild(this._zoomInBtn);
8471
+ this._container.appendChild(this._zoomOutBtn);
8472
+ }
8473
+
8474
+ _createButton(label, title) {
8475
+ const btn = document.createElement('button');
8476
+ btn.type = 'button';
8477
+ btn.title = title;
8478
+ btn.className = 'cesium-button cesium-toolbar-button xs3d-zoom-button';
8479
+ btn.textContent = label;
8480
+ btn.style.width = '32px';
8481
+ btn.style.height = '32px';
8482
+ btn.style.lineHeight = '1';
8483
+ btn.style.fontSize = '18px';
8484
+ btn.style.fontWeight = 'bold';
8485
+ btn.style.display = 'flex';
8486
+ btn.style.alignItems = 'center';
8487
+ btn.style.justifyContent = 'center';
8488
+ btn.style.cursor = 'pointer';
8489
+ // 在统一工具栏内按 order 排列:指南针 -1,放大 1,缩小 2
8490
+ btn.style.order = label === '+' ? '1' : '2';
8491
+ return btn;
8492
+ }
8493
+
8494
+ _bindEvents() {
8495
+ this._addDomEvent(this._zoomInBtn, 'click', (e) => {
8496
+ e.stopPropagation();
8497
+ this._zoomIn();
8498
+ });
8499
+
8500
+ this._addDomEvent(this._zoomOutBtn, 'click', (e) => {
8501
+ e.stopPropagation();
8502
+ this._zoomOut();
8503
+ });
8504
+ }
8505
+
8506
+ _zoomIn() {
8507
+ this._zoomBy(1);
8508
+ }
8509
+
8510
+ _zoomOut() {
8511
+ this._zoomBy(-1);
8512
+ }
8513
+
8514
+ /**
8515
+ * 按地图层级步进缩放
8516
+ * @param {number} levelDelta 层级变化量,+1 放大一级,-1 缩小一级
8517
+ */
8518
+ _zoomBy(levelDelta) {
8519
+ const camera = this._viewer.camera;
8520
+ const cartographic = camera.positionCartographic;
8521
+ const currentLevel = this._getZoomLevel();
8522
+ const targetLevel = Math.max(0, Math.min(24, currentLevel + levelDelta));
8523
+ const targetHeight = this._getHeightByZoomLevel(targetLevel);
8524
+
8525
+ camera.flyTo({
8526
+ destination: Cesium$4.Cartesian3.fromRadians(
8527
+ cartographic.longitude,
8528
+ cartographic.latitude,
8529
+ targetHeight
8530
+ ),
8531
+ orientation: {
8532
+ heading: camera.heading,
8533
+ pitch: camera.pitch,
8534
+ roll: camera.roll
8535
+ },
8536
+ duration: this._duration
8537
+ });
8538
+ }
8539
+
8540
+ _getZoomLevel() {
8541
+ const positionCartographic = this._viewer.camera.positionCartographic;
8542
+ const surfaceCartesian = Cesium$4.Cartesian3.fromRadians(
8543
+ positionCartographic.longitude,
8544
+ positionCartographic.latitude,
8545
+ 0
8546
+ );
8547
+ const distance = Cesium$4.Cartesian3.distance(this._viewer.camera.position, surfaceCartesian);
8548
+ if (!distance || distance <= 0 || !Number.isFinite(distance)) return 0;
8549
+ const equatorCircumference = 6378137 * 2 * Math.PI;
8550
+ return Math.max(0, Math.floor(Math.log2(equatorCircumference / distance)) + 1);
8551
+ }
8552
+
8553
+ _getHeightByZoomLevel(level) {
8554
+ const equatorCircumference = 6378137 * 2 * Math.PI;
8555
+ return equatorCircumference / Math.pow(2, Math.max(0, level - 1));
8556
+ }
8400
8557
  }
8401
8558
 
8402
8559
  const Cesium$3 = getCesium();