fix bug修复
This commit is contained in:
@ -580,17 +580,42 @@ export default {
|
||||
// 2. 处理 GeoJSON 数据
|
||||
this.roadNetworkGeoJSON = shpBuffer
|
||||
/// 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) => {
|
||||
// ====网路图=====
|
||||
// 获取当前颜色
|
||||
const currentColor = colors[colorIndex % colors.length];
|
||||
|
||||
// 创建路网线图形
|
||||
const graphicLine = new window.mars3d.graphic.PolylineEntity({
|
||||
positions: feature.geometry.coordinates[0],
|
||||
style: {
|
||||
color: '#FF0000',
|
||||
color: "#FF0000", // 使用当前颜色
|
||||
width: 2,
|
||||
outline: false,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
// 将图形添加到图层
|
||||
this.graphicLayer.addGraphic(graphicLine);
|
||||
|
||||
// 更新颜色索引
|
||||
colorIndex++;
|
||||
})
|
||||
this.roadNetworkGeoJSONBuild = this.buildGraph(this.roadNetworkGeoJSON)
|
||||
this.mapLoading = false
|
||||
@ -1604,488 +1629,431 @@ export default {
|
||||
|
||||
const fullPath = []
|
||||
const infoList = []
|
||||
let prevSegmentEndNode = null; // 存储上一个途经点的衔接点(用于下一段复用)
|
||||
|
||||
for (let i = 0; i < points.length - 1; i++) {
|
||||
const currPoint = points[i]; // 当前点(起点/途经点)
|
||||
const nextPoint = points[i + 1]; // 下一个点(途经点/终点)
|
||||
const segmentStart = currPoint.coord;
|
||||
const segmentEnd = nextPoint.coord;
|
||||
|
||||
// 匹配最近节点:关键修改——若当前是“途经点之后的段”,复用前一段的衔接点作为起点
|
||||
let startNode, endNode;
|
||||
if (currPoint.type === 'via' && prevSegmentEndNode ) {
|
||||
// 直接使用上一段的结束节点作为起点(确保路径连续性)
|
||||
startNode = prevSegmentEndNode;
|
||||
} else {
|
||||
// 情况2:起点/第一段,正常匹配最近节点
|
||||
startNode = this.findNearestNode(segmentStart, nodeCoords);
|
||||
}
|
||||
endNode = this.findNearestNodeWithReturn(segmentEnd, nodeCoords, tempGraph, startNode);
|
||||
// 查找最近的边(线段)而不是节点
|
||||
let startEdgeInfo = this.findNearestEdge(segmentStart, tempGraph, nodeCoords);
|
||||
let endEdgeInfo = this.findNearestEdge(segmentEnd, tempGraph, nodeCoords);
|
||||
|
||||
if (!startNode || !endNode) {
|
||||
console.error('无法匹配到路网节点')
|
||||
prevSegmentEndNode = null; // 重置衔接点,避免后续复用错误
|
||||
if (!startEdgeInfo || !endEdgeInfo) {
|
||||
console.error("无法匹配到路网边")
|
||||
continue
|
||||
}
|
||||
// 计算垂直连接点
|
||||
const startConnection = this.calculatePerpendicularConnection(segmentStart, startEdgeInfo, currPoint.type);
|
||||
const endConnection = this.calculatePerpendicularConnection(segmentEnd, endEdgeInfo, nextPoint.type);
|
||||
|
||||
// 检查路径可行性
|
||||
if (!this.isPathPossible(tempGraph, startNode, endNode)) {
|
||||
this.$message.warning(`无法匹配到路网节点`);
|
||||
continue;
|
||||
}
|
||||
// 构建包含垂直连接点的临时图
|
||||
const extendedGraph = this.buildExtendedGraph(tempGraph, nodeCoords, startConnection, endConnection);
|
||||
|
||||
// 检查路径可行性(考虑死胡同原路返回)
|
||||
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) {
|
||||
this.$message.warning(`第${i+1}段路径未找到!`)
|
||||
prevSegmentEndNode = null;
|
||||
continue
|
||||
}
|
||||
// 生成当前段的路径(处理死胡同原路返回)
|
||||
const segmentResult = await this.generatePathWithReturn(
|
||||
const segmentResult = await this.generatePathWithPerpendicularConnections(
|
||||
pathNodes,
|
||||
segmentStart,
|
||||
segmentEnd,
|
||||
i,
|
||||
points.length,
|
||||
currPoint.type,
|
||||
nextPoint.type,
|
||||
isDeadEnd,
|
||||
prevSegmentEndNode
|
||||
startConnection,
|
||||
endConnection,
|
||||
extendedGraph
|
||||
);
|
||||
|
||||
if (segmentResult.path.length > 0) {
|
||||
// 合并路径段
|
||||
this.mergePathSegments(fullPath, segmentResult.path);
|
||||
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}
|
||||
},
|
||||
// 改进:查找最近节点,考虑死胡同情况
|
||||
findNearestNodeWithReturn(coord, nodeCoords, graph, fromNode = null) {
|
||||
const nearestNode = this.findNearestNode(coord, nodeCoords);
|
||||
|
||||
// 如果没有起点节点或不是死胡同,直接返回最近节点
|
||||
if (!fromNode || !this.isDeadEnd(nearestNode, graph)) {
|
||||
return nearestNode;
|
||||
// 合并路径段
|
||||
mergePathSegments(fullPath, newSegment) {
|
||||
if (!newSegment || newSegment.length === 0) return;
|
||||
if (fullPath.length === 0) {
|
||||
fullPath.push(...newSegment);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是死胡同,找到死胡同的入口节点(原路返回点)
|
||||
return this.findDeadEndEntry(nearestNode, graph, fromNode, nodeCoords);
|
||||
const lastPoint = fullPath[fullPath.length - 1];
|
||||
const firstNewPoint = newSegment[0];
|
||||
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;
|
||||
|
||||
// 判断节点是否是死胡同(只有一个连接)
|
||||
isDeadEnd(node, graph) {
|
||||
if (!graph[node]) return true;
|
||||
const connections = Object.keys(graph[node]);
|
||||
return connections.length === 1;
|
||||
},
|
||||
|
||||
// 找到死胡同的入口节点(原路返回的衔接点)
|
||||
findDeadEndEntry(deadEndNode, graph, fromNode, nodeCoords) {
|
||||
// 从死胡同节点开始,沿着唯一路径往回找,直到找到分支点或起点
|
||||
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 feature of features) {
|
||||
// 支持 LineString 或 MultiLineString(取每条线段)
|
||||
const geom = feature.geometry;
|
||||
let lines = [];
|
||||
if (geom.type === 'LineString') {
|
||||
lines = [geom.coordinates];
|
||||
} else if (geom.type === 'MultiLineString') {
|
||||
lines = geom.coordinates;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 继续往回找(排除已访问的节点)
|
||||
const nextNode = connections.find(node => !visited.has(node));
|
||||
if (!nextNode) break;
|
||||
for (const lineCoords of lines) {
|
||||
// turf.nearestPointOnLine 需要 lineString(数组经纬),返回距离(单位:度->通过 properties.dist)
|
||||
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);
|
||||
currentNode = nextNode;
|
||||
}
|
||||
|
||||
// 如果找不到合适的入口,返回原始最近节点
|
||||
return deadEndNode;
|
||||
},
|
||||
|
||||
// 改进的路径查找,支持原路返回
|
||||
async findPathWithReturn(graph, startNode, endNode, nodeCoords) {
|
||||
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
|
||||
};
|
||||
}
|
||||
if (distMeters < minDistance) {
|
||||
minDistance = distMeters;
|
||||
perpendicularPoint = proj;
|
||||
// 保存 edge 信息:使用 feature 的 FNODE_/TNODE_ 作为端点(同你 graph 的节点)
|
||||
nearestEdge = {
|
||||
feature,
|
||||
lineCoords,
|
||||
startNode: feature.properties.FNODE_,
|
||||
endNode: feature.properties.TNODE_,
|
||||
distance: distMeters
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
console.error('路径查找错误:', error);
|
||||
return {pathNodes: null, isDeadEnd: false};
|
||||
return null;
|
||||
}
|
||||
},
|
||||
// 通过坐标反向查找对应的路网节点(处理浮点数精度)
|
||||
findNodeByCoord(coord, nodeCoords) {
|
||||
const tolerance = 0.000001; // 与findPointIndex保持一致的容差
|
||||
for (const [nodeId, nodeCoord] of Object.entries(nodeCoords)) {
|
||||
if (
|
||||
Math.abs(nodeCoord[0] - coord[0]) < tolerance &&
|
||||
Math.abs(nodeCoord[1] - coord[1]) < tolerance
|
||||
) {
|
||||
return nodeId;
|
||||
|
||||
// 辅助:在一条线(lineCoords)上找到垂足点最近的索引位置(返回最近点索引)
|
||||
// 如果垂足不在顶点上,会返回两个点之间最近的后续起点索引(便于切片)
|
||||
findNearestIndexOnLine(lineCoords, perpPoint) {
|
||||
let minDist = Infinity;
|
||||
let nearestIndex = 0;
|
||||
for (let i = 0; i < lineCoords.length; i++) {
|
||||
const d = turf.distance(turf.point(lineCoords[i]), turf.point(perpPoint), {units: 'meters'});
|
||||
if (d < minDist) {
|
||||
minDist = d;
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
// 若未找到完全匹配的节点,返回最近的节点(降级处理)
|
||||
return this.findNearestNode(coord, nodeCoords);
|
||||
return { index: nearestIndex, distance: minDist };
|
||||
},
|
||||
// 改进的路径生成,支持原路返回
|
||||
async generatePathWithReturn(
|
||||
|
||||
// 根据路径节点(node id 列表)生成最终的经纬路径(含垂足点)
|
||||
// 说明:pathNodes 包含临时节点 temp_xxx,startConnection/endConnection 帮助在首尾插入垂足
|
||||
async generatePathWithPerpendicularConnections(
|
||||
pathNodes,
|
||||
segmentStart,
|
||||
segmentEnd,
|
||||
segmentIndex,
|
||||
totalPoints,
|
||||
currPointType,
|
||||
nextPointType,
|
||||
isDeadEnd = false,
|
||||
prevSegmentEndNode = null
|
||||
startConnection,
|
||||
endConnection,
|
||||
graph
|
||||
) {
|
||||
const segmentPath = [];
|
||||
const segments = [];
|
||||
let hasValidSegment = false;
|
||||
// 新增:存储途经点垂直点之后的路网后半截(供后续路径延续)
|
||||
let viaPerpRemainingCoords = [];
|
||||
|
||||
// 生成主要路径段
|
||||
for (let j = 0; j < pathNodes.length - 1; j++) {
|
||||
const currentNode = pathNodes[j];
|
||||
const nextNode = pathNodes[j + 1];
|
||||
// 1. 起点 -> 垂足(直线)
|
||||
if (startConnection.type !== "via") {
|
||||
// 起点
|
||||
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;
|
||||
if (!this.join) {
|
||||
segment = this.roadNetworkGeoJSON.features.find(
|
||||
(f) =>
|
||||
(f.properties.FNODE_ == currentNode && f.properties.TNODE_ == nextNode) ||
|
||||
(f.properties.FNODE_ == nextNode && f.properties.TNODE_ == currentNode)
|
||||
segment = this.roadNetworkGeoJSON.features.find(f =>
|
||||
(String(f.properties.FNODE_) === String(curNode) && String(f.properties.TNODE_) === String(nextNode)) ||
|
||||
(String(f.properties.FNODE_) === String(nextNode) && String(f.properties.TNODE_) === String(curNode))
|
||||
);
|
||||
} else {
|
||||
} else{
|
||||
segment = this.roadNetworkGeoJSON.features.find(
|
||||
(f) =>
|
||||
((f.properties.FNODE_ == currentNode && f.properties.TNODE_ == nextNode) ||
|
||||
(f.properties.FNODE_ == nextNode && f.properties.TNODE_ == currentNode)) &&
|
||||
((f.properties.FNODE_ == curNode && f.properties.TNODE_ == nextNode) ||
|
||||
(f.properties.FNODE_ == nextNode && f.properties.TNODE_ == curNode)) &&
|
||||
f.properties.载重吨 >= this.inputform.load &&
|
||||
f.properties.宽度 >= this.inputform.width &&
|
||||
f.properties.曲率半 <= this.inputform.minTurnRadius
|
||||
);
|
||||
}
|
||||
|
||||
if (!segment) continue;
|
||||
if (!segment) {
|
||||
console.warn(`找不到符合条件的路段: ${curNode} -> ${nextNode}`);
|
||||
// 若找不到对应路段(可能被避让删掉或数据不一致),则跳过
|
||||
continue;
|
||||
}
|
||||
|
||||
segments.push(segment);
|
||||
const segmentCoords = segment.geometry.coordinates[0];
|
||||
if (segment) {
|
||||
hasValidSegment = true;
|
||||
segments.push(segment);
|
||||
// 取线数组(LineString 或 MultiLine 的第一条)
|
||||
let segCoords = [];
|
||||
if (segment.geometry.type === 'LineString') {
|
||||
segCoords = segment.geometry.coordinates;
|
||||
} else if (segment.geometry.type === 'MultiLineString') {
|
||||
segCoords = segment.geometry.coordinates[0];
|
||||
}
|
||||
|
||||
// 处理段内连接
|
||||
if (j === 0) {
|
||||
await this.handleSegmentStartWithReturn(
|
||||
segmentPath, segmentStart, segmentCoords, segmentIndex, currPointType, prevSegmentEndNode
|
||||
);
|
||||
// 确定方向:如果 FNODE_ === curNode,则线方向为 segCoords 正序,否则需要反转
|
||||
const isForward = String(segment.properties.FNODE_) === String(curNode);
|
||||
const coordsToUse = isForward ? segCoords : [...segCoords].reverse();
|
||||
|
||||
// === 使用第一个版本的精细连接逻辑,但加入第二个版本的途经点切片 ===
|
||||
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 {
|
||||
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(
|
||||
segmentPath, segmentEnd, pathNodes, segmentIndex, totalPoints, nextPointType, isDeadEnd
|
||||
);
|
||||
// 找到路径末尾点在线段上最近的点索引
|
||||
const nearestToLast = this.findNearestPointWithIndex(endLineCoords, lastPt);
|
||||
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 };
|
||||
},
|
||||
// 改进的段起始处理
|
||||
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) {
|
||||
@ -2165,30 +2133,6 @@ export default {
|
||||
const point2 = turf.point(coord2);
|
||||
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() {
|
||||
if (!this.pointQD || !this.pointZD || !this.roadNetworkGeoJSON) {
|
||||
this.$message.warning('请先加载路网数据并绘制障碍面、起点和终点!')
|
||||
@ -2251,23 +2195,6 @@ export default {
|
||||
)
|
||||
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) {
|
||||
const positions = path
|
||||
|
||||
2616
src/views/home/home202510115.vue
Normal file
2616
src/views/home/home202510115.vue
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user