Merge branch 'pathPlan' of https://git.rangutech.com/yiqiuyang/kxfx into yqy

This commit is contained in:
yiqiuyang
2025-10-27 09:50:49 +08:00
3 changed files with 163 additions and 28 deletions

View File

@ -1,16 +1,16 @@
{ {
"startPoint": "114.26344,27.800982", "startPoint": "114.312888,27.796612",
"endPoint": "114.284668,27.794961", "endPoint": "114.336525,27.767989",
"viaPoints": [ "viaPoints": [
{ {
"time": "1694352000000", "time": "1694352000000",
"points": "114.272329,27.797299" "points": "114.334239,27.779261"
} }
], ],
"avoidPoints": [ "avoidPoints": [
{ {
"time": "1694352003000", "time": "1694352003000",
"points": "114.27882,27.792857" "points": ""
} }
], ],
"avoidAreas": [ "avoidAreas": [

View File

@ -80,3 +80,20 @@ html {
.flex-warp { .flex-warp {
flex-wrap: wrap; flex-wrap: wrap;
} }
.el-form-item {
display: flex!important;
justify-content: space-between;
margin-bottom: 12px!important;
}
.el-form-item__label {
letter-spacing: -1px;
min-width: 130px!important;
padding: 0!important;
white-space: nowrap; /* 强制不换行 */
flex-shrink: 0;
font-size: 16px!important;
}
.el-input__inner {
height: 24px;
line-height: 24px;
}

View File

@ -2,11 +2,21 @@
<div> <div>
<div class="home-header"> <div class="home-header">
<div class="home-header-left"> <div class="home-header-left">
<img @click="drawStartPoint" src="@/assets/image/start.png" /> <el-tooltip content="起点绘制">
<img @click="drawEndPoint" src="@/assets/image/end.png" /> <img @click="drawStartPoint" src="@/assets/image/start.png" />
<img @click="drawViaPoint" src="@/assets/image/add.png" /> </el-tooltip>
<img @click="drawAvoidPoint" src="@/assets/image/avoidP.png" /> <el-tooltip content="终点绘制">
<img @click="drawAvoidArea" src="@/assets/image/updown.png" /> <img @click="drawEndPoint" src="@/assets/image/end.png" />
</el-tooltip>
<el-tooltip content="途径点绘制">
<img @click="drawViaPoint" src="@/assets/image/add.png" />
</el-tooltip>
<el-tooltip content="避让点绘制">
<img @click="drawAvoidPoint" src="@/assets/image/avoidP.png" />
</el-tooltip>
<el-tooltip content="避让区域绘制">
<img @click="drawAvoidArea" src="@/assets/image/updown.png" />
</el-tooltip>
<div @click="clear" class="sure">清除</div> <div @click="clear" class="sure">清除</div>
<div @click="calculateShortestPath" class="sure">确定</div> <div @click="calculateShortestPath" class="sure">确定</div>
<div @click="hadBuffer" class="sure">路线隐蔽规划</div> <div @click="hadBuffer" class="sure">路线隐蔽规划</div>
@ -38,12 +48,12 @@
<div class="title">参数</div> <div class="title">参数</div>
<el-form <el-form
@submit.native.prevent="calculateShortestPath" @submit.native.prevent="calculateShortestPath"
label-width="120px" label-width="140px"
label-position="left" label-position="left"
size="mini" size="mini"
:model="form" :model="form"
> >
<el-form-item label="起点"> <el-form-item label="起点">
<el-input <el-input
v-model="form.startPoint" v-model="form.startPoint"
@blur="pointsChange('startPoint')" @blur="pointsChange('startPoint')"
@ -51,7 +61,7 @@
clearable clearable
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="终点"> <el-form-item label="终点">
<el-input <el-input
v-model="form.endPoint" v-model="form.endPoint"
@blur="pointsChange('endPoint')" @blur="pointsChange('endPoint')"
@ -59,7 +69,7 @@
clearable clearable
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="途经点"> <el-form-item label="途经点">
<div v-for="(item, index) in form.viaPoints" :key="index"> <div v-for="(item, index) in form.viaPoints" :key="index">
<el-input <el-input
v-model="item.points" v-model="item.points"
@ -70,7 +80,7 @@
></el-input> ></el-input>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="避让点"> <el-form-item label="避让点">
<div v-for="(item, index) in form.avoidPoints" :key="index"> <div v-for="(item, index) in form.avoidPoints" :key="index">
<el-input <el-input
v-model="item.points" v-model="item.points"
@ -81,7 +91,7 @@
></el-input> ></el-input>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="避让区域"> <el-form-item label="避让区域">
<div v-for="(item, index) in form.avoidAreas" :key="index"> <div v-for="(item, index) in form.avoidAreas" :key="index">
<el-input <el-input
v-model="item.points" v-model="item.points"
@ -98,11 +108,11 @@
</div> </div>
<div class="control-panel"> <div class="control-panel">
<div class="title">隐蔽添加</div> <div class="title">隐蔽添加</div>
<el-form label-width="120px" label-position="left" size="mini"> <el-form label-width="140px" label-position="left" size="mini">
<el-form-item label="缓冲半径m"> <el-form-item label="缓冲半径(m)">
<el-input v-model="hideform.radius"></el-input> <el-input v-model="hideform.radius"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="面积冗余%"> <el-form-item label="面积冗余(%)">
<el-input v-model="hideform.redundancy" placeholder=""></el-input> <el-input v-model="hideform.redundancy" placeholder=""></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -115,14 +125,14 @@
<span>参与路线规划</span> <span>参与路线规划</span>
</div> </div>
</div> </div>
<el-form @submit.native.prevent="calculateShortestPath" label-width="120px" label-position="left" size="mini"> <el-form @submit.native.prevent="calculateShortestPath" label-width="140px" label-position="left" size="mini">
<el-form-item label="宽度"> <el-form-item label="宽度(m)">
<el-input v-model="inputform.width"></el-input> <el-input v-model="inputform.width"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="载重(吨)"> <el-form-item label="载重(吨)">
<el-input v-model="inputform.load" placeholder=""></el-input> <el-input v-model="inputform.load" placeholder=""></el-input>
</el-form-item> </el-form-item>
<el-form-item label="最小转弯半径"> <el-form-item label="最小转弯半径(m)">
<el-input v-model="inputform.minTurnRadius" placeholder=""></el-input> <el-input v-model="inputform.minTurnRadius" placeholder=""></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -426,6 +436,9 @@ export default {
} }
}, },
async mounted() { async mounted() {
[...document.querySelectorAll('*')].forEach(n =>
console.log(window.getComputedStyle(n).fontFamily)
)
this.viewer = null this.viewer = null
await this.getMapOption() await this.getMapOption()
this.$nextTick(async () => { this.$nextTick(async () => {
@ -466,6 +479,9 @@ export default {
pitch: -35, pitch: -35,
}, },
}, },
defaultLabelStyle: {
font: '16px "DengXian",sans-serif',
}
// basemaps: [ // basemaps: [
// { // {
// id: "image-tdss", // id: "image-tdss",
@ -1264,8 +1280,16 @@ export default {
graphic?.remove() graphic?.remove()
this.viaPoints = this.viaPoints.filter((viaPoint) => viaPoint.style.time !== row.time) this.viaPoints = this.viaPoints.filter((viaPoint) => viaPoint.style.time !== row.time)
} else { } else {
const graphic = this.viaPoints.find((viaPoint) => viaPoint.style.time === row.time) if (this.viaPoints.length > 0) {
this.updatePointPosition(graphic, row.points) const graphic = this.viaPoints.find(
(viaPoint) => viaPoint.style.time === row.time
)
this.updatePointPosition(graphic, row.points)
} else {
const time = new Date().getTime()
row.time = time
this.addPointToMap('viaPoints', row, time)
}
} }
} else if (type === 'avoidPoints') { } else if (type === 'avoidPoints') {
if (!row.points) { if (!row.points) {
@ -1279,8 +1303,16 @@ export default {
graphic?.remove() graphic?.remove()
this.avoidPoints = this.avoidPoints.filter((avoidPoint) => avoidPoint.style.time !== row.time) this.avoidPoints = this.avoidPoints.filter((avoidPoint) => avoidPoint.style.time !== row.time)
} else { } else {
const graphic = this.avoidPoints.find((avoidPoint) => avoidPoint.style.time === row.time) if (this.avoidPoints.length > 0) {
this.updatePointPosition(graphic, row.points) const graphic = this.avoidPoints.find(
(avoidPoint) => avoidPoint.style.time === row.time
)
this.updatePointPosition(graphic, row.points)
} else {
const time = new Date().getTime()
row.time = time
this.addPointToMap('avoidPoints', row, time)
}
} }
} else if (type === 'avoidAreas') { } else if (type === 'avoidAreas') {
if (!row.points) { if (!row.points) {
@ -1294,8 +1326,16 @@ export default {
graphic?.remove() graphic?.remove()
this.avoidAreas = this.avoidAreas.filter((avoidArea) => avoidArea.style.time !== row.time) this.avoidAreas = this.avoidAreas.filter((avoidArea) => avoidArea.style.time !== row.time)
} else { } else {
const graphic = this.avoidAreas.find((avoidArea) => avoidArea.style.time === row.time) if (this.avoidAreas.length > 0) {
this.updatePolygonPosition(graphic, row.points) const graphic = this.avoidAreas.find(
(avoidArea) => avoidArea.style.time === row.time
)
this.updatePolygonPosition(graphic, row.points)
} else {
const time = new Date().getTime()
row.time = time
this.addPolygonToMap('avoidAreas', row, time)
}
} }
} }
}, },
@ -1870,15 +1910,93 @@ export default {
const coordsToUse = isForward ? segCoords : [...segCoords].reverse() const coordsToUse = isForward ? segCoords : [...segCoords].reverse()
// === 使用第一个版本的精细连接逻辑,但加入第二个版本的途经点切片 === // === 使用第一个版本的精细连接逻辑,但加入第二个版本的途经点切片 ===
const lastPt = segmentPath[segmentPath.length - 1];
const firstOfSeg = coordsToUse[0];
const lastPt = segmentPath[segmentPath.length - 1] const lastPt = segmentPath[segmentPath.length - 1]
const firstOfSeg = coordsToUse[0] const firstOfSeg = coordsToUse[0]
const distLastToFirst = this.calculateDistance(lastPt, firstOfSeg);
// 如果是途经点,需要找到终点垂足在当前位置
const endPerpNearest = this.findNearestPointWithIndex(coordsToUse, endConnection.perpendicularPoint);
const endNi = endPerpNearest.index;
const distLastToFirst = this.calculateDistance(lastPt, firstOfSeg) const distLastToFirst = this.calculateDistance(lastPt, firstOfSeg)
// 如果是途经点,需要找到终点垂足在当前位置 // 如果是途经点,需要找到终点垂足在当前位置
const endPerpNearest = this.findNearestPointWithIndex(coordsToUse, endConnection.perpendicularPoint) const endPerpNearest = this.findNearestPointWithIndex(coordsToUse, endConnection.perpendicularPoint)
const endNi = endPerpNearest.index const endNi = endPerpNearest.index
if (distLastToFirst < 1e-6) {
// 精度上相同,直接接上(跳过第一个)
// 如果是途经点,需要切片到垂足
if (endConnection.type === 'via') {
segmentPath.push(...coordsToUse.slice(1, endNi + 1));
// 保存垂直点之后的路网后半截
viaPerpRemainingCoords = coordsToUse.slice(endNi);
} else {
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 (distLastToFirst < 1e-6) { if (distLastToFirst < 1e-6) {
// 精度上相同,直接接上(跳过第一个) // 精度上相同,直接接上(跳过第一个)
// 如果是途经点,需要切片到垂足 // 如果是途经点,需要切片到垂足