一、主要功能特点
- 加载线段
页面加载时自动创建一条蓝色原始线段
线段包含多个坐标点,形成折线
- 分割功能
点击"分割线段"按钮进入分割模式
用户在线段两侧依次点击两个点
实时显示红色虚线分割线
500毫秒后自动执行分割,原始线段被分成两段不同颜色的线段
分割线自动消失
- 选择和删除功能
点击任意分段线段会高亮显示(加粗+透明度变化)
"删除选中线段"按钮变为可用
点击按钮删除当前选中的线段段
支持连续删除多个段
- 用户界面
清晰的控制面板
实时状态提示
使用说明
重置功能
- 交互体验
鼠标悬停效果
按钮状态管理
错误处理和提示
使用说明:
替换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>