fix bug修复

This commit is contained in:
gcw_IJ7DAiVL
2025-10-16 16:02:23 +08:00
parent ce70fd98ec
commit 964ce7cede
2 changed files with 3008 additions and 465 deletions

View File

@ -580,17 +580,42 @@ export default {
// 2. 处理 GeoJSON 数据 // 2. 处理 GeoJSON 数据
this.roadNetworkGeoJSON = shpBuffer this.roadNetworkGeoJSON = shpBuffer
/// 3. 将 GeoJSON 数据添加到 graphicLayer /// 3. 将 GeoJSON 数据添加到 graphicLayer
// this.roadNetworkGeoJSON.features.forEach((feature) => {
// // ====网路图=====
// const graphicLine = new window.mars3d.graphic.PolylineEntity({
// positions: feature.geometry.coordinates[0],
// style: {
// color: '#FF0000',
// width: 2,
// outline: false,
// },
// })
// this.graphicLayer.addGraphic(graphicLine);
// })
// 定义颜色数组
const colors = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#FFA500', '#800080', '#008000', '#000080'];
// 绘制路网线时循环使用颜色
let colorIndex = 0; // 用于跟踪当前颜色索引
this.roadNetworkGeoJSON.features.forEach((feature) => { this.roadNetworkGeoJSON.features.forEach((feature) => {
// ====网路图===== // 获取当前颜色
const currentColor = colors[colorIndex % colors.length];
// 创建路网线图形
const graphicLine = new window.mars3d.graphic.PolylineEntity({ const graphicLine = new window.mars3d.graphic.PolylineEntity({
positions: feature.geometry.coordinates[0], positions: feature.geometry.coordinates[0],
style: { style: {
color: '#FF0000', color: "#FF0000", // 使用当前颜色
width: 2, width: 2,
outline: false, outline: false,
}, },
}) });
// 将图形添加到图层
this.graphicLayer.addGraphic(graphicLine); this.graphicLayer.addGraphic(graphicLine);
// 更新颜色索引
colorIndex++;
}) })
this.roadNetworkGeoJSONBuild = this.buildGraph(this.roadNetworkGeoJSON) this.roadNetworkGeoJSONBuild = this.buildGraph(this.roadNetworkGeoJSON)
this.mapLoading = false this.mapLoading = false
@ -1604,488 +1629,431 @@ export default {
const fullPath = [] const fullPath = []
const infoList = [] const infoList = []
let prevSegmentEndNode = null; // 存储上一个途经点的衔接点(用于下一段复用)
for (let i = 0; i < points.length - 1; i++) { for (let i = 0; i < points.length - 1; i++) {
const currPoint = points[i]; // 当前点(起点/途经点) const currPoint = points[i]; // 当前点(起点/途经点)
const nextPoint = points[i + 1]; // 下一个点(途经点/终点) const nextPoint = points[i + 1]; // 下一个点(途经点/终点)
const segmentStart = currPoint.coord; const segmentStart = currPoint.coord;
const segmentEnd = nextPoint.coord; const segmentEnd = nextPoint.coord;
// 匹配最近节点:关键修改——若当前是“途经点之后的段”,复用前一段的衔接点作为起 // 查找最近的边(线段)而不是节
let startNode, endNode; let startEdgeInfo = this.findNearestEdge(segmentStart, tempGraph, nodeCoords);
if (currPoint.type === 'via' && prevSegmentEndNode ) { let endEdgeInfo = this.findNearestEdge(segmentEnd, tempGraph, nodeCoords);
// 直接使用上一段的结束节点作为起点(确保路径连续性)
startNode = prevSegmentEndNode;
} else {
// 情况2起点/第一段,正常匹配最近节点
startNode = this.findNearestNode(segmentStart, nodeCoords);
}
endNode = this.findNearestNodeWithReturn(segmentEnd, nodeCoords, tempGraph, startNode);
if (!startNode || !endNode) { if (!startEdgeInfo || !endEdgeInfo) {
console.error('无法匹配到路网节点') console.error("无法匹配到路网边")
prevSegmentEndNode = null; // 重置衔接点,避免后续复用错误
continue continue
} }
// 计算垂直连接点
const startConnection = this.calculatePerpendicularConnection(segmentStart, startEdgeInfo, currPoint.type);
const endConnection = this.calculatePerpendicularConnection(segmentEnd, endEdgeInfo, nextPoint.type);
// 检查路径可行性 // 构建包含垂直连接点的临时图
if (!this.isPathPossible(tempGraph, startNode, endNode)) { const extendedGraph = this.buildExtendedGraph(tempGraph, nodeCoords, startConnection, endConnection);
this.$message.warning(`无法匹配到路网节点`);
continue;
}
// 检查路径可行性(考虑死胡同原路返回) // 查找路径
const {pathNodes, isDeadEnd} = await this.findPathWithReturn(tempGraph, startNode, endNode, nodeCoords); const pathNodes = await this.findPathWithPerpendicularConnections(
extendedGraph,
startConnection.tempNodeId,
endConnection.tempNodeId
);
if (!pathNodes || pathNodes.length === 0) { if (!pathNodes || pathNodes.length === 0) {
this.$message.warning(`${i+1}段路径未找到!`) this.$message.warning(`${i+1}段路径未找到!`)
prevSegmentEndNode = null;
continue continue
} }
// 生成当前段的路径(处理死胡同原路返回) // 生成当前段的路径(处理死胡同原路返回)
const segmentResult = await this.generatePathWithReturn( const segmentResult = await this.generatePathWithPerpendicularConnections(
pathNodes, pathNodes,
segmentStart, segmentStart,
segmentEnd, segmentEnd,
i, startConnection,
points.length, endConnection,
currPoint.type, extendedGraph
nextPoint.type,
isDeadEnd,
prevSegmentEndNode
); );
if (segmentResult.path.length > 0) { if (segmentResult.path.length > 0) {
// 合并路径段 // 合并路径段
this.mergePathSegments(fullPath, segmentResult.path); this.mergePathSegments(fullPath, segmentResult.path);
infoList.push(...segmentResult.segments); infoList.push(...segmentResult.segments);
// 记录当前段的结束节点,供下一段使用
prevSegmentEndNode = pathNodes[pathNodes.length - 1];
// if (i === 0) {
// // 第一段:直接添加
// fullPath.push(...segmentResult.path);
// } else {
// // 后续段:需要检查连接点,避免重复
// this.mergePathSegments(fullPath, segmentResult.path);
// }
// infoList.push(...segmentResult.segments);
// 关键:若当前段的终点是“途经点”,记录其衔接点,供下一段复用
// if (nextPoint.type === 'via' && segmentResult.viaConnectPoint) {
// prevSegmentEndNode = segmentResult.viaConnectPoint;
// } else {
// prevSegmentEndNode = null; // 非途经点,重置
// }
} else {
prevSegmentEndNode = null;
} }
} }
return {fullPath, infoList} return {fullPath, infoList}
}, },
// 改进:查找最近节点,考虑死胡同情况 // 合并路径段
findNearestNodeWithReturn(coord, nodeCoords, graph, fromNode = null) { mergePathSegments(fullPath, newSegment) {
const nearestNode = this.findNearestNode(coord, nodeCoords); if (!newSegment || newSegment.length === 0) return;
if (fullPath.length === 0) {
// 如果没有起点节点或不是死胡同,直接返回最近节点 fullPath.push(...newSegment);
if (!fromNode || !this.isDeadEnd(nearestNode, graph)) { return;
return nearestNode;
} }
const lastPoint = fullPath[fullPath.length - 1];
// 如果是死胡同,找到死胡同的入口节点(原路返回点) const firstNewPoint = newSegment[0];
return this.findDeadEndEntry(nearestNode, graph, fromNode, nodeCoords); const dist = this.calculateDistance(lastPoint, firstNewPoint);
if (dist > 1e-2) {
// 如果断开了超过1cm就补上连接线
fullPath.push(firstNewPoint);
}
fullPath.push(...newSegment.slice(1));
}, },
// 查找最近的边(线段)
findNearestEdge(pointCoord, graph, nodeCoords) {
let nearestEdge = null;
let minDistance = Infinity;
let perpendicularPoint = null;
// 为了兼容你最初 nodeCoords 结构(你之前用 coords[0][0]),下面我们从 feature.geometry.coordinates 里取实际线数组
const features = this.roadNetworkGeoJSON.features;
// 判断节点是否是死胡同(只有一个连接) for (const feature of features) {
isDeadEnd(node, graph) { // 支持 LineString 或 MultiLineString取每条线段
if (!graph[node]) return true; const geom = feature.geometry;
const connections = Object.keys(graph[node]); let lines = [];
return connections.length === 1; if (geom.type === 'LineString') {
}, lines = [geom.coordinates];
} else if (geom.type === 'MultiLineString') {
// 找到死胡同的入口节点(原路返回的衔接点) lines = geom.coordinates;
findDeadEndEntry(deadEndNode, graph, fromNode, nodeCoords) { } else {
// 从死胡同节点开始,沿着唯一路径往回找,直到找到分支点或起点 continue;
let currentNode = deadEndNode;
let visited = new Set([currentNode]);
while (currentNode) {
const connections = graph[currentNode] ? Object.keys(graph[currentNode]) : [];
// 如果当前节点有多个连接,或者是从起点过来的节点,作为入口点
if (connections.length > 1 || currentNode === fromNode) {
return currentNode;
} }
// 继续往回找(排除已访问的节点) for (const lineCoords of lines) {
const nextNode = connections.find(node => !visited.has(node)); // turf.nearestPointOnLine 需要 lineString数组经纬返回距离单位度->通过 properties.dist
if (!nextNode) break; const line = turf.lineString(lineCoords);
const nearest = turf.nearestPointOnLine(line, turf.point(pointCoord), {units: 'meters'});
// 注意turf.nearestPointOnLine 返回的 properties.dist 是以图层单位(默认度),但在新版本通常有 `dist` (米) 根据 turf 版本会不同
// 为保险,用 turf.distance 重新计算米级距离:
const proj = nearest.geometry.coordinates;
const distMeters = turf.distance(turf.point(pointCoord), turf.point(proj), {units: 'meters'});
visited.add(nextNode); if (distMeters < minDistance) {
currentNode = nextNode; minDistance = distMeters;
} perpendicularPoint = proj;
// 保存 edge 信息:使用 feature 的 FNODE_/TNODE_ 作为端点(同你 graph 的节点)
// 如果找不到合适的入口,返回原始最近节点 nearestEdge = {
return deadEndNode; feature,
}, lineCoords,
startNode: feature.properties.FNODE_,
// 改进的路径查找,支持原路返回 endNode: feature.properties.TNODE_,
async findPathWithReturn(graph, startNode, endNode, nodeCoords) { distance: distMeters
try {
// 先尝试直接路径
const directPath = dijkstra.find_path(graph, startNode, endNode);
if (directPath && directPath.length > 0) {
return {pathNodes: directPath, isDeadEnd: false};
}
// 如果直接路径失败,检查是否是死胡同情况
if (this.isDeadEnd(endNode, graph)) {
// 找到死胡同入口作为替代终点
const entryNode = this.findDeadEndEntry(endNode, graph, startNode, nodeCoords);
if (entryNode && entryNode !== endNode) {
const alternativePath = dijkstra.find_path(graph, startNode, entryNode);
if (alternativePath && alternativePath.length > 0) {
return {
pathNodes: alternativePath,
isDeadEnd: true,
actualEndNode: endNode,
entryNode: entryNode
}; };
} }
} }
} }
return {pathNodes: null, isDeadEnd: false}; if (!nearestEdge) return null;
return {
startNode: nearestEdge.startNode,
endNode: nearestEdge.endNode,
line: turf.lineString(nearestEdge.lineCoords),
distance: nearestEdge.distance,
perpendicularPoint: perpendicularPoint,
tempNodeId: `temp_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,
feature: nearestEdge.feature,
lineCoords: nearestEdge.lineCoords
};
},
// 计算垂直连接信息(保持原结构)
calculatePerpendicularConnection(point, edgeInfo, type = 'start') {
// type: 'start' | 'via' | 'end'
return {
originalPoint: point,
perpendicularPoint: edgeInfo.perpendicularPoint,
edgeStart: edgeInfo.startNode,
edgeEnd: edgeInfo.endNode,
tempNodeId: edgeInfo.tempNodeId,
distance: edgeInfo.distance,
type, // 新增标记,后续拼接方向要用
feature: edgeInfo.feature,
lineCoords: edgeInfo.lineCoords
}
},
// 构建包含垂直连接点的扩展图(改进:会把临时节点连到对应的两个端点,权重为米级距离)
buildExtendedGraph(originalGraph, nodeCoords, startConnection, endConnection) {
// 深拷贝原图
const extendedGraph = JSON.parse(JSON.stringify(originalGraph));
const addTemp = (conn) => {
if (!conn) return;
const tempId = conn.tempNodeId;
extendedGraph[tempId] = extendedGraph[tempId] || {};
// 取对应两个端点的经纬nodeCoords 存储节点经纬)
const coordA = nodeCoords[conn.edgeStart];
const coordB = nodeCoords[conn.edgeEnd];
// 如果 nodeCoords 中没有任意端点坐标,尝试从 feature geometry 取头尾
if (!coordA || !coordB) {
const fcoords = conn.lineCoords;
// 头尾可能需要调整,根据你的 data 结构fcoords[0]/fcoords[fcoords.length-1]
if (!coordA) coordA = fcoords[0];
if (!coordB) coordB = fcoords[fcoords.length - 1];
}
const dA = this.calculateDistance(conn.perpendicularPoint, coordA);
const dB = this.calculateDistance(conn.perpendicularPoint, coordB);
// temp -> A/B
extendedGraph[tempId][conn.edgeStart] = dA;
extendedGraph[tempId][conn.edgeEnd] = dB;
// A/B -> temp (保证无向双向)
if (!extendedGraph[conn.edgeStart]) extendedGraph[conn.edgeStart] = {};
if (!extendedGraph[conn.edgeEnd]) extendedGraph[conn.edgeEnd] = {};
extendedGraph[conn.edgeStart][tempId] = dA;
extendedGraph[conn.edgeEnd][tempId] = dB;
};
addTemp(startConnection);
addTemp(endConnection);
return extendedGraph;
},
// 查找包含垂直连接的路径(你已有 dijkstra.find_path保持不变
async findPathWithPerpendicularConnections(graph, startNode, endNode) {
try {
const path = dijkstra.find_path(graph, startNode, endNode);
return path && path.length > 0 ? path : null;
} catch (error) { } catch (error) {
console.error('路径查找错误:', error); console.error('路径查找错误:', error);
return {pathNodes: null, isDeadEnd: false}; return null;
} }
}, },
// 通过坐标反向查找对应的路网节点(处理浮点数精度)
findNodeByCoord(coord, nodeCoords) { // 辅助在一条线lineCoords上找到垂足点最近的索引位置返回最近点索引
const tolerance = 0.000001; // 与findPointIndex保持一致的容差 // 如果垂足不在顶点上,会返回两个点之间最近的后续起点索引(便于切片)
for (const [nodeId, nodeCoord] of Object.entries(nodeCoords)) { findNearestIndexOnLine(lineCoords, perpPoint) {
if ( let minDist = Infinity;
Math.abs(nodeCoord[0] - coord[0]) < tolerance && let nearestIndex = 0;
Math.abs(nodeCoord[1] - coord[1]) < tolerance for (let i = 0; i < lineCoords.length; i++) {
) { const d = turf.distance(turf.point(lineCoords[i]), turf.point(perpPoint), {units: 'meters'});
return nodeId; if (d < minDist) {
minDist = d;
nearestIndex = i;
} }
} }
// 若未找到完全匹配的节点,返回最近的节点(降级处理) return { index: nearestIndex, distance: minDist };
return this.findNearestNode(coord, nodeCoords);
}, },
// 改进的路径生成,支持原路返回
async generatePathWithReturn( // 根据路径节点node id 列表)生成最终的经纬路径(含垂足点)
// 说明pathNodes 包含临时节点 temp_xxxstartConnection/endConnection 帮助在首尾插入垂足
async generatePathWithPerpendicularConnections(
pathNodes, pathNodes,
segmentStart, segmentStart,
segmentEnd, segmentEnd,
segmentIndex, startConnection,
totalPoints, endConnection,
currPointType, graph
nextPointType,
isDeadEnd = false,
prevSegmentEndNode = null
) { ) {
const segmentPath = []; const segmentPath = [];
const segments = []; const segments = [];
let hasValidSegment = false;
// 新增:存储途经点垂直点之后的路网后半截(供后续路径延续)
let viaPerpRemainingCoords = [];
// 生成主要路径段 // 1. 起点 -> 垂足(直线)
for (let j = 0; j < pathNodes.length - 1; j++) { if (startConnection.type !== "via") {
const currentNode = pathNodes[j]; // 起点
const nextNode = pathNodes[j + 1]; segmentPath.push(segmentStart);
segmentPath.push(startConnection.perpendicularPoint);
// return { path: [], segments: [] };
} else {
// 途径点
segmentPath.push(segmentStart);
// segmentPath.push(startConnection.perpendicularPoint);
// return { path: [], segments: [] };
}
// 2. 去掉 temp 节点,保留路网节点序列(但我们需要完整 pathNodes 来判断是否直接由 temp -> temp
// 但在拼接线段时我们会遍历 pathNodes 的相邻 pair包含真实节点
for (let idx = 0; idx < pathNodes.length - 1; idx++) {
const curNode = pathNodes[idx];
const nextNode = pathNodes[idx + 1];
// 如果任意一端是临时节点temp_跳过真实线段查找逻辑临时节点已经在扩展图里用直连距离
if (curNode.startsWith('temp_') || nextNode.startsWith('temp_')) {
// 当 cur 是 temp 且 next 是真实节点 -> 需要把垂足点连到 nextNode 所在路段的最近点
// 但我们已在扩展图把 temp 与它相连的端点edgeStart/edgeEnd连接上了所以这里可以直接跳过具体线段拼接
// 为了保证路径的连续性,当 temp 后面直接接真实节点(如 A我们应该把垂足点到 A 的连线在上一部分已经通过 extendedGraph 权重计算,
// 但这里仍需把真正的路段几何feature加入以便在地图上显示路段细节故继续到下一部分的真实-真实节点处理
continue;
}
// curNode 与 nextNode 都是真实节点:找到你路网 feature可能有多段匹配取第一个匹配并决定方向
let segment = null; let segment = null;
if (!this.join) { if (!this.join) {
segment = this.roadNetworkGeoJSON.features.find( segment = this.roadNetworkGeoJSON.features.find(f =>
(f) => (String(f.properties.FNODE_) === String(curNode) && String(f.properties.TNODE_) === String(nextNode)) ||
(f.properties.FNODE_ == currentNode && f.properties.TNODE_ == nextNode) || (String(f.properties.FNODE_) === String(nextNode) && String(f.properties.TNODE_) === String(curNode))
(f.properties.FNODE_ == nextNode && f.properties.TNODE_ == currentNode)
); );
} else { } else{
segment = this.roadNetworkGeoJSON.features.find( segment = this.roadNetworkGeoJSON.features.find(
(f) => (f) =>
((f.properties.FNODE_ == currentNode && f.properties.TNODE_ == nextNode) || ((f.properties.FNODE_ == curNode && f.properties.TNODE_ == nextNode) ||
(f.properties.FNODE_ == nextNode && f.properties.TNODE_ == currentNode)) && (f.properties.FNODE_ == nextNode && f.properties.TNODE_ == curNode)) &&
f.properties.载重吨 >= this.inputform.load && f.properties.载重吨 >= this.inputform.load &&
f.properties.宽度 >= this.inputform.width && f.properties.宽度 >= this.inputform.width &&
f.properties.曲率半 <= this.inputform.minTurnRadius f.properties.曲率半 <= this.inputform.minTurnRadius
); );
} }
if (!segment) continue; if (!segment) {
console.warn(`找不到符合条件的路段: ${curNode} -> ${nextNode}`);
// 若找不到对应路段(可能被避让删掉或数据不一致),则跳过
continue;
}
if (segment) {
hasValidSegment = true;
segments.push(segment); segments.push(segment);
const segmentCoords = segment.geometry.coordinates[0]; // 取线数组LineString 或 MultiLine 的第一条)
let segCoords = [];
if (segment.geometry.type === 'LineString') {
segCoords = segment.geometry.coordinates;
} else if (segment.geometry.type === 'MultiLineString') {
segCoords = segment.geometry.coordinates[0];
}
// 处理段内连接 // 确定方向:如果 FNODE_ === curNode则线方向为 segCoords 正序,否则需要反转
if (j === 0) { const isForward = String(segment.properties.FNODE_) === String(curNode);
await this.handleSegmentStartWithReturn( const coordsToUse = isForward ? segCoords : [...segCoords].reverse();
segmentPath, segmentStart, segmentCoords, segmentIndex, currPointType, prevSegmentEndNode
); // === 使用第一个版本的精细连接逻辑,但加入第二个版本的途经点切片 ===
const lastPt = segmentPath[segmentPath.length - 1];
const firstOfSeg = coordsToUse[0];
const distLastToFirst = this.calculateDistance(lastPt, firstOfSeg);
// 如果是途经点,需要找到终点垂足在当前位置
const endPerpNearest = this.findNearestPointWithIndex(coordsToUse, endConnection.perpendicularPoint);
const endNi = endPerpNearest.index;
if (distLastToFirst < 1e-6) {
// 精度上相同,直接接上(跳过第一个)
// 如果是途经点,需要切片到垂足
if (endConnection.type === 'via') {
segmentPath.push(...coordsToUse.slice(1, endNi + 1));
// 保存垂直点之后的路网后半截
viaPerpRemainingCoords = coordsToUse.slice(endNi);
} else { } else {
this.connectSegmentInternally(segmentPath, segmentCoords); segmentPath.push(...coordsToUse.slice(1));
}
} else {
// 找到 coordsToUse 上与 lastPt 最近的索引
const nearestInfo = this.findNearestPointWithIndex(coordsToUse, lastPt);
const ni = nearestInfo.index;
// === 整合第二个版本的途经点切片逻辑 ===
if (endConnection.type === 'via') {
// 途经点处理:统一以终点垂足为切片终点
if (ni <= endNi) {
segmentPath.push(...coordsToUse.slice(ni, endNi + 1));
// 关键:保存垂直点之后的路网后半截(供后续路径延续)
viaPerpRemainingCoords = coordsToUse.slice(endNi);
} else {
const reversedSlice = coordsToUse.slice(endNi, ni + 1).reverse();
segmentPath.push(...reversedSlice);
// 关键:保存垂直点之后的路网后半截(反向场景需要反转回去)
const originalRemaining = coordsToUse.slice(endNi);
viaPerpRemainingCoords = originalRemaining.reverse();
}
} else {
// 非途经点:使用第一个版本的完整连接逻辑
if (ni === 0) {
// 从头开始接(直接接)
segmentPath.push(...coordsToUse);
} else if (ni === coordsToUse.length - 1) {
// 最近点是段尾 —— 说明我们需要反向接(取反转)
const rev = [...coordsToUse].reverse();
// 以 rev 的第一个点连接
if (this.calculateDistance(lastPt, rev[0]) < 1e-6) {
segmentPath.push(...rev.slice(1));
} else {
// 否则直接把最近点加入并向最近端延伸(避免断链)
segmentPath.push(coordsToUse[ni]);
// 选择靠近终点的方向延伸(更短的一侧)
const distToStart = this.calculateDistance(coordsToUse[ni], coordsToUse[0]);
const distToEnd = this.calculateDistance(coordsToUse[ni], coordsToUse[coordsToUse.length - 1]);
if (distToStart <= distToEnd) {
const toStart = coordsToUse.slice(0, ni).reverse();
segmentPath.push(...toStart);
} else {
const toEnd = coordsToUse.slice(ni + 1);
segmentPath.push(...toEnd);
} }
} }
} else {
// 最近点在中间:选择向起点或终点延伸,取较短的一侧
segmentPath.push(coordsToUse[ni]);
const distToStart = this.calculateDistance(coordsToUse[ni], coordsToUse[0]);
const distToEnd = this.calculateDistance(coordsToUse[ni], coordsToUse[coordsToUse.length - 1]);
if (distToStart <= distToEnd) {
const toStart = coordsToUse.slice(0, ni).reverse();
segmentPath.push(...toStart);
} else {
const toEnd = coordsToUse.slice(ni + 1);
segmentPath.push(...toEnd);
}
}
}
}
}
}
// 如果没有任何有效的路段,返回空路径
if (!hasValidSegment) {
this.$message.warning('整段路径中没有找到任何符合条件的路段');
return { path: [], segments: [] };
}
// 3. 最后加上终点垂足 -> 终点 的直线
// === 处理终点和途径点的垂足连线 ===
// 确保终点/途径点垂足处理
if (endConnection.type === 'end') {
const endLineCoords = endConnection.lineCoords;
const lastPt = segmentPath[segmentPath.length - 1];
// 处理段结束连接(支持死胡同原路返回) // 找到路径末尾点在线段上最近的点索引
await this.handleSegmentEndWithReturn( const nearestToLast = this.findNearestPointWithIndex(endLineCoords, lastPt);
segmentPath, segmentEnd, pathNodes, segmentIndex, totalPoints, nextPointType, isDeadEnd const nearestToPerp = this.findNearestPointWithIndex(endLineCoords, endConnection.perpendicularPoint);
);
let subLine;
if (nearestToLast.index <= nearestToPerp.index) {
// 从 lastPt -> 垂足方向
subLine = endLineCoords.slice(nearestToLast.index, nearestToPerp.index + 1);
} else {
// 反向
subLine = endLineCoords.slice(nearestToPerp.index, nearestToLast.index + 1).reverse();
}
// 将最近线段拼到路径末尾(确保方向合理)
this.connectSegmentInternally(segmentPath, subLine);
// 最后垂足 -> 点
segmentPath.push(endConnection.perpendicularPoint);
segmentPath.push(segmentEnd);
} else if (endConnection.type === 'via') {
segmentPath.push(endConnection.perpendicularPoint);
segmentPath.push(segmentEnd);
segmentPath.push(endConnection.perpendicularPoint);
// 结束点为途经点:
// a. 先添加垂直点后续的路网后半截(供下一段路径延续)
if (viaPerpRemainingCoords.length > 1) {
// 跳过第一个点(与垂直点重复),添加后续部分
segmentPath.push(...viaPerpRemainingCoords.slice(1));
}
} else {
// 默认逻辑
segmentPath.push(endConnection.perpendicularPoint);
segmentPath.push(segmentEnd);
}
if (startConnection.type == 'via' && segmentPath.length > 0) {
segmentPath.shift(); // 移除数组第一个元素
}
return { path: segmentPath, segments }; return { path: segmentPath, segments };
}, },
// 改进的段起始处理
async handleSegmentStartWithReturn(segmentPath, segmentStart, segmentCoords, segmentIndex, currPointType, prevSegmentEndNode) {
if (segmentIndex === 0 || currPointType === 'start') {
// 第一段或起点:从实际起点开始
segmentPath.push(segmentStart);
const nearestPoint = this.findNearestPointOnLine(segmentStart, segmentCoords);
segmentPath.push(nearestPoint);
const startIndex = this.findPointIndex(segmentCoords, nearestPoint);
if (startIndex !== -1 && startIndex < segmentCoords.length - 1) {
segmentPath.push(...segmentCoords.slice(startIndex + 1));
}
} else {
// 途经点之后的段:确保路径连续性
segmentPath.push(segmentStart);
// 找到与上一段衔接的最佳点
const connectionPoint = this.findContinuationPoint(segmentCoords, prevSegmentEndNode);
if (connectionPoint) {
segmentPath.push(connectionPoint);
const connectionIndex = this.findPointIndex(segmentCoords, connectionPoint);
if (connectionIndex !== -1 && connectionIndex < segmentCoords.length - 1) {
segmentPath.push(...segmentCoords.slice(connectionIndex + 1));
}
} else {
// 降级处理:使用最近点
const nearestPoint = this.findNearestPointOnLine(segmentStart, segmentCoords);
segmentPath.push(nearestPoint);
const startIndex = this.findPointIndex(segmentCoords, nearestPoint);
if (startIndex !== -1 && startIndex < segmentCoords.length - 1) {
segmentPath.push(...segmentCoords.slice(startIndex + 1));
}
}
}
},
// 改进的段结束处理(支持死胡同)
async handleSegmentEndWithReturn(segmentPath, segmentEnd, pathNodes, segmentIndex, totalPoints, nextPointType, isDeadEnd) {
if (isDeadEnd) {
// 死胡同情况:先到达目标点,然后原路返回到入口点
const lastRoadNode = pathNodes[pathNodes.length - 1];
const nearestPoint = this.findBestConnectionPoint(segmentEnd, lastRoadNode);
if (nearestPoint) {
// 前往目标点
segmentPath.push(nearestPoint);
segmentPath.push(segmentEnd);
// 原路返回到入口点(反向路径)
this.retracePath(segmentPath, pathNodes);
}
} else {
// 正常情况
if (segmentIndex === totalPoints - 2) {
// 最后一段:连接到实际终点
const lastRoadNode = pathNodes[pathNodes.length - 1];
const nearestPoint = this.findBestConnectionPoint(segmentEnd, lastRoadNode);
if (nearestPoint) {
segmentPath.push(nearestPoint);
segmentPath.push(segmentEnd);
}
} else {
// 中间段:途经点处理
const lastRoadNode = pathNodes[pathNodes.length - 1];
const nearestPoint = this.findBestConnectionPoint(segmentEnd, lastRoadNode);
if (nearestPoint) {
segmentPath.push(nearestPoint);
segmentPath.push(segmentEnd);
}
}
}
},
// 原路返回逻辑
retracePath(segmentPath, pathNodes) {
// 从路径节点反向生成返回路径
for (let i = pathNodes.length - 2; i >= 0; i--) {
const currentNode = pathNodes[i];
const nextNode = pathNodes[i + 1];
let segment = this.roadNetworkGeoJSON.features.find(
(f) =>
(f.properties.FNODE_ == currentNode && f.properties.TNODE_ == nextNode) ||
(f.properties.FNODE_ == nextNode && f.properties.TNODE_ == currentNode)
);
if (!segment) continue;
const segmentCoords = [...segment.geometry.coordinates[0]].reverse();
this.connectSegmentInternally(segmentPath, segmentCoords);
}
},
// 找到路径延续点(确保路径连续性)
findContinuationPoint(segmentCoords, prevEndNode) {
if (!prevEndNode) return null;
const nodeCoords = this.roadNetworkGeoJSONBuild.nodeCoords[prevEndNode];
if (!nodeCoords) return null;
// 在路段上找到与上一段结束点最近的点
return this.findNearestPointOnLine(nodeCoords, segmentCoords);
},
// 处理段起始连接
async handleSegmentStart(segmentPath, segmentStart, segmentCoords, segmentIndex) {
if (segmentIndex === 0) {
// 第一段:从实际起点开始
segmentPath.push(segmentStart);
// 找到距离起点最近的道路点
const nearestPoint = this.findNearestPointOnLine(segmentStart, segmentCoords);
segmentPath.push(nearestPoint);
// 添加从最近点到路径的剩余部分
const startIndex = this.findPointIndex(segmentCoords, nearestPoint);
if (startIndex !== -1 && startIndex < segmentCoords.length - 1) {
segmentPath.push(...segmentCoords.slice(startIndex + 1));
}
} else {
// 中间段(途经点之后):从途经点开始
segmentPath.push(segmentStart);
// 找到距离途经点最近的道路点
const nearestPoint = this.findNearestPointOnLine(segmentStart, segmentCoords);
segmentPath.push(nearestPoint);
// 添加从最近点到路径的剩余部分
const startIndex = this.findPointIndex(segmentCoords, nearestPoint);
if (startIndex !== -1 && startIndex < segmentCoords.length - 1) {
segmentPath.push(...segmentCoords.slice(startIndex + 1));
} else {
// 如果找不到索引,直接添加整段路径
segmentPath.push(...segmentCoords);
}
}
},
// 处理段结束连接
async handleSegmentEnd(segmentPath, segmentEnd, pathNodes, segmentIndex, totalPoints) {
if (segmentIndex === totalPoints - 2) {
// 最后一段:连接到实际终点
const lastRoadNode = pathNodes[pathNodes.length - 1];
const nearestPoint = this.findBestConnectionPoint(segmentEnd, lastRoadNode);
if (nearestPoint) {
segmentPath.push(nearestPoint);
segmentPath.push(segmentEnd);
}
} else {
// 中间段:途经点处理 - 确保正确连接到途经点
const lastRoadNode = pathNodes[pathNodes.length - 1];
const nearestPoint = this.findBestConnectionPoint(segmentEnd, lastRoadNode);
if (nearestPoint) {
// 先连接到路径上的最近点
segmentPath.push(nearestPoint);
// 然后连接到途经点本身
segmentPath.push(segmentEnd);
}
}
},
// 合并路径段(避免重复点)
mergePathSegments(fullPath, newSegment) {
if (newSegment.length === 0) return;
if (fullPath.length === 0) {
fullPath.push(...newSegment);
return;
}
const lastPoint = fullPath[fullPath.length - 1];
const firstNewPoint = newSegment[0];
// 检查是否需要连接(如果点不重复)
if (lastPoint[0] !== firstNewPoint[0] || lastPoint[1] !== firstNewPoint[1]) {
// 点不重复,直接添加
fullPath.push(...newSegment);
} else {
// 点重复,跳过第一个点
if (newSegment.length > 1) {
fullPath.push(...newSegment.slice(1));
}
}
},
// 在线段上找到最近的点
findNearestPointOnLine(point, lineCoords) {
let nearestPoint = lineCoords[0];
let minDistance = this.calculateDistance(point, nearestPoint);
for (let i = 1; i < lineCoords.length; i++) {
const coord = lineCoords[i];
const distance = this.calculateDistance(point, coord);
if (distance < minDistance) {
minDistance = distance;
nearestPoint = coord;
}
}
return nearestPoint;
},
// 查找点在数组中的索引
findPointIndex(coords, point) {
const tolerance = 0.000001; // 容差,处理浮点数精度问题
for (let i = 0; i < coords.length; i++) {
if (Math.abs(coords[i][0] - point[0]) < tolerance &&
Math.abs(coords[i][1] - point[1]) < tolerance) {
return i;
}
}
return -1;
},
// 找到最佳连接点
findBestConnectionPoint(actualPoint, roadNode) {
const roadNodeCoord = this.roadNetworkGeoJSONBuild.nodeCoords[roadNode];
if (!roadNodeCoord) return null;
// 查找连接到该节点的所有道路段
const connectedRoads = this.roadNetworkGeoJSON.features.filter(road =>
road.properties.FNODE_ === roadNode || road.properties.TNODE_ === roadNode
);
if (connectedRoads.length === 0) return roadNodeCoord;
let bestPoint = roadNodeCoord;
let minDistance = this.calculateDistance(actualPoint, roadNodeCoord);
// 在所有连接的道路段上寻找最佳连接点
connectedRoads.forEach(road => {
const coords = road.geometry.coordinates[0];
// 检查道路段的每个点
coords.forEach(coord => {
const distance = this.calculateDistance(actualPoint, coord);
if (distance < minDistance) {
minDistance = distance;
bestPoint = coord;
}
});
});
return bestPoint;
},
// 段内小段连接 // 段内小段连接
connectSegmentInternally(segmentPath, segmentCoords) { connectSegmentInternally(segmentPath, segmentCoords) {
@ -2165,30 +2133,6 @@ export default {
const point2 = turf.point(coord2); const point2 = turf.point(coord2);
return turf.distance(point1, point2, { units: 'meters' }); return turf.distance(point1, point2, { units: 'meters' });
}, },
// 检查路径可行性
isPathPossible(graph, startNode, endNode) {
// 使用广度优先搜索BFS检查路径可行性
const visited = new Set();
const queue = [startNode];
visited.add(startNode);
while (queue.length > 0) {
const currentNode = queue.shift();
if (currentNode === endNode) {
return true;
}
const neighbors = graph[currentNode];
if (neighbors) {
for (const neighbor in neighbors) {
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push(neighbor);
}
}
}
}
return false;
},
async calculateShortestPath() { async calculateShortestPath() {
if (!this.pointQD || !this.pointZD || !this.roadNetworkGeoJSON) { if (!this.pointQD || !this.pointZD || !this.roadNetworkGeoJSON) {
this.$message.warning('请先加载路网数据并绘制障碍面、起点和终点!') this.$message.warning('请先加载路网数据并绘制障碍面、起点和终点!')
@ -2251,23 +2195,6 @@ export default {
) )
this.drawPath(route) this.drawPath(route)
}, },
// 单独的路径绘制方法
// drawPath(path) {
// const positions = path
// if (positions.fullPath.length == 0) return
// const polyline = new window.mars3d.graphic.PolylinePrimitive({
// positions: positions.fullPath,
// style: {
// clampToGround: true,
// color: '#55ff33',
// width: 8,
// },
// })
// this.shortestPathLayer.addGraphic(polyline)
// this.shortestPathList.push(path.infoList)
// this.infoList = path.infoList.map((item) => item.properties)
// },
// 单独的路径绘制方法 - 添加箭头 // 单独的路径绘制方法 - 添加箭头
drawPath(path) { drawPath(path) {
const positions = path const positions = path

File diff suppressed because it is too large Load Diff