You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1574 lines
44 KiB
1574 lines
44 KiB
<template>
|
|
<div class="cesium-container">
|
|
<div id="cesiumViewer" ref="cesiumViewer"></div>
|
|
|
|
<!-- 主工具栏 -->
|
|
<div class="main-toolbar" v-if="drawDomClick" :class="{ collapsed: isToolbarCollapsed }">
|
|
<!-- 展开/收起按钮 -->
|
|
<!-- <div class="collapse-btn" @click="toggleToolbar">
|
|
<span class="collapse-icon">{{ isToolbarCollapsed ? '▶' : '◀' }}</span>
|
|
<span class="collapse-text" v-if="!isToolbarCollapsed">收起</span>
|
|
</div> -->
|
|
|
|
<!-- 工具栏内容 -->
|
|
<div class="toolbar-content" v-show="!isToolbarCollapsed">
|
|
<div class="toolbar-group">
|
|
<button
|
|
@click="toggleDrawing('point')"
|
|
:class="{ active: drawingMode === 'point' }"
|
|
title="点"
|
|
class="tool-btn"
|
|
>
|
|
<span class="icon">📍</span>
|
|
<span class="text">{{ drawingMode === 'point' ? '停止' : '点' }}</span>
|
|
</button>
|
|
|
|
<button
|
|
@click="toggleDrawing('line')"
|
|
:class="{ active: drawingMode === 'line' }"
|
|
title="线"
|
|
class="tool-btn"
|
|
>
|
|
<span class="icon">📏</span>
|
|
<span class="text">{{ drawingMode === 'line' ? '停止' : '线' }}</span>
|
|
</button>
|
|
|
|
<button
|
|
@click="toggleDrawing('polygon')"
|
|
:class="{ active: drawingMode === 'polygon' }"
|
|
title="面"
|
|
class="tool-btn"
|
|
>
|
|
<span class="icon">🔶</span>
|
|
<span class="text">{{ drawingMode === 'polygon' ? '停止' : '面' }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="toolbar-group">
|
|
<button
|
|
@click="toggleDrawing('rectangle')"
|
|
:class="{ active: drawingMode === 'rectangle' }"
|
|
title="矩形"
|
|
class="tool-btn"
|
|
>
|
|
<span class="icon">⬜</span>
|
|
<span class="text">{{ drawingMode === 'rectangle' ? '停止' : '矩形' }}</span>
|
|
</button>
|
|
|
|
<button
|
|
@click="toggleDrawing('circle')"
|
|
:class="{ active: drawingMode === 'circle' }"
|
|
title="圆形"
|
|
class="tool-btn"
|
|
>
|
|
<span class="icon">⭕</span>
|
|
<span class="text">{{ drawingMode === 'circle' ? '停止' : '圆形' }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="toolbar-group">
|
|
<button
|
|
@click="clearAll"
|
|
:disabled="!allEntities.length"
|
|
title="清除所有"
|
|
class="tool-btn danger"
|
|
>
|
|
<span class="icon">🗑️</span>
|
|
<span class="text">清除</span>
|
|
</button>
|
|
|
|
<button
|
|
@click="exportData"
|
|
:disabled="!allEntities.length"
|
|
title="导出数据"
|
|
class="tool-btn success"
|
|
>
|
|
<span class="icon">💾</span>
|
|
<span class="text">导出</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 收起时的最小化工具栏 -->
|
|
<div class="toolbar-minimized" v-show="isToolbarCollapsed">
|
|
<button
|
|
@click="toggleDrawing('point')"
|
|
:class="{ active: drawingMode === 'point' }"
|
|
title="点"
|
|
class="min-tool-btn"
|
|
>
|
|
<span class="icon">📍</span>
|
|
</button>
|
|
<button
|
|
@click="toggleDrawing('line')"
|
|
:class="{ active: drawingMode === 'line' }"
|
|
title="线"
|
|
class="min-tool-btn"
|
|
>
|
|
<span class="icon">📏</span>
|
|
</button>
|
|
<button
|
|
@click="toggleDrawing('polygon')"
|
|
:class="{ active: drawingMode === 'polygon' }"
|
|
title="面"
|
|
class="min-tool-btn"
|
|
>
|
|
<span class="icon">🔶</span>
|
|
</button>
|
|
<button
|
|
@click="toggleDrawing('rectangle')"
|
|
:class="{ active: drawingMode === 'rectangle' }"
|
|
title="矩形"
|
|
class="min-tool-btn"
|
|
>
|
|
<span class="icon">⬜</span>
|
|
</button>
|
|
<button
|
|
@click="toggleDrawing('circle')"
|
|
:class="{ active: drawingMode === 'circle' }"
|
|
title="圆形"
|
|
class="min-tool-btn"
|
|
>
|
|
<span class="icon">⭕</span>
|
|
</button>
|
|
<div class="min-tool-separator"></div>
|
|
<button
|
|
@click="clearAll"
|
|
:disabled="!allEntities.length"
|
|
title="清除所有"
|
|
class="min-tool-btn danger"
|
|
>
|
|
<span class="icon">🗑️</span>
|
|
</button>
|
|
<button
|
|
@click="exportData"
|
|
:disabled="!allEntities.length"
|
|
title="导出数据"
|
|
class="min-tool-btn success"
|
|
>
|
|
<span class="icon">💾</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 测量结果显示 -->
|
|
<div class="measurement-panel" v-if="measurementResult">
|
|
<div class="measurement-content">
|
|
<h5>测量结果</h5>
|
|
<div class="measurement-item" v-if="measurementResult.distance">
|
|
<span>长度:</span>
|
|
<strong>{{ measurementResult.distance.toFixed(2) }} 米</strong>
|
|
</div>
|
|
<div class="measurement-item" v-if="measurementResult.area">
|
|
<span>面积:</span>
|
|
<strong>{{ measurementResult.area.toFixed(2) }} 平方米</strong>
|
|
</div>
|
|
<div class="measurement-item" v-if="measurementResult.radius">
|
|
<span>半径:</span>
|
|
<strong>{{ measurementResult.radius.toFixed(2) }} 米</strong>
|
|
</div>
|
|
<button @click="measurementResult = null" class="close-btn">关闭</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
// import * as Cesium from 'cesium'
|
|
// import 'cesium/Build/Cesium/Widgets/widgets.css'
|
|
export default {
|
|
name: 'CesiumMap',
|
|
props: {
|
|
drawDomClick: {
|
|
type: Boolean,
|
|
default: false,
|
|
// 增加 props 类型校验,方便调试
|
|
validator(val) {
|
|
const isBoolean = typeof val === 'boolean'
|
|
if (!isBoolean) {
|
|
console.error('drawDomClick 必须是布尔值,当前值:', val, '类型:', typeof val)
|
|
}
|
|
return isBoolean
|
|
}
|
|
},
|
|
},
|
|
watch: {
|
|
drawDomClick: {
|
|
immediate: true, // 组件初始化时立即执行一次
|
|
handler(newVal, oldVal) {
|
|
// 可选:如果需要在值变化时执行额外逻辑(比如初始化地图)
|
|
if (newVal) {
|
|
// this.initMap()
|
|
}
|
|
}
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
viewer: null,
|
|
scaleBar: null,
|
|
isToolbarCollapsed: false,
|
|
|
|
// 绘制相关
|
|
drawingMode: null, // 'point', 'line', 'polygon', 'rectangle', 'circle'
|
|
drawingHandler: null,
|
|
tempEntity: null, // 最终实体
|
|
tempPreviewEntity: null, // 预览实体(新增)
|
|
drawingPoints: [],
|
|
drawingStartPoint: null,
|
|
isDrawing: false,
|
|
|
|
// 实体管理
|
|
allEntities: [], // 所有绘制的实体
|
|
entityCounter: 0,
|
|
selectedEntity: null, // 当前选中的实体
|
|
|
|
// 测量结果
|
|
measurementResult: null,
|
|
|
|
// 默认样式
|
|
defaultStyles: {
|
|
point: { color: '#FF0000', size: 12 },
|
|
line: { color: '#00FF00', width: 3 },
|
|
polygon: { color: '#0000FF', opacity: 0.5, width: 2 },
|
|
rectangle: { color: '#FFA500', opacity: 0.3, width: 2 },
|
|
circle: { color: '#800080', opacity: 0.4, width: 2 }
|
|
}
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
console.log(this.drawDomClick,999999)
|
|
// this.initMap()
|
|
this.checkCesiumLoaded()
|
|
},
|
|
|
|
beforeDestroy() {
|
|
this.destroyViewer()
|
|
},
|
|
|
|
methods: {
|
|
// 切换工具栏展开/收起
|
|
toggleToolbar() {
|
|
this.isToolbarCollapsed = !this.isToolbarCollapsed
|
|
},
|
|
checkCesiumLoaded() {
|
|
if (typeof Cesium === 'undefined') {
|
|
console.error('Cesium未加载,请检查CDN链接');
|
|
// 可以设置重试机制
|
|
setTimeout(() => {
|
|
if (typeof Cesium !== 'undefined') {
|
|
this.initMap();
|
|
} else {
|
|
console.error('Cesium加载失败');
|
|
}
|
|
}, 1000);
|
|
} else {
|
|
this.initMap();
|
|
}
|
|
},
|
|
initMap() {
|
|
try {
|
|
// 确保 Cesium 已加载
|
|
// Cesium.buildModuleUrl.setBaseUrl(window.CESIUM_BASE_URL)
|
|
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjN2MzMmE5OS01NGU3LTQzOGQtYjdjZi1mNGIwZTFjZjQ0NmEiLCJpZCI6MTQ0MDc2LCJpYXQiOjE2ODU3NjY1OTN9.iCmFY-5WNdvyAT-EO2j-unrFm4ZN9J6aSuB2wElQZ-I'
|
|
|
|
this.viewer = new Cesium.Viewer('cesiumViewer', {
|
|
animation: false,
|
|
fullscreenButton: false,
|
|
baseLayerPicker: false,
|
|
navigationInstructionsInitiallyVisible: false,
|
|
geocoder: false,
|
|
homeButton: false,
|
|
infoBox: false,
|
|
sceneModePicker: false,
|
|
selectionIndicator: false,
|
|
timeline: false,
|
|
navigationHelpButton: false,
|
|
sceneMode: Cesium.SceneMode.SCENE2D,
|
|
mapProjection: new Cesium.WebMercatorProjection(),
|
|
imageryProvider: false,
|
|
terrainProvider: new Cesium.EllipsoidTerrainProvider(),
|
|
baseLayer: false
|
|
})
|
|
|
|
this.viewer.cesiumWidget.creditContainer.style.display = "none"
|
|
this.loadOfflineMap()
|
|
this.setup2DConstraints()
|
|
|
|
this.viewer.camera.setView({
|
|
destination: Cesium.Cartesian3.fromDegrees(116.3974, 39.9093, 5000000),
|
|
orientation: {
|
|
heading: 0,
|
|
pitch: -Cesium.Math.PI_OVER_TWO,
|
|
roll: 0
|
|
}
|
|
})
|
|
|
|
this.initScaleBar()
|
|
console.log('Cesium离线二维地图已加载')
|
|
|
|
} catch (error) {
|
|
console.error('地图错误:', error)
|
|
// 如果Cesium加载失败,显示错误信息
|
|
this.showErrorMessage();
|
|
}
|
|
},
|
|
showErrorMessage() {
|
|
const container = document.getElementById('cesiumViewer');
|
|
if (container) {
|
|
container.innerHTML = `
|
|
<div style="
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #f5f5f5;
|
|
color: #666;
|
|
font-family: Arial, sans-serif;
|
|
">
|
|
<h3 style="margin-bottom: 20px;">地图加载失败</h3>
|
|
<p>可能的原因:</p>
|
|
<ul style="text-align: left; margin: 10px 0;">
|
|
<li>网络连接问题</li>
|
|
<li>Cesium CDN资源未加载</li>
|
|
<li>浏览器兼容性问题</li>
|
|
</ul>
|
|
<button onclick="location.reload()" style="
|
|
margin-top: 20px;
|
|
padding: 10px 20px;
|
|
background: #4dabf7;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
">
|
|
重新加载
|
|
</button>
|
|
</div>
|
|
`;
|
|
}
|
|
},
|
|
|
|
setup2DConstraints() {
|
|
const scene = this.viewer.scene
|
|
const controller = scene.screenSpaceCameraController
|
|
|
|
controller.enableTilt = false
|
|
controller.enableRotate = false
|
|
controller.enableLook = false
|
|
scene.screenSpaceCameraController.maximumPitch = 0
|
|
scene.screenSpaceCameraController.minimumPitch = 0
|
|
},
|
|
|
|
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}',
|
|
minimumLevel: 0,
|
|
maximumLevel: 18,
|
|
tileWidth: 256,
|
|
tileHeight: 256,
|
|
tilingScheme: new Cesium.WebMercatorTilingScheme(),
|
|
credit: '离线地图'
|
|
})
|
|
|
|
this.viewer.imageryLayers.addImageryProvider(offlineProvider)
|
|
|
|
} catch (error) {
|
|
console.error('加载离线地图失败:', error)
|
|
this.showGridLayer()
|
|
}
|
|
},
|
|
|
|
showGridLayer() {
|
|
const gridProvider = new Cesium.GridImageryProvider()
|
|
this.viewer.imageryLayers.addImageryProvider(gridProvider)
|
|
this.addCoordinateLabels()
|
|
},
|
|
|
|
addCoordinateLabels() {
|
|
for (let lon = -180; lon <= 180; lon += 30) {
|
|
for (let lat = -90; lat <= 90; lat += 30) {
|
|
this.viewer.entities.add({
|
|
position: Cesium.Cartesian3.fromDegrees(lon, lat),
|
|
label: {
|
|
text: `${lat}°N\n${lon}°E`,
|
|
font: '12px sans-serif',
|
|
fillColor: Cesium.Color.BLACK,
|
|
outlineColor: Cesium.Color.WHITE,
|
|
outlineWidth: 2,
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
pixelOffset: new Cesium.Cartesian2(10, 0)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
},
|
|
|
|
// ================== 绘制功能 ==================
|
|
|
|
toggleDrawing(mode) {
|
|
if (this.drawingMode === mode) {
|
|
// 停止当前绘制
|
|
this.stopDrawing()
|
|
this.drawingMode = null
|
|
} else {
|
|
// 停止之前的绘制
|
|
if (this.drawingMode) {
|
|
this.stopDrawing()
|
|
}
|
|
|
|
// 开始新的绘制
|
|
this.drawingMode = mode
|
|
this.startDrawing(mode)
|
|
}
|
|
},
|
|
|
|
startDrawing(mode) {
|
|
this.stopDrawing()
|
|
this.drawingPoints = []
|
|
this.drawingStartPoint = null
|
|
this.isDrawing = true
|
|
|
|
this.drawingHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
|
|
|
|
// 根据模式设置不同的鼠标样式和事件
|
|
switch(mode) {
|
|
case 'point':
|
|
this.startPointDrawing()
|
|
break
|
|
case 'line':
|
|
this.startLineDrawing()
|
|
break
|
|
case 'polygon':
|
|
this.startPolygonDrawing()
|
|
break
|
|
case 'rectangle':
|
|
this.startRectangleDrawing()
|
|
break
|
|
case 'circle':
|
|
this.startCircleDrawing()
|
|
break
|
|
}
|
|
|
|
this.viewer.scene.canvas.style.cursor = 'crosshair'
|
|
console.log(`开始绘制 ${this.getTypeName(mode)}`)
|
|
},
|
|
|
|
stopDrawing() {
|
|
if (this.drawingHandler) {
|
|
this.drawingHandler.destroy();
|
|
this.drawingHandler = null;
|
|
}
|
|
|
|
if (this.tempEntity) {
|
|
this.viewer.entities.remove(this.tempEntity);
|
|
this.tempEntity = null;
|
|
}
|
|
|
|
// 确保也清理预览实体
|
|
if (this.tempPreviewEntity) {
|
|
this.viewer.entities.remove(this.tempPreviewEntity);
|
|
this.tempPreviewEntity = null;
|
|
}
|
|
|
|
this.drawingPoints = [];
|
|
this.drawingStartPoint = null;
|
|
this.isDrawing = false;
|
|
|
|
this.viewer.scene.canvas.style.cursor = 'default';
|
|
},
|
|
// ********************************************************************
|
|
// 绘制点
|
|
startPointDrawing() {
|
|
this.drawingHandler.setInputAction((click) => {
|
|
const position = this.getClickPosition(click.position)
|
|
if (position) {
|
|
const { lat, lng } = this.cartesianToLatLng(position)
|
|
this.addPointEntity(lat, lng)
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
|
},
|
|
|
|
// 绘制线
|
|
startLineDrawing() {
|
|
this.drawingPoints = [];
|
|
|
|
// 鼠标点击事件 - 添加点
|
|
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);
|
|
}
|
|
|
|
this.tempPreviewEntity = this.viewer.entities.add({
|
|
polyline: {
|
|
positions: previewPositions,
|
|
width: this.defaultStyles.line.width,
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.line.color).withAlpha(0.5),
|
|
clampToGround: true
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}, 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({
|
|
polyline: {
|
|
positions: previewPositions,
|
|
width: this.defaultStyles.line.width,
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.line.color).withAlpha(0.5), // 半透明效果
|
|
clampToGround: true
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
|
|
|
// 右键完成绘制
|
|
this.drawingHandler.setInputAction(() => {
|
|
if (this.drawingPoints.length > 1) {
|
|
this.finishLineDrawing();
|
|
} else {
|
|
this.cancelDrawing();
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
|
},
|
|
|
|
finishLineDrawing() {
|
|
// 将预览线段转换为最终线段
|
|
if (this.drawingPoints.length > 1) {
|
|
// 移除预览线段
|
|
if (this.tempPreviewEntity) {
|
|
this.viewer.entities.remove(this.tempPreviewEntity);
|
|
this.tempPreviewEntity = null;
|
|
}
|
|
|
|
// 创建最终的实线实体
|
|
const entity = this.addLineEntity([...this.drawingPoints]);
|
|
|
|
// 计算长度
|
|
const length = this.calculateLineLength([...this.drawingPoints]);
|
|
this.measurementResult = {
|
|
distance: length,
|
|
type: 'line'
|
|
};
|
|
|
|
this.stopDrawing();
|
|
return entity;
|
|
} else {
|
|
this.cancelDrawing();
|
|
return null;
|
|
}
|
|
},
|
|
|
|
// 绘制多边形
|
|
startPolygonDrawing() {
|
|
this.drawingHandler.setInputAction((click) => {
|
|
const position = this.getClickPosition(click.position)
|
|
if (position) {
|
|
this.drawingPoints.push(position)
|
|
|
|
// 更新临时多边形
|
|
if (this.tempEntity) {
|
|
this.viewer.entities.remove(this.tempEntity)
|
|
}
|
|
|
|
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({
|
|
polyline: {
|
|
positions: this.drawingPoints,
|
|
width: this.defaultStyles.polygon.width,
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color),
|
|
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)
|
|
|
|
// 双击完成绘制
|
|
this.drawingHandler.setInputAction((click) => {
|
|
if (this.drawingPoints.length > 2) {
|
|
this.finishPolygonDrawing()
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
|
|
|
|
// 右键取消
|
|
this.drawingHandler.setInputAction(() => {
|
|
this.cancelDrawing()
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
|
},
|
|
|
|
finishPolygonDrawing() {
|
|
const positions = [...this.drawingPoints]
|
|
const entity = this.addPolygonEntity(positions)
|
|
|
|
// 计算面积
|
|
const area = this.calculatePolygonArea(positions)
|
|
this.measurementResult = {
|
|
area: area,
|
|
type: 'polygon'
|
|
}
|
|
|
|
this.stopDrawing()
|
|
return entity
|
|
},
|
|
|
|
// 绘制矩形
|
|
startRectangleDrawing() {
|
|
let startPosition = null
|
|
|
|
this.drawingHandler.setInputAction((click) => {
|
|
startPosition = this.getClickPosition(click.position)
|
|
if (startPosition) {
|
|
this.drawingStartPoint = startPosition
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOWN)
|
|
|
|
this.drawingHandler.setInputAction((movement) => {
|
|
if (startPosition) {
|
|
const endPosition = this.getClickPosition(movement.endPosition)
|
|
if (endPosition) {
|
|
// 更新临时矩形
|
|
if (this.tempEntity) {
|
|
this.viewer.entities.remove(this.tempEntity)
|
|
}
|
|
|
|
const rectangle = this.calculateRectangle(startPosition, endPosition)
|
|
this.tempEntity = this.viewer.entities.add({
|
|
rectangle: {
|
|
coordinates: rectangle,
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color)
|
|
.withAlpha(this.defaultStyles.rectangle.opacity),
|
|
outline: true,
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color),
|
|
outlineWidth: this.defaultStyles.rectangle.width
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
|
|
|
this.drawingHandler.setInputAction(() => {
|
|
if (startPosition && this.tempEntity) {
|
|
this.finishRectangleDrawing(startPosition)
|
|
startPosition = null
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.LEFT_UP)
|
|
|
|
// 右键取消
|
|
this.drawingHandler.setInputAction(() => {
|
|
this.cancelDrawing()
|
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
|
},
|
|
|
|
finishRectangleDrawing(startPosition) {
|
|
if (!this.tempEntity) return
|
|
|
|
const rectangleCoords = this.tempEntity.rectangle.coordinates.getValue()
|
|
const entity = this.addRectangleEntity(rectangleCoords)
|
|
|
|
// 计算面积
|
|
const area = this.calculateRectangleArea(rectangleCoords)
|
|
this.measurementResult = {
|
|
area: area,
|
|
type: 'rectangle'
|
|
}
|
|
|
|
this.stopDrawing()
|
|
return entity
|
|
},
|
|
|
|
// 绘制圆形
|
|
startCircleDrawing() {
|
|
let centerPosition = null
|
|
let radius = 0
|
|
|
|
this.drawingHandler.setInputAction((click) => {
|
|
centerPosition = this.getClickPosition(click.position)
|
|
if (centerPosition) {
|
|
this.drawingStartPoint = centerPosition
|
|
|
|
// 创建中心点
|
|
if (this.tempEntity) {
|
|
this.viewer.entities.remove(this.tempEntity)
|
|
}
|
|
|
|
this.tempEntity = this.viewer.entities.add({
|
|
position: centerPosition,
|
|
point: {
|
|
pixelSize: 8,
|
|
color: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color)
|
|
}
|
|
})
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOWN)
|
|
|
|
this.drawingHandler.setInputAction((movement) => {
|
|
if (centerPosition) {
|
|
const currentPosition = this.getClickPosition(movement.endPosition)
|
|
if (currentPosition) {
|
|
radius = Cesium.Cartesian3.distance(centerPosition, currentPosition)
|
|
|
|
// 更新临时圆形
|
|
if (this.tempEntity) {
|
|
this.viewer.entities.remove(this.tempEntity)
|
|
}
|
|
|
|
// 创建圆形多边形
|
|
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),
|
|
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
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.LEFT_UP)
|
|
|
|
// 右键取消
|
|
// this.drawingHandler.setInputAction(() => {
|
|
// this.cancelDrawing()
|
|
// }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
|
},
|
|
|
|
finishCircleDrawing(center, radius) {
|
|
const positions = this.calculateCircle(center, radius, 64)
|
|
const entity = this.addCircleEntity(center, radius, positions)
|
|
|
|
// 计算面积
|
|
const area = Math.PI * radius * radius
|
|
this.measurementResult = {
|
|
area: area,
|
|
radius: radius,
|
|
type: 'circle'
|
|
}
|
|
|
|
this.stopDrawing()
|
|
return entity
|
|
},
|
|
|
|
cancelDrawing() {
|
|
this.stopDrawing()
|
|
this.drawingMode = null
|
|
},
|
|
|
|
// ================== 实体创建方法 ==================
|
|
|
|
addPointEntity(lat, lng) {
|
|
this.entityCounter++
|
|
const id = `point_${this.entityCounter}`
|
|
|
|
const entity = this.viewer.entities.add({
|
|
id: id,
|
|
name: `点 ${this.entityCounter}`,
|
|
position: Cesium.Cartesian3.fromDegrees(lng, lat),
|
|
point: {
|
|
pixelSize: this.defaultStyles.point.size,
|
|
color: Cesium.Color.fromCssColorString(this.defaultStyles.point.color),
|
|
outlineColor: Cesium.Color.WHITE,
|
|
outlineWidth: 2
|
|
},
|
|
label: {
|
|
text: `${this.entityCounter}`,
|
|
font: '14px Arial',
|
|
fillColor: Cesium.Color.WHITE,
|
|
outlineColor: Cesium.Color.BLACK,
|
|
outlineWidth: 2,
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER
|
|
}
|
|
})
|
|
|
|
const entityData = {
|
|
id,
|
|
type: 'point',
|
|
lat,
|
|
lng,
|
|
entity: entity,
|
|
color: this.defaultStyles.point.color,
|
|
size: this.defaultStyles.point.size,
|
|
label: `点 ${this.entityCounter}`
|
|
}
|
|
|
|
this.allEntities.push(entityData)
|
|
|
|
// 添加点击事件
|
|
entity.clickHandler = (e) => {
|
|
this.selectEntity(entityData)
|
|
e.stopPropagation()
|
|
}
|
|
|
|
return entityData
|
|
},
|
|
|
|
addLineEntity(positions) {
|
|
this.entityCounter++
|
|
const id = `line_${this.entityCounter}`
|
|
|
|
const entity = this.viewer.entities.add({
|
|
id: id,
|
|
name: `线 ${this.entityCounter}`,
|
|
polyline: {
|
|
positions: positions,
|
|
width: this.defaultStyles.line.width,
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.line.color),
|
|
clampToGround: true
|
|
}
|
|
})
|
|
|
|
const entityData = {
|
|
id,
|
|
type: 'line',
|
|
points: positions.map(p => this.cartesianToLatLng(p)),
|
|
positions: positions,
|
|
entity: entity,
|
|
color: this.defaultStyles.line.color,
|
|
width: this.defaultStyles.line.width,
|
|
label: `线 ${this.entityCounter}`
|
|
}
|
|
|
|
this.allEntities.push(entityData)
|
|
|
|
entity.clickHandler = (e) => {
|
|
this.selectEntity(entityData)
|
|
e.stopPropagation()
|
|
}
|
|
|
|
return entityData
|
|
},
|
|
|
|
addPolygonEntity(positions) {
|
|
this.entityCounter++
|
|
const id = `polygon_${this.entityCounter}`
|
|
|
|
// 闭合多边形
|
|
const polygonPositions = [...positions, positions[0]]
|
|
|
|
const entity = this.viewer.entities.add({
|
|
id: id,
|
|
name: `面 ${this.entityCounter}`,
|
|
polygon: {
|
|
hierarchy: new Cesium.PolygonHierarchy(polygonPositions),
|
|
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
|
|
}
|
|
})
|
|
|
|
const entityData = {
|
|
id,
|
|
type: 'polygon',
|
|
points: positions.map(p => this.cartesianToLatLng(p)),
|
|
positions: polygonPositions,
|
|
entity: entity,
|
|
color: this.defaultStyles.polygon.color,
|
|
opacity: this.defaultStyles.polygon.opacity,
|
|
width: this.defaultStyles.polygon.width,
|
|
label: `面 ${this.entityCounter}`
|
|
}
|
|
|
|
this.allEntities.push(entityData)
|
|
|
|
entity.clickHandler = (e) => {
|
|
this.selectEntity(entityData)
|
|
e.stopPropagation()
|
|
}
|
|
|
|
return entityData
|
|
},
|
|
|
|
addRectangleEntity(coordinates) {
|
|
this.entityCounter++
|
|
const id = `rectangle_${this.entityCounter}`
|
|
|
|
const entity = this.viewer.entities.add({
|
|
id: id,
|
|
name: `矩形 ${this.entityCounter}`,
|
|
rectangle: {
|
|
coordinates: coordinates,
|
|
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color)
|
|
.withAlpha(this.defaultStyles.rectangle.opacity),
|
|
outline: true,
|
|
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color),
|
|
outlineWidth: this.defaultStyles.rectangle.width
|
|
}
|
|
})
|
|
|
|
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.clickHandler = (e) => {
|
|
this.selectEntity(entityData)
|
|
e.stopPropagation()
|
|
}
|
|
|
|
return entityData
|
|
},
|
|
|
|
addCircleEntity(center, radius, positions) {
|
|
this.entityCounter++
|
|
const id = `circle_${this.entityCounter}`
|
|
|
|
const entity = this.viewer.entities.add({
|
|
id: id,
|
|
name: `圆形 ${this.entityCounter}`,
|
|
polygon: {
|
|
hierarchy: new Cesium.PolygonHierarchy(positions),
|
|
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()
|
|
}
|
|
|
|
return entityData
|
|
},
|
|
|
|
// ================== 工具方法 ==================
|
|
|
|
getClickPosition(pixelPosition) {
|
|
const cartesian = this.viewer.camera.pickEllipsoid(pixelPosition, this.viewer.scene.globe.ellipsoid)
|
|
return cartesian
|
|
},
|
|
|
|
cartesianToLatLng(cartesian) {
|
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
|
|
return {
|
|
lat: Cesium.Math.toDegrees(cartographic.latitude),
|
|
lng: Cesium.Math.toDegrees(cartographic.longitude)
|
|
}
|
|
},
|
|
|
|
calculateRectangle(start, end) {
|
|
const startLL = this.cartesianToLatLng(start)
|
|
const endLL = this.cartesianToLatLng(end)
|
|
|
|
return Cesium.Rectangle.fromDegrees(
|
|
Math.min(startLL.lng, endLL.lng),
|
|
Math.min(startLL.lat, endLL.lat),
|
|
Math.max(startLL.lng, endLL.lng),
|
|
Math.max(startLL.lat, endLL.lat)
|
|
)
|
|
},
|
|
|
|
calculateCircle(center, radius, segments) {
|
|
const positions = []
|
|
const centerLL = Cesium.Cartographic.fromCartesian(center)
|
|
|
|
for (let i = 0; i < segments; i++) {
|
|
const angle = (i / segments) * Math.PI * 2
|
|
const lat = centerLL.latitude + (radius / 6378137) * Math.sin(angle)
|
|
const lon = centerLL.longitude + (radius / 6378137) * Math.cos(angle) / Math.cos(centerLL.latitude)
|
|
|
|
positions.push(Cesium.Cartesian3.fromRadians(lon, lat))
|
|
}
|
|
|
|
return positions
|
|
},
|
|
|
|
calculateLineLength(positions) {
|
|
let totalLength = 0
|
|
for (let i = 0; i < positions.length - 1; i++) {
|
|
totalLength += Cesium.Cartesian3.distance(positions[i], positions[i + 1])
|
|
}
|
|
return totalLength
|
|
},
|
|
|
|
calculatePolygonArea(positions) {
|
|
if (positions.length < 3) return 0
|
|
|
|
let area = 0
|
|
const n = positions.length
|
|
|
|
for (let i = 0; i < n; i++) {
|
|
const j = (i + 1) % n
|
|
const p1 = Cesium.Cartographic.fromCartesian(positions[i])
|
|
const p2 = Cesium.Cartographic.fromCartesian(positions[j])
|
|
|
|
area += p1.longitude * p2.latitude - p2.longitude * p1.latitude
|
|
}
|
|
|
|
area = Math.abs(area) * 6378137 * 6378137 / 2
|
|
return area
|
|
},
|
|
|
|
calculateRectangleArea(coordinates) {
|
|
const rect = coordinates.getValue ? coordinates.getValue() : coordinates
|
|
const width = Cesium.Cartesian3.distance(
|
|
Cesium.Cartesian3.fromRadians(rect.west, rect.north),
|
|
Cesium.Cartesian3.fromRadians(rect.east, rect.north)
|
|
)
|
|
const height = Cesium.Cartesian3.distance(
|
|
Cesium.Cartesian3.fromRadians(rect.west, rect.north),
|
|
Cesium.Cartesian3.fromRadians(rect.west, rect.south)
|
|
)
|
|
|
|
return width * height
|
|
},
|
|
|
|
// ================== 实体管理 ==================
|
|
|
|
selectEntity(entity) {
|
|
this.selectedEntity = entity
|
|
|
|
// 居中显示
|
|
if (entity.type === 'point') {
|
|
this.viewer.camera.flyTo({
|
|
destination: Cesium.Cartesian3.fromDegrees(entity.lng, entity.lat, 1000),
|
|
duration: 1.0
|
|
})
|
|
} else if (entity.positions && entity.positions.length > 0) {
|
|
const rectangle = Cesium.Rectangle.fromCartographicArray(
|
|
entity.positions.map(p => Cesium.Cartographic.fromCartesian(p))
|
|
)
|
|
this.viewer.camera.flyTo({
|
|
destination: rectangle,
|
|
duration: 1.0
|
|
})
|
|
}
|
|
},
|
|
|
|
updateEntityStyle() {
|
|
if (!this.selectedEntity || !this.selectedEntity.entity) return
|
|
|
|
const entity = this.selectedEntity.entity
|
|
const data = this.selectedEntity
|
|
|
|
switch(data.type) {
|
|
case 'point':
|
|
entity.point.color = Cesium.Color.fromCssColorString(data.color)
|
|
entity.point.pixelSize = data.size
|
|
break
|
|
case 'line':
|
|
entity.polyline.material = Cesium.Color.fromCssColorString(data.color)
|
|
entity.polyline.width = data.width
|
|
break
|
|
case 'polygon':
|
|
entity.polygon.material = Cesium.Color.fromCssColorString(data.color).withAlpha(data.opacity)
|
|
entity.polygon.outlineColor = Cesium.Color.fromCssColorString(data.color)
|
|
entity.polygon.outlineWidth = data.width
|
|
break
|
|
case 'rectangle':
|
|
entity.rectangle.material = Cesium.Color.fromCssColorString(data.color).withAlpha(data.opacity)
|
|
entity.rectangle.outlineColor = Cesium.Color.fromCssColorString(data.color)
|
|
entity.rectangle.outlineWidth = data.width
|
|
break
|
|
case 'circle':
|
|
entity.polygon.material = Cesium.Color.fromCssColorString(data.color).withAlpha(data.opacity)
|
|
entity.polygon.outlineColor = Cesium.Color.fromCssColorString(data.color)
|
|
entity.polygon.outlineWidth = data.width
|
|
break
|
|
}
|
|
},
|
|
|
|
updateEntityLabel() {
|
|
if (!this.selectedEntity || !this.selectedEntity.entity) return
|
|
|
|
const entity = this.selectedEntity.entity
|
|
entity.name = this.selectedEntity.label
|
|
|
|
// 如果是点,更新标签
|
|
if (this.selectedEntity.type === 'point') {
|
|
entity.label.text = this.selectedEntity.label
|
|
}
|
|
},
|
|
|
|
deleteSelectedEntity() {
|
|
if (this.selectedEntity) {
|
|
this.removeEntity(this.selectedEntity.id)
|
|
this.selectedEntity = null
|
|
}
|
|
},
|
|
|
|
removeEntity(id) {
|
|
const index = this.allEntities.findIndex(e => e.id === id)
|
|
if (index > -1) {
|
|
const entity = this.allEntities[index]
|
|
|
|
// 从地图中移除
|
|
if (entity.entity) {
|
|
this.viewer.entities.remove(entity.entity)
|
|
}
|
|
|
|
// 从数组中移除
|
|
this.allEntities.splice(index, 1)
|
|
|
|
// 如果删除的是选中的实体,清空选中状态
|
|
if (this.selectedEntity && this.selectedEntity.id === id) {
|
|
this.selectedEntity = null
|
|
}
|
|
}
|
|
},
|
|
|
|
clearAll() {
|
|
// 停止绘制
|
|
this.stopDrawing()
|
|
this.drawingMode = null
|
|
|
|
// 移除所有实体
|
|
this.allEntities.forEach(entity => {
|
|
if (entity.entity) {
|
|
this.viewer.entities.remove(entity.entity)
|
|
}
|
|
})
|
|
|
|
// 清空数组
|
|
this.allEntities = []
|
|
this.entityCounter = 0
|
|
this.selectedEntity = null
|
|
this.measurementResult = null
|
|
|
|
console.log('已清除所有图形')
|
|
},
|
|
|
|
// ================== 其他方法 ==================
|
|
|
|
getTypeName(type) {
|
|
const names = {
|
|
point: '点',
|
|
line: '线',
|
|
polygon: '面',
|
|
rectangle: '矩形',
|
|
circle: '圆形'
|
|
}
|
|
return names[type] || type
|
|
},
|
|
|
|
exportData() {
|
|
const data = {
|
|
version: '1.0',
|
|
date: new Date().toISOString(),
|
|
entities: this.allEntities.map(entity => ({
|
|
id: entity.id,
|
|
type: entity.type,
|
|
label: entity.label,
|
|
color: entity.color,
|
|
data: entity.type === 'point' ? {
|
|
lat: entity.lat,
|
|
lng: entity.lng
|
|
} : entity.type === 'line' || entity.type === 'polygon' ? {
|
|
points: entity.points
|
|
} : entity.type === 'rectangle' ? {
|
|
coordinates: entity.coordinates
|
|
} : {
|
|
center: entity.center,
|
|
radius: entity.radius
|
|
}
|
|
}))
|
|
}
|
|
|
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
|
|
const url = URL.createObjectURL(blob)
|
|
const a = document.createElement('a')
|
|
a.href = url
|
|
a.download = `cesium-drawing-${new Date().toISOString().slice(0, 10)}.json`
|
|
a.click()
|
|
URL.revokeObjectURL(url)
|
|
|
|
console.log('数据已导出', data)
|
|
},
|
|
|
|
initScaleBar() {
|
|
// ... 原有的比例尺代码保持不变
|
|
},
|
|
|
|
updateScaleBar() {
|
|
// ... 原有的比例尺更新代码保持不变
|
|
},
|
|
|
|
destroyViewer() {
|
|
this.stopDrawing()
|
|
this.clearAll()
|
|
|
|
if (this.viewer) {
|
|
this.viewer.destroy()
|
|
this.viewer = null
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.cesium-container {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
position: relative;
|
|
}
|
|
|
|
#cesiumViewer {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/* 主工具栏 - 修改后 */
|
|
.main-toolbar {
|
|
position: absolute;
|
|
top: 365px; /* 增大这个数值即可整体下移,比如从100px改成150px */
|
|
left: 62px;
|
|
z-index: 1000;
|
|
display: flex;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
border-radius: 12px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.main-toolbar.collapsed {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
/* 工具栏内容 - 无需修改 */
|
|
.toolbar-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
padding: 15px;
|
|
min-width: 180px;
|
|
}
|
|
|
|
/* 收起按钮 */
|
|
.collapse-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 10px;
|
|
cursor: pointer;
|
|
background: #f8f9fa;
|
|
border-top-left-radius: 12px;
|
|
border-bottom-left-radius: 12px;
|
|
border-right: 1px solid #e9ecef;
|
|
transition: all 0.3s ease;
|
|
user-select: none;
|
|
}
|
|
|
|
.collapse-btn:hover {
|
|
background: #e9ecef;
|
|
}
|
|
|
|
.collapse-icon {
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
color: #495057;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.collapse-text {
|
|
font-size: 12px;
|
|
color: #6c757d;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* 工具栏组 */
|
|
.toolbar-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid #e9ecef;
|
|
}
|
|
|
|
.toolbar-group:last-child {
|
|
border-bottom: none;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
/* 收起时的最小化工具栏 */
|
|
.toolbar-minimized {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 10px 8px;
|
|
gap: 8px;
|
|
}
|
|
|
|
.min-tool-btn {
|
|
width: 36px;
|
|
height: 36px;
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 8px;
|
|
background: white;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 16px;
|
|
color: #495057;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.min-tool-btn:hover {
|
|
background: #f8f9fa;
|
|
border-color: #4dabf7;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.min-tool-btn.active {
|
|
background: #4dabf7;
|
|
color: white;
|
|
border-color: #4dabf7;
|
|
}
|
|
|
|
.min-tool-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
transform: none !important;
|
|
}
|
|
|
|
.min-tool-btn.danger {
|
|
color: #dc3545;
|
|
}
|
|
|
|
.min-tool-btn.danger:hover {
|
|
background: #f8d7da;
|
|
border-color: #dc3545;
|
|
}
|
|
|
|
.min-tool-btn.success {
|
|
color: #28a745;
|
|
}
|
|
|
|
.min-tool-btn.success:hover {
|
|
background: #d4edda;
|
|
border-color: #28a745;
|
|
}
|
|
|
|
.min-tool-separator {
|
|
height: 1px;
|
|
background: #e9ecef;
|
|
margin: 5px 0;
|
|
}
|
|
|
|
/* 原来的工具按钮样式保持不变,但添加展开/收起状态的调整 */
|
|
.tool-btn {
|
|
padding: 10px 15px;
|
|
background: white;
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: #495057;
|
|
transition: all 0.3s ease;
|
|
min-width: 120px;
|
|
}
|
|
|
|
.tool-btn:hover {
|
|
background: #f8f9fa;
|
|
border-color: #4dabf7;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.tool-btn.active {
|
|
background: #4dabf7;
|
|
color: white;
|
|
border-color: #4dabf7;
|
|
}
|
|
|
|
.tool-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
transform: none !important;
|
|
}
|
|
|
|
.tool-btn.danger {
|
|
color: #dc3545;
|
|
}
|
|
|
|
.tool-btn.danger:hover {
|
|
background: #f8d7da;
|
|
border-color: #dc3545;
|
|
}
|
|
|
|
.tool-btn.success {
|
|
color: #28a745;
|
|
}
|
|
|
|
.tool-btn.success:hover {
|
|
background: #d4edda;
|
|
border-color: #28a745;
|
|
}
|
|
|
|
.tool-btn .icon {
|
|
font-size: 16px;
|
|
width: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.tool-btn .text {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* 其他样式保持不变... */
|
|
|
|
/* 属性面板 */
|
|
.property-panel {
|
|
position: absolute;
|
|
top: 20px;
|
|
left: 20px;
|
|
z-index: 1000;
|
|
background: rgba(255, 255, 255, 0.98);
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
min-width: 250px;
|
|
max-width: 300px;
|
|
}
|
|
|
|
/* 测量结果面板 */
|
|
.measurement-panel {
|
|
position: absolute;
|
|
bottom: 80px;
|
|
left: 20px;
|
|
z-index: 1000;
|
|
background: rgba(255, 255, 255, 0.98);
|
|
border-radius: 12px;
|
|
padding: 15px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
min-width: 200px;
|
|
}
|
|
|
|
/* 自定义比例尺样式 */
|
|
:deep(.scale-bar-container) {
|
|
user-select: none;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* 隐藏Cesium的默认控件 */
|
|
:deep(.cesium-viewer-bottom) {
|
|
display: none !important;
|
|
}
|
|
|
|
:deep(.cesium-credit-logoContainer) {
|
|
display: none !important;
|
|
}
|
|
|
|
:deep(.cesium-credit-textContainer) {
|
|
display: none !important;
|
|
}
|
|
|
|
/* 响应式调整 */
|
|
@media (max-width: 768px) {
|
|
.main-toolbar {
|
|
right: 10px;
|
|
top: 80px;
|
|
}
|
|
|
|
.toolbar-content {
|
|
min-width: 150px;
|
|
}
|
|
|
|
.tool-btn {
|
|
min-width: 100px;
|
|
padding: 8px 12px;
|
|
}
|
|
}
|
|
</style>
|
|
|