Cesium 线段分割和删除

一、主要功能特点

  1. 加载线段

页面加载时自动创建一条蓝色原始线段

线段包含多个坐标点,形成折线

  1. 分割功能

点击"分割线段"按钮进入分割模式

用户在线段两侧依次点击两个点

实时显示红色虚线分割线

500毫秒后自动执行分割,原始线段被分成两段不同颜色的线段

分割线自动消失

  1. 选择和删除功能

点击任意分段线段会高亮显示(加粗+透明度变化)

"删除选中线段"按钮变为可用

点击按钮删除当前选中的线段段

支持连续删除多个段

  1. 用户界面

清晰的控制面板

实时状态提示

使用说明

重置功能

  1. 交互体验

鼠标悬停效果

按钮状态管理

错误处理和提示

使用说明:

替换Mapbox Token:将代码中的mapboxgl.accessToken替换为您的实际token

分割操作:点击分割按钮→依次点击两个点→自动分割

删除操作:点击要删除的线段→点击删除按钮

重置:随时可以重置到初始状态

代码采用了模块化的设计,易于理解和扩展,可以根据实际需求调整坐标、颜色和交互逻辑。

二、完整代码

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cesium 线段分割和删除(修复版)</title>
    <script src="https://cesium.com/downloads/cesiumjs/releases/1.108/Build/Cesium/Cesium.js"></script>
    <link href="https://cesium.com/downloads/cesiumjs/releases/1.108/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
    <style>
        html, body, #cesiumContainer {
            width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
            font-family: Arial, sans-serif;
        }
        
        .control-panel {
            position: absolute;
            top: 10px;
            left: 10px;
            background: rgba(42, 42, 42, 0.9);
            padding: 15px;
            border-radius: 8px;
            color: white;
            z-index: 1000;
            max-width: 350px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
        }
        
        .btn {
            background: #48b;
            color: white;
            border: none;
            padding: 10px 15px;
            margin: 5px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.3s;
        }
        
        .btn:hover {
            background: #369;
        }
        
        .btn:disabled {
            background: #666;
            cursor: not-allowed;
        }
        
        .btn.split-active {
            background: #2d8a2d !important;
        }
        
        .btn.split-active:hover {
            background: #1f6b1f !important;
        }
        
        .status {
            margin-top: 10px;
            padding: 8px;
            background: rgba(255,255,255,0.1);
            border-radius: 4px;
            font-size: 12px;
            min-height: 20px;
            word-wrap: break-word;
        }
        
        .instructions {
            margin-top: 10px;
            font-size: 12px;
            color: #ccc;
            line-height: 1.4;
        }
        
        .point-counter {
            display: inline-block;
            background: #48b;
            color: white;
            border-radius: 50%;
            width: 20px;
            height: 20px;
            text-align: center;
            line-height: 20px;
            font-size: 12px;
            font-weight: bold;
            margin-right: 5px;
        }
        
        .preview-info {
            margin-top: 5px;
            font-size: 11px;
            color: #4ade80;
            font-style: italic;
        }
        
        .highlight-info {
            margin-top: 5px;
            font-size: 11px;
            color: #fb923c;
            font-style: italic;
        }
        
        .auto-reset-info {
            margin-top: 5px;
            font-size: 11px;
            color: #a78bfa;
            font-style: italic;
        }
        
        .target-highlight-info {
            margin-top: 5px;
            font-size: 11px;
            color: #22d3ee;
            font-style: italic;
        }
        
        /* Cesium控件样式调整 */
        .cesium-widget-credits {
            display: none !important;
        }
    </style>
