|
|
|
@ -11,6 +11,26 @@ |
|
|
|
</div> --> |
|
|
|
|
|
|
|
<!-- 工具栏内容 --> |
|
|
|
<div class="location-panel"> |
|
|
|
<div class="input-group"> |
|
|
|
<input |
|
|
|
type="number" |
|
|
|
v-model.number="targetLng" |
|
|
|
placeholder="经度 (例如 116.40)" |
|
|
|
step="0.000001" |
|
|
|
> |
|
|
|
<input |
|
|
|
type="number" |
|
|
|
v-model.number="targetLat" |
|
|
|
placeholder="纬度 (例如 39.90)" |
|
|
|
step="0.000001" |
|
|
|
> |
|
|
|
<button @click="flyToLocation" class="tool-btn location-btn"> |
|
|
|
<span class="icon">🚀</span> |
|
|
|
定位 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="toolbar-content" v-show="!isToolbarCollapsed"> |
|
|
|
<div class="toolbar-group"> |
|
|
|
<button |
|
|
|
@ -209,6 +229,11 @@ export default { |
|
|
|
scaleBar: null, |
|
|
|
isToolbarCollapsed: false, |
|
|
|
|
|
|
|
// ================== 新增:定位数据 ================== |
|
|
|
targetLng: 116.3974, // 默认经度 (北京) |
|
|
|
targetLat: 39.9093, // 默认纬度 |
|
|
|
// ================================================= |
|
|
|
|
|
|
|
// 绘制相关 |
|
|
|
drawingMode: null, // 'point', 'line', 'polygon', 'rectangle', 'circle' |
|
|
|
drawingHandler: null, |
|
|
|
@ -365,25 +390,26 @@ export default { |
|
|
|
|
|
|
|
loadOfflineMap() { |
|
|
|
this.viewer.imageryLayers.removeAll() |
|
|
|
|
|
|
|
try { |
|
|
|
const offlineProvider = new Cesium.UrlTemplateImageryProvider({ |
|
|
|
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', |
|
|
|
// 修改部分:将原来的 ArcGIS 卫星图替换为高德矢量行政图 |
|
|
|
const administrativeMap = new Cesium.UrlTemplateImageryProvider({ |
|
|
|
// 高德地图矢量瓦片服务(纯 2D 行政图风格) |
|
|
|
url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', |
|
|
|
minimumLevel: 0, |
|
|
|
maximumLevel: 18, |
|
|
|
tileWidth: 256, |
|
|
|
tileHeight: 256, |
|
|
|
tilingScheme: new Cesium.WebMercatorTilingScheme(), |
|
|
|
credit: '离线地图' |
|
|
|
// 高德不需要专门的 TilingScheme,默认即可,或者使用 WebMercator |
|
|
|
tilingScheme: new Cesium.WebMercatorTilingScheme() |
|
|
|
}) |
|
|
|
|
|
|
|
this.viewer.imageryLayers.addImageryProvider(offlineProvider) |
|
|
|
this.viewer.imageryLayers.addImageryProvider(administrativeMap) |
|
|
|
|
|
|
|
// 如果需要叠加路网或注记,可以在这里再 add 一个图层 |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error('加载离线地图失败:', error) |
|
|
|
console.error('加载地图失败:', error) |
|
|
|
this.showGridLayer() |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
showGridLayer() { |
|
|
|
const gridProvider = new Cesium.GridImageryProvider() |
|
|
|
@ -497,68 +523,94 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
// 绘制线 |
|
|
|
// 绘制线 |
|
|
|
startLineDrawing() { |
|
|
|
this.drawingPoints = []; |
|
|
|
// 记录当前鼠标的实时位置 |
|
|
|
let activeCursorPosition = null; |
|
|
|
|
|
|
|
// 鼠标点击事件 - 添加点 |
|
|
|
// 清除可能存在的旧实体 |
|
|
|
if (this.tempEntity) this.viewer.entities.remove(this.tempEntity); |
|
|
|
if (this.tempPreviewEntity) this.viewer.entities.remove(this.tempPreviewEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
this.tempPreviewEntity = null; |
|
|
|
|
|
|
|
// 1. 鼠标移动事件:仅更新坐标变量,不操作实体 |
|
|
|
this.drawingHandler.setInputAction((movement) => { |
|
|
|
const newPosition = this.getClickPosition(movement.endPosition); |
|
|
|
if (newPosition) { |
|
|
|
activeCursorPosition = newPosition; |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE); |
|
|
|
|
|
|
|
// 2. 鼠标点击事件:确定点位 |
|
|
|
this.drawingHandler.setInputAction((click) => { |
|
|
|
const position = this.getClickPosition(click.position); |
|
|
|
if (position) { |
|
|
|
this.drawingPoints.push(position); |
|
|
|
|
|
|
|
// 如果这是第一个点,创建一个与自身连接的线段(看起来像一个点) |
|
|
|
// === 第一步:点击第一个点后,立即创建“动态虚线” === |
|
|
|
if (this.drawingPoints.length === 1) { |
|
|
|
const previewPositions = [position, position]; |
|
|
|
|
|
|
|
if (this.tempPreviewEntity) { |
|
|
|
this.viewer.entities.remove(this.tempPreviewEntity); |
|
|
|
} |
|
|
|
activeCursorPosition = position; // 初始化鼠标位置 |
|
|
|
|
|
|
|
// 创建预览虚线(只创建这一次,之后它会自动随数据更新) |
|
|
|
this.tempPreviewEntity = this.viewer.entities.add({ |
|
|
|
polyline: { |
|
|
|
positions: previewPositions, |
|
|
|
// 关键:使用 CallbackProperty 动态获取位置 |
|
|
|
positions: new Cesium.CallbackProperty(() => { |
|
|
|
// 只有当有点且鼠标位置存在时才渲染 |
|
|
|
if (this.drawingPoints.length > 0 && activeCursorPosition) { |
|
|
|
// 获取最后一个已确认的点 |
|
|
|
const lastPoint = this.drawingPoints[this.drawingPoints.length - 1]; |
|
|
|
// 返回 [最后一个点, 当前鼠标位置] |
|
|
|
return [lastPoint, activeCursorPosition]; |
|
|
|
} |
|
|
|
return []; |
|
|
|
}, false), |
|
|
|
width: this.defaultStyles.line.width, |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.line.color).withAlpha(0.5), |
|
|
|
clampToGround: true |
|
|
|
// 虚线材质 |
|
|
|
material: new Cesium.PolylineDashMaterialProperty({ |
|
|
|
color: Cesium.Color.fromCssColorString(this.defaultStyles.line.color), |
|
|
|
dashLength: 16 |
|
|
|
}), |
|
|
|
clampToGround: true // 贴地 |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
// === 第二步:点击后续点时,绘制/延长“固定实线” === |
|
|
|
else { |
|
|
|
// 移除旧的实线,重新画包含新点的实线 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity); |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
|
|
|
|
|
|
|
// 鼠标移动事件 - 实时更新预览线段 |
|
|
|
this.drawingHandler.setInputAction((movement) => { |
|
|
|
if (this.drawingPoints.length > 0) { |
|
|
|
const mousePosition = this.getClickPosition(movement.endPosition); |
|
|
|
if (mousePosition) { |
|
|
|
// 创建包含当前鼠标位置的预览线段 |
|
|
|
const previewPositions = [...this.drawingPoints, mousePosition]; |
|
|
|
|
|
|
|
// 如果已有预览线段,先移除 |
|
|
|
if (this.tempPreviewEntity) { |
|
|
|
this.viewer.entities.remove(this.tempPreviewEntity); |
|
|
|
} |
|
|
|
|
|
|
|
// 创建预览线段(半透明效果),显示实时轨迹 |
|
|
|
this.tempPreviewEntity = this.viewer.entities.add({ |
|
|
|
this.tempEntity = this.viewer.entities.add({ |
|
|
|
polyline: { |
|
|
|
positions: previewPositions, |
|
|
|
positions: this.drawingPoints, |
|
|
|
width: this.defaultStyles.line.width, |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.line.color).withAlpha(0.5), // 半透明效果 |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.line.color), |
|
|
|
clampToGround: true |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE); |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
|
|
|
|
|
|
|
// 右键完成绘制 |
|
|
|
// 3. 右键完成绘制 |
|
|
|
this.drawingHandler.setInputAction(() => { |
|
|
|
// 完成绘制前,移除那条动态虚线 |
|
|
|
if (this.tempPreviewEntity) { |
|
|
|
this.viewer.entities.remove(this.tempPreviewEntity); |
|
|
|
this.tempPreviewEntity = null; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.drawingPoints.length > 1) { |
|
|
|
this.finishLineDrawing(); |
|
|
|
} else { |
|
|
|
this.cancelDrawing(); |
|
|
|
} |
|
|
|
|
|
|
|
// 重置局部变量 |
|
|
|
activeCursorPosition = null; |
|
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK); |
|
|
|
}, |
|
|
|
|
|
|
|
@ -591,63 +643,84 @@ export default { |
|
|
|
|
|
|
|
// 绘制多边形 |
|
|
|
startPolygonDrawing() { |
|
|
|
this.drawingPoints = []; |
|
|
|
let activeCursorPosition = null; // 存储鼠标实时位置 |
|
|
|
|
|
|
|
// 1. 清理旧实体 |
|
|
|
// 移除之前可能遗留的实体,确保干净的画布 |
|
|
|
if (this.tempEntity) this.viewer.entities.remove(this.tempEntity); |
|
|
|
if (this.tempPreviewEntity) this.viewer.entities.remove(this.tempPreviewEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
this.tempPreviewEntity = null; |
|
|
|
|
|
|
|
// 2. 鼠标移动事件:只负责更新坐标变量,不涉及繁重的绘图逻辑 |
|
|
|
this.drawingHandler.setInputAction((movement) => { |
|
|
|
const newPosition = this.getClickPosition(movement.endPosition); |
|
|
|
if (newPosition) { |
|
|
|
activeCursorPosition = newPosition; |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE); |
|
|
|
|
|
|
|
// 3. 鼠标点击事件:添加关键点 |
|
|
|
this.drawingHandler.setInputAction((click) => { |
|
|
|
const position = this.getClickPosition(click.position) |
|
|
|
const position = this.getClickPosition(click.position); |
|
|
|
if (position) { |
|
|
|
this.drawingPoints.push(position) |
|
|
|
this.drawingPoints.push(position); |
|
|
|
|
|
|
|
// 更新临时多边形 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity) |
|
|
|
} |
|
|
|
// === 关键逻辑:点击第一个点时,创建唯一的“动态多边形” === |
|
|
|
if (this.drawingPoints.length === 1) { |
|
|
|
activeCursorPosition = position; // 初始化鼠标位置 |
|
|
|
|
|
|
|
if (this.drawingPoints.length > 2) { |
|
|
|
// 闭合多边形 |
|
|
|
const polygonPoints = [...this.drawingPoints, this.drawingPoints[0]] |
|
|
|
this.tempEntity = this.viewer.entities.add({ |
|
|
|
// --- 填充面配置 --- |
|
|
|
polygon: { |
|
|
|
hierarchy: new Cesium.PolygonHierarchy(polygonPoints), |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color) |
|
|
|
.withAlpha(this.defaultStyles.polygon.opacity), |
|
|
|
outline: true, |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color), |
|
|
|
outlineWidth: this.defaultStyles.polygon.width |
|
|
|
} |
|
|
|
}) |
|
|
|
} else if (this.drawingPoints.length === 2) { |
|
|
|
// 只有两个点时显示线 |
|
|
|
this.tempEntity = this.viewer.entities.add({ |
|
|
|
// hierarchy 使用 CallbackProperty 实现动态填充 |
|
|
|
hierarchy: new Cesium.CallbackProperty(() => { |
|
|
|
// 组合:已确定的点 + 当前鼠标位置 |
|
|
|
if (activeCursorPosition) { |
|
|
|
return new Cesium.PolygonHierarchy([...this.drawingPoints, activeCursorPosition]); |
|
|
|
} |
|
|
|
return new Cesium.PolygonHierarchy(this.drawingPoints); |
|
|
|
}, false), |
|
|
|
// 使用半透明颜色,方便看到地图底图 |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color).withAlpha(0.5), |
|
|
|
// 确保贴地 |
|
|
|
perPositionHeight: false |
|
|
|
}, |
|
|
|
// --- 边框线配置 --- |
|
|
|
polyline: { |
|
|
|
positions: this.drawingPoints, |
|
|
|
width: this.defaultStyles.polygon.width, |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color), |
|
|
|
// positions 使用 CallbackProperty 实现动态闭合线 |
|
|
|
positions: new Cesium.CallbackProperty(() => { |
|
|
|
if (activeCursorPosition) { |
|
|
|
// 闭合回路:[所有点, 鼠标位置, 回到起点] |
|
|
|
return [...this.drawingPoints, activeCursorPosition, this.drawingPoints[0]]; |
|
|
|
} |
|
|
|
return this.drawingPoints; |
|
|
|
}, false), |
|
|
|
width: this.defaultStyles.line.width, |
|
|
|
// 边框使用虚线,表示正在编辑中 |
|
|
|
material: new Cesium.PolylineDashMaterialProperty({ |
|
|
|
color: Cesium.Color.fromCssColorString(this.defaultStyles.line.color), |
|
|
|
dashLength: 16 |
|
|
|
}), |
|
|
|
clampToGround: true |
|
|
|
} |
|
|
|
}) |
|
|
|
} else if (this.drawingPoints.length === 1) { |
|
|
|
// 只有一个点时显示点 |
|
|
|
this.tempEntity = this.viewer.entities.add({ |
|
|
|
position: this.drawingPoints[0], |
|
|
|
point: { |
|
|
|
pixelSize: 8, |
|
|
|
color: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color) |
|
|
|
} |
|
|
|
}) |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK) |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
|
|
|
|
|
|
|
// 双击完成绘制 |
|
|
|
this.drawingHandler.setInputAction((click) => { |
|
|
|
if (this.drawingPoints.length > 2) { |
|
|
|
this.finishPolygonDrawing() |
|
|
|
// 4. 右键完成绘制 |
|
|
|
this.drawingHandler.setInputAction(() => { |
|
|
|
if (this.drawingPoints.length >= 3) { |
|
|
|
this.finishPolygonDrawing(); // 调用原有的完成逻辑 |
|
|
|
} else { |
|
|
|
this.cancelDrawing(); // 点数不够则取消 |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK) |
|
|
|
|
|
|
|
// 右键取消 |
|
|
|
this.drawingHandler.setInputAction(() => { |
|
|
|
this.cancelDrawing() |
|
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK) |
|
|
|
// 重置状态 |
|
|
|
activeCursorPosition = null; |
|
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK); |
|
|
|
}, |
|
|
|
|
|
|
|
finishPolygonDrawing() { |
|
|
|
@ -665,156 +738,288 @@ export default { |
|
|
|
return entity |
|
|
|
}, |
|
|
|
|
|
|
|
// 绘制矩形 |
|
|
|
// 绘制矩形(优化版:两点定矩形,实时预览) |
|
|
|
startRectangleDrawing() { |
|
|
|
let startPosition = null |
|
|
|
this.drawingPoints = []; // 存储起点和终点 |
|
|
|
let activeCursorPosition = null; // 实时鼠标位置 |
|
|
|
let startPoint = null; // 记录起点 |
|
|
|
|
|
|
|
this.drawingHandler.setInputAction((click) => { |
|
|
|
startPosition = this.getClickPosition(click.position) |
|
|
|
if (startPosition) { |
|
|
|
this.drawingStartPoint = startPosition |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOWN) |
|
|
|
// 1. 清理旧实体 |
|
|
|
if (this.tempEntity) this.viewer.entities.remove(this.tempEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
|
|
|
|
// 2. 鼠标移动事件:更新鼠标位置 |
|
|
|
this.drawingHandler.setInputAction((movement) => { |
|
|
|
if (startPosition) { |
|
|
|
const endPosition = this.getClickPosition(movement.endPosition) |
|
|
|
if (endPosition) { |
|
|
|
// 更新临时矩形 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity) |
|
|
|
const newPosition = this.getClickPosition(movement.endPosition); |
|
|
|
if (newPosition) { |
|
|
|
activeCursorPosition = newPosition; |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE); |
|
|
|
|
|
|
|
const rectangle = this.calculateRectangle(startPosition, endPosition) |
|
|
|
// 3. 鼠标点击事件 |
|
|
|
this.drawingHandler.setInputAction((click) => { |
|
|
|
const position = this.getClickPosition(click.position); |
|
|
|
if (position) { |
|
|
|
// --- 情况A:第一次点击(确定起点) --- |
|
|
|
if (!startPoint) { |
|
|
|
startPoint = position; |
|
|
|
this.drawingPoints.push(startPoint); |
|
|
|
activeCursorPosition = startPoint; // 初始化鼠标位置 |
|
|
|
|
|
|
|
// 创建动态预览矩形 |
|
|
|
this.tempEntity = this.viewer.entities.add({ |
|
|
|
rectangle: { |
|
|
|
coordinates: rectangle, |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color) |
|
|
|
.withAlpha(this.defaultStyles.rectangle.opacity), |
|
|
|
// 关键:使用 CallbackProperty 动态计算矩形范围 |
|
|
|
coordinates: new Cesium.CallbackProperty(() => { |
|
|
|
if (startPoint && activeCursorPosition) { |
|
|
|
// 使用 Cesium 内置工具,根据两个点(对角)自动计算矩形范围 |
|
|
|
return Cesium.Rectangle.fromCartesianArray([startPoint, activeCursorPosition]); |
|
|
|
} |
|
|
|
return Cesium.Rectangle.fromDegrees(0, 0, 0, 0); |
|
|
|
}, false), |
|
|
|
// 样式:半透明填充 + 边框 |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color).withAlpha(0.5), |
|
|
|
outline: true, |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color), |
|
|
|
outlineWidth: this.defaultStyles.rectangle.width |
|
|
|
} |
|
|
|
}) |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.line.color), |
|
|
|
outlineWidth: 2, |
|
|
|
clampToGround: true // 贴地 |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE) |
|
|
|
// --- 情况B:第二次点击(确定终点) --- |
|
|
|
else { |
|
|
|
this.drawingPoints.push(position); |
|
|
|
// 停止监听鼠标移动,因为形状已确定 |
|
|
|
activeCursorPosition = null; |
|
|
|
|
|
|
|
this.drawingHandler.setInputAction(() => { |
|
|
|
if (startPosition && this.tempEntity) { |
|
|
|
this.finishRectangleDrawing(startPosition) |
|
|
|
startPosition = null |
|
|
|
// 调用完成逻辑 |
|
|
|
this.finishRectangleDrawing(); |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_UP) |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
|
|
|
|
|
|
|
// 右键取消 |
|
|
|
// 4. 右键取消 |
|
|
|
this.drawingHandler.setInputAction(() => { |
|
|
|
this.cancelDrawing() |
|
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK) |
|
|
|
this.cancelDrawing(); |
|
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK); |
|
|
|
}, |
|
|
|
|
|
|
|
finishRectangleDrawing(startPosition) { |
|
|
|
if (!this.tempEntity) return |
|
|
|
finishRectangleDrawing() { |
|
|
|
// 1. 获取最终的矩形范围对象 |
|
|
|
const rect = Cesium.Rectangle.fromCartesianArray(this.drawingPoints); |
|
|
|
|
|
|
|
const rectangleCoords = this.tempEntity.rectangle.coordinates.getValue() |
|
|
|
const entity = this.addRectangleEntity(rectangleCoords) |
|
|
|
// 2. 移除动态预览的临时实体 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
} |
|
|
|
|
|
|
|
// 计算面积 |
|
|
|
const area = this.calculateRectangleArea(rectangleCoords) |
|
|
|
// 3. 创建最终显示的静态实体 |
|
|
|
const finalEntity = this.viewer.entities.add({ |
|
|
|
id: 'rectangle-' + new Date().getTime(), // 给个唯一ID |
|
|
|
rectangle: { |
|
|
|
coordinates: rect, |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color).withAlpha(0.5), |
|
|
|
outline: true, |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.line.color), |
|
|
|
outlineWidth: 2, |
|
|
|
clampToGround: true |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 4. 记录到实体列表 |
|
|
|
this.allEntities.push(finalEntity); |
|
|
|
|
|
|
|
// 5. 计算并显示面积 |
|
|
|
const area = this.calculateRectangleArea(rect); |
|
|
|
this.measurementResult = { |
|
|
|
area: area, |
|
|
|
type: 'rectangle' |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
this.stopDrawing() |
|
|
|
return entity |
|
|
|
// 6. 重置状态 |
|
|
|
this.stopDrawing(); |
|
|
|
}, |
|
|
|
|
|
|
|
// 计算矩形面积(辅助方法) |
|
|
|
calculateRectangleArea(rectangle) { |
|
|
|
// 获取地球椭球体对象 |
|
|
|
const ellipsoid = this.viewer.scene.globe.ellipsoid; |
|
|
|
|
|
|
|
// 方法一:使用 Cesium 几何管道计算(更精确,但需要特定模块支持) |
|
|
|
// const geometry = new Cesium.RectangleGeometry({ rectangle: rectangle }); |
|
|
|
// const geometryInstance = Cesium.RectangleGeometry.createGeometry(geometry); |
|
|
|
// ...比较复杂 |
|
|
|
|
|
|
|
// 方法二:使用采样估算(简单且足够精确) |
|
|
|
// 获取矩形的四个角(弧度) |
|
|
|
const west = rectangle.west; |
|
|
|
const south = rectangle.south; |
|
|
|
const east = rectangle.east; |
|
|
|
const north = rectangle.north; |
|
|
|
|
|
|
|
// 计算中心点的纬度 |
|
|
|
const centerLat = (south + north) / 2; |
|
|
|
|
|
|
|
// 计算宽度(东西向距离):使用余弦定理校正纬度影响 |
|
|
|
// 地球半径约为 6378137 米 |
|
|
|
const R = 6378137; |
|
|
|
const width = (east - west) * R * Math.cos(centerLat); |
|
|
|
|
|
|
|
// 计算高度(南北向距离) |
|
|
|
const height = (north - south) * R; |
|
|
|
|
|
|
|
// 面积 = 宽 * 高 |
|
|
|
const area = Math.abs(width * height); |
|
|
|
|
|
|
|
return area; |
|
|
|
}, |
|
|
|
// 绘制圆形 |
|
|
|
startCircleDrawing() { |
|
|
|
let centerPosition = null |
|
|
|
let radius = 0 |
|
|
|
this.drawingPoints = []; // 存储圆心 |
|
|
|
let activeCursorPosition = null; // 实时鼠标位置 |
|
|
|
let centerPoint = null; // 圆心坐标 |
|
|
|
|
|
|
|
this.drawingHandler.setInputAction((click) => { |
|
|
|
centerPosition = this.getClickPosition(click.position) |
|
|
|
if (centerPosition) { |
|
|
|
this.drawingStartPoint = centerPosition |
|
|
|
// 1. 清理旧实体 |
|
|
|
if (this.tempEntity) this.viewer.entities.remove(this.tempEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
|
|
|
|
// 创建中心点 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity) |
|
|
|
// 2. 鼠标移动事件 |
|
|
|
this.drawingHandler.setInputAction((movement) => { |
|
|
|
const newPosition = this.getClickPosition(movement.endPosition); |
|
|
|
if (newPosition) { |
|
|
|
activeCursorPosition = newPosition; |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE); |
|
|
|
|
|
|
|
// 3. 鼠标点击事件 |
|
|
|
this.drawingHandler.setInputAction((click) => { |
|
|
|
const position = this.getClickPosition(click.position); |
|
|
|
if (position) { |
|
|
|
// --- 情况A:第一次点击(确定圆心) --- |
|
|
|
if (!centerPoint) { |
|
|
|
centerPoint = position; |
|
|
|
this.drawingPoints.push(centerPoint); |
|
|
|
activeCursorPosition = centerPoint; |
|
|
|
|
|
|
|
// 创建动态预览圆形 |
|
|
|
this.tempEntity = this.viewer.entities.add({ |
|
|
|
position: centerPosition, |
|
|
|
point: { |
|
|
|
pixelSize: 8, |
|
|
|
color: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color) |
|
|
|
position: centerPoint, // 圆心固定 |
|
|
|
ellipse: { |
|
|
|
// 关键:使用 CallbackProperty 动态计算半径(半长轴和半短轴) |
|
|
|
semiMajorAxis: new Cesium.CallbackProperty(() => { |
|
|
|
if (centerPoint && activeCursorPosition) { |
|
|
|
return Cesium.Cartesian3.distance(centerPoint, activeCursorPosition); |
|
|
|
} |
|
|
|
return 0; |
|
|
|
}, false), |
|
|
|
semiMinorAxis: new Cesium.CallbackProperty(() => { |
|
|
|
if (centerPoint && activeCursorPosition) { |
|
|
|
return Cesium.Cartesian3.distance(centerPoint, activeCursorPosition); |
|
|
|
} |
|
|
|
return 0; |
|
|
|
}, false), |
|
|
|
// 样式设置 |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color).withAlpha(0.5), |
|
|
|
outline: true, |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.line.color), |
|
|
|
outlineWidth: 2, |
|
|
|
// height: 0, // 如果需要贴地可开启或使用 heightReference |
|
|
|
} |
|
|
|
}) |
|
|
|
}); |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOWN) |
|
|
|
// --- 情况B:第二次点击(确定边缘/半径) --- |
|
|
|
else { |
|
|
|
// 记录边缘点(虽然圆只需要圆心和半径,但记录下来方便后续处理) |
|
|
|
this.drawingPoints.push(position); |
|
|
|
activeCursorPosition = null; // 停止动态更新 |
|
|
|
|
|
|
|
this.drawingHandler.setInputAction((movement) => { |
|
|
|
if (centerPosition) { |
|
|
|
const currentPosition = this.getClickPosition(movement.endPosition) |
|
|
|
if (currentPosition) { |
|
|
|
radius = Cesium.Cartesian3.distance(centerPosition, currentPosition) |
|
|
|
// 传递边缘点位置去结束绘制 |
|
|
|
this.finishCircleDrawing(position); |
|
|
|
} |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
|
|
|
|
|
|
|
// 更新临时圆形 |
|
|
|
// 4. 右键取消 |
|
|
|
this.drawingHandler.setInputAction(() => { |
|
|
|
this.cancelDrawing(); |
|
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK); |
|
|
|
}, |
|
|
|
|
|
|
|
finishCircleDrawing(edgePosition) { |
|
|
|
const centerPoint = this.drawingPoints[0]; |
|
|
|
|
|
|
|
// 1. 计算最终半径 |
|
|
|
const radius = Cesium.Cartesian3.distance(centerPoint, edgePosition); |
|
|
|
|
|
|
|
// 2. 移除动态预览实体 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity) |
|
|
|
this.viewer.entities.remove(this.tempEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
} |
|
|
|
|
|
|
|
// 创建圆形多边形 |
|
|
|
const positions = this.calculateCircle(centerPosition, radius, 64) |
|
|
|
this.tempEntity = this.viewer.entities.add({ |
|
|
|
polygon: { |
|
|
|
hierarchy: new Cesium.PolygonHierarchy(positions), |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color) |
|
|
|
.withAlpha(this.defaultStyles.circle.opacity), |
|
|
|
// 3. 创建最终显示的静态实体 |
|
|
|
const finalEntity = this.viewer.entities.add({ |
|
|
|
id: 'circle-' + new Date().getTime(), |
|
|
|
position: centerPoint, |
|
|
|
ellipse: { |
|
|
|
semiMajorAxis: radius, |
|
|
|
semiMinorAxis: radius, |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color).withAlpha(0.5), |
|
|
|
outline: true, |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color), |
|
|
|
outlineWidth: this.defaultStyles.circle.width |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE) |
|
|
|
|
|
|
|
this.drawingHandler.setInputAction(() => { |
|
|
|
if (centerPosition && radius > 0) { |
|
|
|
this.finishCircleDrawing(centerPosition, radius) |
|
|
|
centerPosition = null |
|
|
|
radius = 0 |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.line.color), |
|
|
|
outlineWidth: 2 |
|
|
|
} |
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_UP) |
|
|
|
}); |
|
|
|
|
|
|
|
// 右键取消 |
|
|
|
// this.drawingHandler.setInputAction(() => { |
|
|
|
// this.cancelDrawing() |
|
|
|
// }, Cesium.ScreenSpaceEventType.RIGHT_CLICK) |
|
|
|
}, |
|
|
|
// 4. 记录实体 |
|
|
|
this.allEntities.push(finalEntity); |
|
|
|
|
|
|
|
finishCircleDrawing(center, radius) { |
|
|
|
const positions = this.calculateCircle(center, radius, 64) |
|
|
|
const entity = this.addCircleEntity(center, radius, positions) |
|
|
|
// 5. 计算面积 (π * r²) 并显示 |
|
|
|
// 半径单位是米,面积单位是平方米 |
|
|
|
const area = Math.PI * Math.pow(radius, 2); |
|
|
|
|
|
|
|
// 计算面积 |
|
|
|
const area = Math.PI * radius * radius |
|
|
|
this.measurementResult = { |
|
|
|
radius: radius, // 也可以额外显示半径 |
|
|
|
area: area, |
|
|
|
radius: radius, |
|
|
|
type: 'circle' |
|
|
|
}; |
|
|
|
|
|
|
|
// 6. 结束绘制状态 |
|
|
|
this.stopDrawing(); |
|
|
|
}, |
|
|
|
|
|
|
|
flyToLocation() { |
|
|
|
// 1. 校验输入 |
|
|
|
if (!this.targetLng || !this.targetLat) { |
|
|
|
alert("请输入有效的经度和纬度!"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this.stopDrawing() |
|
|
|
return entity |
|
|
|
const lng = parseFloat(this.targetLng); |
|
|
|
const lat = parseFloat(this.targetLat); |
|
|
|
|
|
|
|
// 简单校验范围 |
|
|
|
if (lng < -180 || lng > 180 || lat < -90 || lat > 90) { |
|
|
|
alert("经纬度超出有效范围!"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 执行飞行 |
|
|
|
if (this.viewer) { |
|
|
|
this.viewer.camera.flyTo({ |
|
|
|
destination: Cesium.Cartesian3.fromDegrees(lng, lat, 5000), // 5000是默认高度,可调整 |
|
|
|
orientation: { |
|
|
|
heading: Cesium.Math.toRadians(0.0), |
|
|
|
pitch: Cesium.Math.toRadians(-90.0), // 俯视 |
|
|
|
roll: 0.0 |
|
|
|
}, |
|
|
|
duration: 2 // 飞行持续时间(秒) |
|
|
|
}); |
|
|
|
|
|
|
|
cancelDrawing() { |
|
|
|
this.stopDrawing() |
|
|
|
this.drawingMode = null |
|
|
|
// 可选:添加一个临时的标记点来指示位置 |
|
|
|
this.addTempMarker(lng, lat); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// ================== 实体创建方法 ================== |
|
|
|
@ -962,72 +1167,37 @@ export default { |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
const entityData = { |
|
|
|
id, |
|
|
|
type: 'rectangle', |
|
|
|
coordinates: coordinates, |
|
|
|
entity: entity, |
|
|
|
color: this.defaultStyles.rectangle.color, |
|
|
|
opacity: this.defaultStyles.rectangle.opacity, |
|
|
|
width: this.defaultStyles.rectangle.width, |
|
|
|
label: `矩形 ${this.entityCounter}` |
|
|
|
} |
|
|
|
|
|
|
|
this.allEntities.push(entityData) |
|
|
|
// 【重要修改】直接把 entity 推入数组,修复清除功能的 bug |
|
|
|
this.allEntities.push(entity) |
|
|
|
|
|
|
|
entity.clickHandler = (e) => { |
|
|
|
this.selectEntity(entityData) |
|
|
|
e.stopPropagation() |
|
|
|
} |
|
|
|
|
|
|
|
return entityData |
|
|
|
}, |
|
|
|
|
|
|
|
addCircleEntity(center, radius, positions) { |
|
|
|
return entity |
|
|
|
}, |
|
|
|
addCircleEntity(center, radius) { |
|
|
|
this.entityCounter++ |
|
|
|
const id = `circle_${this.entityCounter}` |
|
|
|
|
|
|
|
const entity = this.viewer.entities.add({ |
|
|
|
id: id, |
|
|
|
name: `圆形 ${this.entityCounter}`, |
|
|
|
polygon: { |
|
|
|
hierarchy: new Cesium.PolygonHierarchy(positions), |
|
|
|
position: center, // 圆心位置 |
|
|
|
|
|
|
|
// 【优化】使用 ellipse (椭圆) 绘制圆形 |
|
|
|
ellipse: { |
|
|
|
semiMinorAxis: radius, // 半短轴 = 半径 |
|
|
|
semiMajorAxis: radius, // 半长轴 = 半径 |
|
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color) |
|
|
|
.withAlpha(this.defaultStyles.circle.opacity), |
|
|
|
outline: true, |
|
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color), |
|
|
|
outlineWidth: this.defaultStyles.circle.width |
|
|
|
}, |
|
|
|
position: center, |
|
|
|
point: { |
|
|
|
pixelSize: 5, |
|
|
|
color: Cesium.Color.RED |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
const centerLL = this.cartesianToLatLng(center) |
|
|
|
const entityData = { |
|
|
|
id, |
|
|
|
type: 'circle', |
|
|
|
center: centerLL, |
|
|
|
radius: radius, |
|
|
|
positions: positions, |
|
|
|
entity: entity, |
|
|
|
color: this.defaultStyles.circle.color, |
|
|
|
opacity: this.defaultStyles.circle.opacity, |
|
|
|
width: this.defaultStyles.circle.width, |
|
|
|
label: `圆形 ${this.entityCounter}` |
|
|
|
} |
|
|
|
|
|
|
|
this.allEntities.push(entityData) |
|
|
|
|
|
|
|
entity.clickHandler = (e) => { |
|
|
|
this.selectEntity(entityData) |
|
|
|
e.stopPropagation() |
|
|
|
} |
|
|
|
// 【重要修改】直接把 entity 推入数组 |
|
|
|
this.allEntities.push(entity) |
|
|
|
|
|
|
|
return entityData |
|
|
|
}, |
|
|
|
return entity |
|
|
|
}, |
|
|
|
|
|
|
|
// ================== 工具方法 ================== |
|
|
|
|
|
|
|
@ -1206,25 +1376,47 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
clearAll() { |
|
|
|
// 停止绘制 |
|
|
|
this.stopDrawing() |
|
|
|
this.drawingMode = null |
|
|
|
// 1. 检查数组是否有内容 |
|
|
|
if (this.allEntities && this.allEntities.length > 0) { |
|
|
|
|
|
|
|
// 移除所有实体 |
|
|
|
this.allEntities.forEach(entity => { |
|
|
|
if (entity.entity) { |
|
|
|
this.viewer.entities.remove(entity.entity) |
|
|
|
// 2. 遍历每一个对象进行删除 |
|
|
|
this.allEntities.forEach(item => { |
|
|
|
try { |
|
|
|
// 情况 A: 数组里存的是原生的 Cesium Entity (点、线通常是这种情况) |
|
|
|
if (item instanceof Cesium.Entity) { |
|
|
|
this.viewer.entities.remove(item); |
|
|
|
} |
|
|
|
// 情况 B: 数组里存的是包装对象 (你的矩形、圆可能是这种 { entity: ... }) |
|
|
|
else if (item.entity && item.entity instanceof Cesium.Entity) { |
|
|
|
this.viewer.entities.remove(item.entity); |
|
|
|
} |
|
|
|
// 情况 C: 兜底方案,尝试通过 ID 删除 |
|
|
|
else if (item.id) { |
|
|
|
this.viewer.entities.removeById(item.id); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
console.warn('删除实体失败:', e); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
// 清空数组 |
|
|
|
this.allEntities = [] |
|
|
|
this.entityCounter = 0 |
|
|
|
this.selectedEntity = null |
|
|
|
this.measurementResult = null |
|
|
|
// 3. 清空数组 |
|
|
|
this.allEntities = []; |
|
|
|
|
|
|
|
console.log('已清除所有图形') |
|
|
|
}, |
|
|
|
// 4. 清理可能残留的绘制过程中的临时图形 |
|
|
|
if (this.tempEntity) { |
|
|
|
this.viewer.entities.remove(this.tempEntity); |
|
|
|
this.tempEntity = null; |
|
|
|
} |
|
|
|
if (this.tempPreviewEntity) { |
|
|
|
this.viewer.entities.remove(this.tempPreviewEntity); |
|
|
|
this.tempPreviewEntity = null; |
|
|
|
} |
|
|
|
|
|
|
|
// 5. 重置其他状态(如测量面板、绘制状态) |
|
|
|
this.measurementResult = null; |
|
|
|
this.stopDrawing(); |
|
|
|
}, |
|
|
|
|
|
|
|
// ================== 其他方法 ================== |
|
|
|
|
|
|
|
@ -1443,6 +1635,54 @@ export default { |
|
|
|
margin: 5px 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* 定位面板样式 */ |
|
|
|
.location-panel { |
|
|
|
position: absolute; |
|
|
|
top: -80px; |
|
|
|
right: -180px; /* 放在右上角 */ |
|
|
|
z-index: 1000; |
|
|
|
background: rgba(255, 255, 255, 0.95); |
|
|
|
border-radius: 8px; |
|
|
|
padding: 10px; |
|
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.2); |
|
|
|
} |
|
|
|
|
|
|
|
.input-group { |
|
|
|
display: flex; |
|
|
|
gap: 10px; |
|
|
|
align-items: center; |
|
|
|
} |
|
|
|
|
|
|
|
.input-group input { |
|
|
|
width: 100px; |
|
|
|
padding: 6px 10px; |
|
|
|
border: 1px solid #ddd; |
|
|
|
border-radius: 4px; |
|
|
|
outline: none; |
|
|
|
font-size: 14px; |
|
|
|
} |
|
|
|
|
|
|
|
.input-group input:focus { |
|
|
|
border-color: #4dabf7; |
|
|
|
} |
|
|
|
|
|
|
|
.location-btn { |
|
|
|
background: #4dabf7; |
|
|
|
color: white; |
|
|
|
border: none; |
|
|
|
padding: 6px 15px; |
|
|
|
border-radius: 4px; |
|
|
|
cursor: pointer; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 5px; |
|
|
|
transition: background 0.3s; |
|
|
|
} |
|
|
|
|
|
|
|
.location-btn:hover { |
|
|
|
background: #3b8dbd; |
|
|
|
} |
|
|
|
|
|
|
|
/* 原来的工具按钮样式保持不变,但添加展开/收起状态的调整 */ |
|
|
|
.tool-btn { |
|
|
|
padding: 10px 15px; |
|
|
|
@ -1572,3 +1812,5 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
</style> |
|
|
|
|
|
|
|
|
|
|
|
|