</head>
<body>
    <div id="cesiumContainer"></div>
    
    <div class="control-panel">
        <h3>线段操作控制</h3>
        <button id="splitBtn" class="btn">分割线段</button>
        <button id="deleteBtn" class="btn" disabled>删除选中线段</button>
        <button id="resetBtn" class="btn">手动重置</button>
        
        <div class="status" id="status">
            <span class="point-counter" id="pointCounter">0</span>
            点击"分割线段"开始操作
        </div>
        
        <div class="preview-info">
            ✨ 实时预览:点击第一点后即可看到动态分割线
        </div>
        
        <div class="highlight-info">
            🎯 选中效果:宽度×1.5倍,亮度显著提升
        </div>
        
        <div class="auto-reset-info">
            🔄 自动重置:每次分割前自动清除上次结果
        </div>
        
        <div class="target-highlight-info">
            📍 目标高亮:宽度×1.2倍,亮度+20%(基于原始状态)
        </div>
        
        <div class="instructions">
            <strong>使用说明:</strong><br>
            1. 点击"分割线段"按钮开始<br>
            2. <strong>目标线段高亮:</strong><br>
               • 点击后原始线段会变为<strong style="color:#22d3ee">青色高亮</strong><br>
               • <strong>宽度增加1.2倍</strong>(基于原始宽度)<br>
               • <strong>亮度增加0.2</strong>(不透明度提升20%)<br>
               • 其他线段保持原样不变<br>
               • 按钮变为绿色表示处于分割模式<br>
            3. <strong>自动重置机制:</strong><br>
               • 如果已有分割线段存在,会自动清除并恢复原始线段<br>
               • 确保每次分割都在干净的原始线段上进行<br>
            4. <strong>实时预览模式:</strong><br>
               • 点击第一个点①:立即显示该点和动态分割线<br>
               • 移动鼠标:动态分割线跟随鼠标实时更新<br>
               • 点击第二个点②:确认分割线并完成分割<br>
            5. <strong>选中高亮效果:</strong><br>
               • 点击分段线段:宽度变为7px(原5px的1.5倍)<br>
               • 亮度提升:不透明度80%,颜色更鲜艳
        </div>
    </div>

    <script>
        // 使用Cesium Ion的默认访问令牌,请替换为您自己的令牌
        Cesium.Ion.defaultAccessToken = "AccessToken";

        // 初始化Cesium viewer
        const viewer = new Cesium.Viewer('cesiumContainer', {
            // terrainProvider: Cesium.createWorldTerrain(),
            timeline: false,
            animation: false,
            homeButton: false,
            sceneModePicker: false,
            baseLayerPicker: false,
            navigationHelpButton: false,
            fullscreenButton: false,
            vrButton: false,
            geocoder: false,
            infoBox: false,
            selectionIndicator: false
        });

        // 设置初始视角到中国北京
        viewer.camera.setView({
            destination: Cesium.Cartesian3.fromDegrees(116.3974, 39.9093, 50000),
            orientation: {
                heading: 0.0,
                pitch: -Cesium.Math.PI_OVER_TWO,
                roll: 0.0
            }
        });

        // 全局变量
        let isSplitMode = false;
        let splitPoints = [];
        let pointEntities = [];
        let tempSplitLine = null;
        let previewSplitLine = null;
        let segmentedLines = [];
        let selectedSegmentId = null;
        let currentMousePos = null;
        let targetHighlightActive = false;
        let originalLineEntity = null;
        let originalLineMaterial = null;
        let originalLineWidth = 6;

        // 常量定义
        const NORMAL_WIDTH = 5;
        const BASE_LINE_WIDTH = NORMAL_WIDTH + 1; // 原始线段的基础宽度:6
        const TARGET_HIGHLIGHT_MULTIPLIER = 1.2; // 宽度增加1.2倍
        const TARGET_HIGHLIGHT_BRIGHTNESS_INCREASE = 0.2; // 亮度增加0.2
        const SELECTED_WIDTH = Math.round(NORMAL_WIDTH * 1.5); // 7
        const NORMAL_OPACITY = 1.0;
        const SELECTED_OPACITY = 0.8;

        // 计算目标高亮的具体数值
        const TARGET_HIGHLIGHT_WIDTH = Math.round(BASE_LINE_WIDTH * TARGET_HIGHLIGHT_MULTIPLIER);
        const TARGET_HIGHLIGHT_OPACITY = Math.min(1.0, NORMAL_OPACITY + TARGET_HIGHLIGHT_BRIGHTNESS_INCREASE);

        // DOM元素
        const splitBtn = document.getElementById('splitBtn');
        const deleteBtn = document.getElementById('deleteBtn');
        const resetBtn = document.getElementById('resetBtn');
        const status = document.getElementById('status');
        const pointCounter = document.getElementById('pointCounter');

        // 坐标转换工具函数
        function degreesToCartesian(degreesArray) {
            return degreesArray.map(coord => 
                Cesium.Cartesian3.fromDegrees(coord[0], coord[1])
            );
        }

        function cartesianToDegrees(cartesian) {
            const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
            return [
                Cesium.Math.toDegrees(cartographic.longitude),
                Cesium.Math.toDegrees(cartographic.latitude)
            ];
        }

        // 计算两点间距离
        function calculateDistance(point1, point2) {
            const cartographic1 = Cesium.Cartographic.fromCartesian(point1);
            const cartographic2 = Cesium.Cartographic.fromCartesian(point2);
            
            const lat1 = cartographic1.latitude;
            const lon1 = cartographic1.longitude;
            const lat2 = cartographic2.latitude;
            const lon2 = cartographic2.longitude;
            
            const R = 6371000; // 地球半径(米)
            const dLat = lat2 - lat1;
            const dLon = lon2 - lon1;
            
            const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                     Math.cos(lat1) * Math.cos(lat2) *
                     Math.sin(dLon/2) * Math.sin(dLon/2);
            const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            
            return R * c;
        }

        // 找到线段上距离目标点最近的点
        function findClosestPointOnLine(targetPoint, linePoints) {
            let closestPoint = null;
            let closestDistance = Infinity;
            let closestSegmentIndex = -1;
            let closestSegmentProgress = 0;

            for (let i = 0; i < linePoints.length - 1; i++) {
                const start = linePoints[i];
                const end = linePoints[i + 1];
                const result = getClosestPointOnSegment(targetPoint, start, end);
                const distance = calculateDistance(targetPoint, result.point);
                
                if (distance < closestDistance) {
                    closestDistance = distance;
                    closestPoint = result.point;
                    closestSegmentIndex = i;
                    closestSegmentProgress = result.progress;
                }
            }

            return {
                point: closestPoint,
                distance: closestDistance,
                segmentIndex: closestSegmentIndex,
                progress: closestSegmentProgress
            };
        }

        // 计算点到线段的最远点
        function getClosestPointOnSegment(point, segmentStart, segmentEnd) {
            const startCartographic = Cesium.Cartographic.fromCartesian(segmentStart);
            const endCartographic = Cesium.Cartographic.fromCartesian(segmentEnd);
            const pointCartographic = Cesium.Cartographic.fromCartesian(point);
            
            const startLon = startCartographic.longitude;
            const startLat = startCartographic.latitude;
            const endLon = endCartographic.longitude;
            const endLat = endCartographic.latitude;
            const pointLon = pointCartographic.longitude;
            const pointLat = pointCartographic.latitude;
            
            const dx = endLon - startLon;
            const dy = endLat - startLat;
            const lengthSquared = dx * dx + dy * dy;
            
            if (lengthSquared === 0) {
                return { point: segmentStart, progress: 0 };
            }
            
            let t = ((pointLon - startLon) * dx + (pointLat - startLat) * dy) / lengthSquared;
            t = Math.max(0, Math.min(1, t));
            
            const closestLon = startLon + t * dx;
            const closestLat = startLat + t * dy;
            
            return {
                point: Cesium.Cartesian3.fromRadians(closestLon, closestLat),
                progress: t
            };
        }

        // 计算两条线段的交点
        function findIntersectionOfTwoSegments(p1, p2, p3, p4) {
            const deg1 = cartesianToDegrees(p1);
            const deg2 = cartesianToDegrees(p2);
            const deg3 = cartesianToDegrees(p3);
            const deg4 = cartesianToDegrees(p4);
            
            const x1 = deg1[0], y1 = deg1[1];
            const x2 = deg2[0], y2 = deg2[1];
            const x3 = deg3[0], y3 = deg3[1];
            const x4 = deg4[0], y4 = deg4[1];
            
            const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
            
            if (Math.abs(denom) < 1e-10) {
                return null;
            }
            
            const t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
            const u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
            
            if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
                const intersectionLon = x1 + t * (x2 - x1);
                const intersectionLat = y1 + t * (y2 - y1);
                return Cesium.Cartesian3.fromDegrees(intersectionLon, intersectionLat);
            }
            
            return null;
        }

        // 调整颜色亮度 - 修复版本
        function adjustColorBrightness(color, percent) {
            let red, green, blue, alpha;
            
            // 正确处理不同类型的Cesium Color
            if (typeof color === 'string') {
                // 如果是字符串颜色名称,转换为Color对象
                color = Cesium.Color[color.toUpperCase()] || Cesium.Color.WHITE;
            }
            
            if (color instanceof Cesium.Color) {
                red = color.red;
                green = color.green;
                blue = color.blue;
                alpha = color.alpha;
            } else if (color && typeof color === 'object') {
                // 如果是对象格式的颜色
                red = color.red !== undefined ? color.red : color.r || 0;
                green = color.green !== undefined ? color.green : color.g || 0;
                blue = color.blue !== undefined ? color.blue : color.b || 0;
                alpha = color.alpha !== undefined ? color.alpha : color.a || 1.0;
            } else {
                // 默认白色
                red = green = blue = 1.0;
                alpha = 1.0;
            }
            
            const newRed = Math.min(1, red * (1 + percent / 100));
            const newGreen = Math.min(1, green * (1 + percent / 100));
            const newBlue = Math.min(1, blue * (1 + percent / 100));
            
            return new Cesium.Color(newRed, newGreen, newBlue, alpha);
        }

        // 获取颜色值的安全方法 - 新增函数
        function getColorValue(color) {
            if (color instanceof Cesium.Color) {
                return color;
            } else if (typeof color === 'string') {
                return Cesium.Color[color.toUpperCase()] || Cesium.Color.WHITE;
            } else if (color && typeof color === 'object') {
                if (color.getValue) {
                    return color.getValue();
                }
                return new Cesium.Color(
                    color.red || color.r || 1.0,
                    color.green || color.g || 1.0,
                    color.blue || color.b || 1.0,
                    color.alpha || color.a || 1.0
                );
            }
            return Cesium.Color.WHITE;
        }

        // 初始化应用
        function initializeApp() {
            createOriginalLine();
            setupEventListeners();
        }

        // 创建原始线段
        function createOriginalLine() {
            // 清除现有线段
            if (originalLineEntity && viewer.entities.contains(originalLineEntity)) {
                viewer.entities.remove(originalLineEntity);
            }
            
            const originalCoords = [
                [116.3874, 39.9193],
                [116.3924, 39.9143],
                [116.3974, 39.9093],
                [116.4024, 39.9043],
                [116.4074, 39.8993],
                [116.4124, 39.8943],
                [116.4174, 39.8893]
            ];
            
            const positions = degreesToCartesian(originalCoords);
            
            originalLineEntity = viewer.entities.add({
                name: 'original-line',
                polyline: {
                    positions: positions,
                    width: BASE_LINE_WIDTH,
                    material: Cesium.Color.BLUE.withAlpha(NORMAL_OPACITY),
                    clampToGround: true
                }
            });
            
            // 保存原始材质配置 - 修复版本
            const material = originalLineEntity.polyline.material;
            if (material && material.getValue) {
                originalLineMaterial = material.getValue();
            } else {
                originalLineMaterial = Cesium.Color.BLUE.withAlpha(NORMAL_OPACITY);
            }
            originalLineWidth = BASE_LINE_WIDTH;
        }

        // 设置事件监听器
        function setupEventListeners() {
            splitBtn.addEventListener('click', startSplitMode);
            deleteBtn.addEventListener('click', deleteSelectedSegment);
            resetBtn.addEventListener('click', manualReset);
            
            // Cesium鼠标事件
            const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
            handler.setInputAction(handleMapClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);
            handler.setInputAction(handleMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        }

        // 开始分割模式
        function startSplitMode() {
            // 自动重置:如果已经有分割线段存在,先清除它们
            if (hasExistingSegments()) {
                autoResetBeforeNewSplit();
            }
            
            isSplitMode = true;
            splitPoints = [];
            clearPointEntities();
            removePreviewLine();
            updatePointCounter();
            
            // 激活目标高亮
            activateTargetHighlight();
            
            // 更新按钮样式为激活状态
            splitBtn.classList.add('split-active');
            splitBtn.textContent = '分割模式中';
            
            status.innerHTML = `<span class="point-counter">0</span>📍 原始线段已高亮(宽度×${TARGET_HIGHLIGHT_MULTIPLIER},亮度+${TARGET_HIGHLIGHT_BRIGHTNESS_INCREASE}),点击地图放置第一个分割点①`;
            updatePointCounter();
        }

        // 激活目标高亮
        function activateTargetHighlight() {
            if (originalLineEntity && viewer.entities.contains(originalLineEntity)) {
                // 保存原始线段的正常配置 - 修复版本
                const material = originalLineEntity.polyline.material;
                if (material && material.getValue) {
                    originalLineMaterial = material.getValue();
                } else {
                    originalLineMaterial = Cesium.Color.BLUE.withAlpha(NORMAL_OPACITY);
                }
                originalLineWidth = originalLineEntity.polyline.width.getValue();
                
                console.log('原始线段配置:', { material: originalLineMaterial, width: originalLineWidth });
                
                // 精确计算目标高亮参数
                const targetWidth = Math.round(originalLineWidth * TARGET_HIGHLIGHT_MULTIPLIER);
                const targetOpacity = Math.min(1.0, NORMAL_OPACITY + TARGET_HIGHLIGHT_BRIGHTNESS_INCREASE);
                
                console.log(`目标高亮计算: 宽度=${originalLineWidth} × ${TARGET_HIGHLIGHT_MULTIPLIER} = ${targetWidth}`);
                console.log(`目标高亮计算: 透明度=${NORMAL_OPACITY} + ${TARGET_HIGHLIGHT_BRIGHTNESS_INCREASE} = ${targetOpacity}`);
                
                // 应用精确的目标高亮样式
                originalLineEntity.polyline.material = Cesium.Color.CYAN.withAlpha(targetOpacity);
                originalLineEntity.polyline.width = targetWidth;
                
                targetHighlightActive = true;
                console.log(`📍 目标高亮已激活:宽度×${TARGET_HIGHLIGHT_MULTIPLIER}(${targetWidth}),亮度+${TARGET_HIGHLIGHT_BRIGHTNESS_INCREASE}(${targetOpacity})`);
            }
        }

        // 取消目标高亮
        function deactivateTargetHighlight() {
            if (targetHighlightActive && originalLineEntity && viewer.entities.contains(originalLineEntity)) {
                // 恢复原始线段的正常配置 - 修复版本
                originalLineEntity.polyline.material = originalLineMaterial;
                originalLineEntity.polyline.width = originalLineWidth;
                
                targetHighlightActive = false;
                console.log('📍 目标高亮已取消:原始线段恢复正常状态');
            }
        }

        // 检查是否存在已分割的线段
        function hasExistingSegments() {
            return segmentedLines.length > 0 || 
                   (originalLineEntity && viewer.entities.contains(originalLineEntity));
        }

        // 自动重置
        function autoResetBeforeNewSplit() {
            console.log('🔄 检测到已有分割线段,执行自动重置...');
            
            status.innerHTML = '<span class="point-counter">⏳</span>正在自动重置,清除上次分割结果...';
            
            setTimeout(() => {
                // 首先取消目标高亮
                deactivateTargetHighlight();
                
                // 清理所有分段线段
                cleanupSegmentedLines();
                
                // 清理选中状态
                cleanupSelectionState();
                
                // 重新创建原始线段
                createOriginalLine();
                
                // 清理所有临时状态变量
                cleanupTemporaryStates();
                
                console.log('✅ 自动重置完成,可以开始新的分割');
            }, 200);
        }

        // 清理所有分段线段
        function cleanupSegmentedLines() {
            segmentedLines.forEach(segmentId => {
                if (viewer.entities.contains(segmentId)) {
                    viewer.entities.remove(segmentId);
                    console.log(`🗑️ 已删除分段: ${segmentId.name}`);
                }
            });
            segmentedLines = [];
        }

        // 清理选中状态
        function cleanupSelectionState() {
            if (selectedSegmentId) {
                try {
                    selectedSegmentId.polyline.width = NORMAL_WIDTH;
                    selectedSegmentId.polyline.material = selectedSegmentId.originalMaterial;
                } catch (e) {
                    // 忽略错误
                }
                selectedSegmentId = null;
            }
            deleteBtn.disabled = true;
        }

        // 清理临时状态
        function cleanupTemporaryStates() {
            if (splitPoints.length > 0) {
                console.log(`🗑️ 清除分割点: ${splitPoints.length}个`);
                splitPoints = [];
            }
            
            if (pointEntities.length > 0) {
                console.log(`🗑️ 清除点标记: ${pointEntities.length}个`);
                clearPointEntities();
            }
            
            if (tempSplitLine) {
                viewer.entities.remove(tempSplitLine);
                tempSplitLine = null;
            }
            
            removePreviewLine();
            currentMousePos = null;
            updatePointCounter();
        }

        // 手动重置
        function manualReset() {
            console.log('🔄 执行手动重置');
            
            if (isSplitMode) {
                isSplitMode = false;
                splitBtn.classList.remove('split-active');
                splitBtn.textContent = '分割线段';
                deactivateTargetHighlight();
            }
            
            cleanupSegmentedLines();
            cleanupSelectionState();
            cleanupTemporaryStates();
            
            createOriginalLine();
            
            status.innerHTML = '<span class="point-counter">0</span>🔄 手动重置完成,点击"分割线段"开始操作';
            console.log('✅ 手动重置完成');
        }

        // 处理地图点击
        function handleMapClick(event) {
            if (!isSplitMode) {
                checkSegmentClick(event);
                return;
            }
            addSplitPoint(event.position);
        }

        // 添加分割点
        function addSplitPoint(screenPosition) {
            const pickedPosition = viewer.camera.pickEllipsoid(screenPosition, viewer.scene.globe.ellipsoid);
            if (!pickedPosition) return;
            
            splitPoints.push(pickedPosition);
            createPointMarker(pickedPosition, splitPoints.length);
            updatePointCounter();

            if (splitPoints.length === 1) {
                status.innerHTML = '<span class="point-counter">1</span>已添加第一个点①,移动鼠标查看实时分割线,点击放置第二个点②';
                createPreviewSplitLine();
            } else if (splitPoints.length === 2) {
                removePreviewLine();
                createFinalSplitLine();
                
                setTimeout(() => {
                    performSplit();
                }, 300);
            }
        }

        // 创建点标记
        function createPointMarker(position, pointNumber) {
            const entity = viewer.entities.add({
                position: position,
                point: {
                    pixelSize: 24,
                    color: Cesium.Color.RED,
                    outlineColor: Cesium.Color.WHITE,
                    outlineWidth: 3,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
                },
                label: {
                    text: pointNumber.toString(),
                    font: 'bold 11px sans-serif',
                    pixelOffset: new Cesium.Cartesian2(0, -40),
                    fillColor: Cesium.Color.WHITE,
                    outlineColor: Cesium.Color.RED,
                    outlineWidth: 2,
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
                }
            });
            
            pointEntities.push(entity);
        }

        // 清除所有点标记
        function clearPointEntities() {
            pointEntities.forEach(entity => {
                if (viewer.entities.contains(entity)) {
                    viewer.entities.remove(entity);
                }
            });
            pointEntities = [];
        }

        // 更新点数计数器
        function updatePointCounter() {
            pointCounter.textContent = splitPoints.length;
        }

        // 创建预览分割线
        function createPreviewSplitLine() {
            if (previewSplitLine) {
                viewer.entities.remove(previewSplitLine);
            }
            
            previewSplitLine = viewer.entities.add({
                name: 'preview-split-line',
                polyline: {
                    positions: [splitPoints[0], splitPoints[0]],
                    width: 3,
                    material: Cesium.Color.ORANGE.withAlpha(0.8),
                    clampToGround: true
                }
            });
        }

        // 更新预览分割线
        function updatePreviewSplitLine(mousePos) {
            if (!previewSplitLine || splitPoints.length === 0) return;
            
            previewSplitLine.polyline.positions = [splitPoints[0], mousePos];
        }

        // 移除预览分割线
        function removePreviewLine() {
            if (previewSplitLine) {
                viewer.entities.remove(previewSplitLine);
                previewSplitLine = null;
            }
        }

        // 创建最终的分割线
        function createFinalSplitLine() {
            if (tempSplitLine) {
                viewer.entities.remove(tempSplitLine);
            }
            
            tempSplitLine = viewer.entities.add({
                name: 'temp-split-line',
                polyline: {
                    positions: splitPoints,
                    width: 3,
                    material: Cesium.Color.RED.withAlpha(1.0),
                    clampToGround: true
                }
            });
        }

        // 鼠标移动事件处理
        function handleMouseMove(event) {
            if (isSplitMode) {
                viewer.scene.canvas.style.cursor = 'crosshair';
                
                if (splitPoints.length === 1) {
                    const pickedPosition = viewer.camera.pickEllipsoid(event.endPosition, viewer.scene.globe.ellipsoid);
                    if (pickedPosition) {
                        currentMousePos = pickedPosition;
                        updatePreviewSplitLine(currentMousePos);
                    }
                }
            } else if (segmentedLines.length > 0) {
                // 检查是否悬停在分段线段上
                const pickedObject = viewer.scene.pick(event.endPosition);
                viewer.scene.canvas.style.cursor = pickedObject && segmentedLines.includes(pickedObject.id) ? 'pointer' : '';
            } else {
                viewer.scene.canvas.style.cursor = '';
            }
        }

        // 执行真正的几何分割
        function performSplit() {
            if (!originalLineEntity || !viewer.entities.contains(originalLineEntity)) {
                status.innerHTML = '<span class="point-counter">0</span>原始线段不存在,无法分割';
                cleanupSplitMode();
                return;
            }
            
            const positions = originalLineEntity.polyline.positions.getValue();
            const originalCoords = positions.map(pos => cartesianToDegrees(pos));
            
            console.log('原始线段坐标:', originalCoords);
            
            // 找到真正的分割交点
            const intersectionResult = findTrueIntersection(positions, splitPoints);
            
            if (!intersectionResult) {
                status.innerHTML = '<span class="point-counter">0</span>分割线与原线段未相交,请重新选择更接近线段的位置';
                cleanupSplitMode();
                return;
            }
            
            console.log('找到真实交点:', cartesianToDegrees(intersectionResult.intersectionPoint));
            console.log('分割位置: 线段段', intersectionResult.segmentIndex, '进度', intersectionResult.progress);
            
            // 在真实交点处分割线段
            const segments = splitLineAtTrueIntersection(positions, intersectionResult);
            
            // 移除原始线段
            viewer.entities.remove(originalLineEntity);
            originalLineEntity = null;
            
            // 清除临时分割线、预览线和点标记
            if (tempSplitLine) {
                viewer.entities.remove(tempSplitLine);
                tempSplitLine = null;
            }
            removePreviewLine();
            clearPointEntities();
            
            // 创建分段线段
            segmentedLines = [];
            const colors = [Cesium.Color.GREEN, Cesium.Color.RED, Cesium.Color.ORANGE, Cesium.Color.PURPLE, Cesium.Color.TEAL];
            
            segments.forEach((segmentPositions, index) => {
                const segmentId = viewer.entities.add({
                    name: `segment-${index}`,
                    polyline: {
                        positions: segmentPositions,
                        width: NORMAL_WIDTH,
                        material: colors[index % colors.length].withAlpha(NORMAL_OPACITY),
                        clampToGround: true
                    }
                });
                
                // 保存原始材质 - 修复版本
                const material = segmentId.polyline.material;
                if (material && material.getValue) {
                    segmentId.originalMaterial = material.getValue();
                } else {
                    segmentId.originalMaterial = colors[index % colors.length].withAlpha(NORMAL_OPACITY);
                }
                
                segmentedLines.push(segmentId);
            });
            
            cleanupSplitMode();
            status.innerHTML = '<span class="point-counter">0</span>✅ 线段已分割完成!点击任意段查看高亮效果';
        }

        // 找到真实的分割交点
        function findTrueIntersection(linePositions, splitLinePositions) {
            for (let i = 0; i < linePositions.length - 1; i++) {
                const segmentStart = linePositions[i];
                const segmentEnd = linePositions[i + 1];
                
                const intersection = findIntersectionOfTwoSegments(
                    splitLinePositions[0], splitLinePositions[1],
                    segmentStart, segmentEnd
                );
                
                if (intersection) {
                    const progress = calculateProgressOnSegment(intersection, segmentStart, segmentEnd);
                    
                    return {
                        intersectionPoint: intersection,
                        segmentIndex: i,
                        progress: progress,
                        method: 'direct_intersection'
                    };
                }
            }
            
            const splitMidpoint = new Cesium.Cartesian3();
            Cesium.Cartesian3.midpoint(splitLinePositions[0], splitLinePositions[1], splitMidpoint);
            
            const closestOnLine = findClosestPointOnLine(splitMidpoint, linePositions);
            
            if (closestOnLine.distance < 100) { // 100米范围内认为是接近的
                return {
                    intersectionPoint: closestOnLine.point,
                    segmentIndex: closestOnLine.segmentIndex,
                    progress: closestOnLine.progress,
                    method: 'proximity_snap'
                };
            }
            
            const midIndex = Math.floor(linePositions.length / 2);
            return {
                intersectionPoint: linePositions[midIndex],
                segmentIndex: midIndex,
                progress: 0,
                method: 'fallback_center'
            };
        }

        // 计算点在某线段段上的进度
        function calculateProgressOnSegment(point, segmentStart, segmentEnd) {
            const startCartographic = Cesium.Cartographic.fromCartesian(segmentStart);
            const endCartographic = Cesium.Cartographic.fromCartesian(segmentEnd);
            const pointCartographic = Cesium.Cartographic.fromCartesian(point);
            
            const dx = endCartographic.longitude - startCartographic.longitude;
            const dy = endCartographic.latitude - startCartographic.latitude;
            const segmentLengthSquared = dx * dx + dy * dy;
            
            if (segmentLengthSquared === 0) return 0;
            
            const pointDx = pointCartographic.longitude - startCartographic.longitude;
            const pointDy = pointCartographic.latitude - startCartographic.latitude;
            
            return (pointDx * dx + pointDy * dy) / segmentLengthSquared;
        }

        // 在真实交点处分割线段
        function splitLineAtTrueIntersection(positions, intersectionResult) {
            const splitIndex = intersectionResult.segmentIndex;
            const progress = intersectionResult.progress;
            const intersectionPoint = intersectionResult.intersectionPoint;
            
            console.log(`在第${splitIndex}段线段上,进度${progress}处分割`);
            
            if (intersectionResult.method === 'direct_intersection') {
                const segmentStart = positions[splitIndex];
                const segmentEnd = positions[splitIndex + 1];
                
                const exactIntersection = new Cesium.Cartesian3();
                Cesium.Cartesian3.lerp(segmentStart, segmentEnd, progress, exactIntersection);
                
                const segment1 = [...positions.slice(0, splitIndex + 1), exactIntersection];
                const segment2 = [exactIntersection, ...positions.slice(splitIndex + 1)];
                
                console.log('分段1:', segment1.length, '个点');
                console.log('分段2:', segment2.length, '个点');
                
                return [segment1, segment2];
            } else {
                const segment1 = positions.slice(0, splitIndex + 1);
                const segment2 = positions.slice(splitIndex);
                return [segment1, segment2];
            }
        }

        // 检查分段点击 - 修复版本
        function checkSegmentClick(event) {
            if (segmentedLines.length === 0) return;
            
            const pickedObject = viewer.scene.pick(event.position);
            if (pickedObject && segmentedLines.includes(pickedObject.id)) {
                const clickedEntity = pickedObject.id;
                
                if (selectedSegmentId === clickedEntity) {
                    return;
                }
                
                if (selectedSegmentId) {
                    selectedSegmentId.polyline.width = NORMAL_WIDTH;
                    selectedSegmentId.polyline.material = selectedSegmentId.originalMaterial;
                }
                
                selectedSegmentId = clickedEntity;
                selectedSegmentId.polyline.width = SELECTED_WIDTH;
                
                // 修复:正确处理材质获取和应用
                try {
                    const originalMaterial = selectedSegmentId.originalMaterial;
                    const brightenedMaterial = adjustColorBrightness(originalMaterial, 40);
                    selectedSegmentId.polyline.material = brightenedMaterial.withAlpha(SELECTED_OPACITY);
                } catch (error) {
                    console.warn('材质处理出错,使用默认材质:', error);
                    selectedSegmentId.polyline.material = Cesium.Color.YELLOW.withAlpha(SELECTED_OPACITY);
                }
                
                deleteBtn.disabled = false;
                const segmentIndex = parseInt(clickedEntity.name.split('-')[1]);
                status.innerHTML = `<span class="point-counter">0</span>已选中第${segmentIndex + 1}段 🎯 宽度×1.5倍(${SELECTED_WIDTH}),亮度+40%,点击"删除选中线段"进行删除`;
            }
        }

        // 删除选中的线段段
        function deleteSelectedSegment() {
            if (!selectedSegmentId || segmentedLines.length === 0) return;
            
            viewer.entities.remove(selectedSegmentId);
            
            const index = segmentedLines.indexOf(selectedSegmentId);
            if (index > -1) {
                segmentedLines.splice(index, 1);
            }
            
            selectedSegmentId = null;
            deleteBtn.disabled = true;
            
            if (segmentedLines.length === 0) {
                status.innerHTML = '<span class="point-counter">0</span>所有线段已被删除,点击"分割线段"开始操作(或"手动重置")';
            } else {
                status.innerHTML = `<span class="point-counter">0</span>成功删除一段,还剩${segmentedLines.length}段`;
            }
        }

        // 清理分割模式
        function cleanupSplitMode() {
            isSplitMode = false;
            splitPoints = [];
            tempSplitLine = null;
            previewSplitLine = null;
            currentMousePos = null;
            
            deactivateTargetHighlight();
            
            splitBtn.classList.remove('split-active');
            splitBtn.textContent = '分割线段';
            splitBtn.disabled = false;
        }

        // 启动应用
        initializeApp();
    </script>
</body>
</html>
相关推荐
YAY_tyy15 小时前
Cesium 基础:地球场景初始化与视角控制
前端·cesium
ct9788 天前
Cesium中的CZML
学习·gis·cesium
weipt10 天前
关于vue项目中cesium的地图显示问题
前端·javascript·vue.js·cesium·卫星影像·地形
YAY_tyy11 天前
综合实战:基于 Turfjs 的智慧园区空间管理系统
前端·3d·cesium·turfjs
haokan_Jia11 天前
【三、基于Cesium的三维智慧流域四预系统-轻量级搭建】
cesium
YAY_tyy11 天前
Turfjs 性能优化:大数据量地理要素处理技巧
前端·3d·arcgis·cesium·turfjs
Tiam-201611 天前
cesium使用cesium-plot-js标绘多种图形
javascript·vue.js·arcgis·es6·gis·cesium·cesium-plot-js
gis_rc17 天前
python下shp转3dtiles
python·3d·cesium·3dtiles·数字孪生模型
grasperp18 天前
3DTiles数据切片工具,支持LAS、OBJ、FBX 3DTiles怎么切片?3DTiles切片
cesium·3dtiles·三维gis·3dtiles切片·数据切片