Compare commits
7 Commits
pathPlan
...
278176a0fb
| Author | SHA1 | Date | |
|---|---|---|---|
| 278176a0fb | |||
| 9444e6db5d | |||
| 2b639ea0f3 | |||
| 0aecd99d65 | |||
| 04f23de755 | |||
| 7dca973b3b | |||
| 31f8c849b8 |
@ -4,5 +4,6 @@
|
|||||||
"semi": false,
|
"semi": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"bracketSpacing": false
|
"bracketSpacing": false,
|
||||||
|
"printWidth": 120
|
||||||
}
|
}
|
||||||
|
|||||||
421
package-lock.json
generated
421
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/preset-env": "^7.28.3",
|
"@babel/preset-env": "^7.28.3",
|
||||||
"axios": "^1.13.2",
|
"axios": "0.21.0",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"element-ui": "2.9.2",
|
"element-ui": "2.9.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[http]
|
[http]
|
||||||
port=8381
|
port=8083
|
||||||
address=192.168.3.35
|
address=127.0.0.1
|
||||||
|
|
||||||
[title]
|
[title]
|
||||||
msg=道路堪选分析
|
msg=道路堪选分析
|
||||||
@ -1,4 +0,0 @@
|
|||||||
window.config = {
|
|
||||||
// baseUrl: 'http://192.168.3.35:8381',
|
|
||||||
baseUrl: '/api'
|
|
||||||
}
|
|
||||||
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"startPoint": "114.312888,27.796612",
|
"startPoint": "114.26344,27.800982",
|
||||||
"endPoint": "114.336525,27.767989",
|
"endPoint": "114.284668,27.794961",
|
||||||
"viaPoints": [
|
"viaPoints": [
|
||||||
{
|
{
|
||||||
"time": "1694352000000",
|
"time": "1694352000000",
|
||||||
"points": "114.334239,27.779261"
|
"points": "114.272329,27.797299"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"avoidPoints": [
|
"avoidPoints": [
|
||||||
{
|
{
|
||||||
"time": "1694352003000",
|
"time": "1694352003000",
|
||||||
"points": ""
|
"points": "114.27882,27.792857"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"avoidAreas": [
|
"avoidAreas": [
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
/>
|
/>
|
||||||
<script src="./config/config.js" type="text/javascript"></script>
|
|
||||||
<script src="./map/Cesium/Cesium.js" type="text/javascript"></script>
|
<script src="./map/Cesium/Cesium.js" type="text/javascript"></script>
|
||||||
<link href="./map/mars3d/mars3d.css" rel="stylesheet" type="text/css" />
|
<link href="./map/mars3d/mars3d.css" rel="stylesheet" type="text/css" />
|
||||||
<script src="./map/mars3d/mars3d.js" type="text/javascript"></script>
|
<script src="./map/mars3d/mars3d.js" type="text/javascript"></script>
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
routerMap: {
|
routerMap: {
|
||||||
1: '/',
|
1: '/',
|
||||||
2: '/residentAnalysis',
|
// 2: '/residentAnalysis ',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -22,7 +22,8 @@ html, body, #app, .cesium-widget, .mars3d-container,
|
|||||||
.el-table__footer-wrapper span,
|
.el-table__footer-wrapper span,
|
||||||
.el-table__header-wrapper span,
|
.el-table__header-wrapper span,
|
||||||
.el-table__fixed span,
|
.el-table__fixed span,
|
||||||
.el-table__fixed-right span,
|
.el-table__fixed-right span,.vxe-table *,
|
||||||
|
.vxe-table--render-default .vxe-cell span,
|
||||||
.vxe-table--render-default .vxe-cell--title,
|
.vxe-table--render-default .vxe-cell--title,
|
||||||
.vxe-table--render-default .vxe-cell--label,
|
.vxe-table--render-default .vxe-cell--label,
|
||||||
.vxe-toolbar *,
|
.vxe-toolbar *,
|
||||||
@ -79,20 +80,3 @@ 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;
|
|
||||||
}
|
|
||||||
@ -10,9 +10,6 @@ import 'vxe-pc-ui/es/style.css'
|
|||||||
|
|
||||||
import VxeUITable from 'vxe-table'
|
import VxeUITable from 'vxe-table'
|
||||||
import 'vxe-table/es/style.css'
|
import 'vxe-table/es/style.css'
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
Vue.prototype.$http = axios
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
|||||||
@ -6,13 +6,13 @@ import residentAnalysis from '@/views/residentAnalysis/index.vue'
|
|||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
|
// {
|
||||||
|
// path: '/',
|
||||||
|
// name: 'home',
|
||||||
|
// component: HomeView,
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
|
||||||
component: HomeView,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/residentAnalysis',
|
|
||||||
name: 'residentAnalysis',
|
name: 'residentAnalysis',
|
||||||
component: residentAnalysis,
|
component: residentAnalysis,
|
||||||
},
|
},
|
||||||
|
|||||||
162
src/utils/map.js
162
src/utils/map.js
@ -1,162 +0,0 @@
|
|||||||
/**
|
|
||||||
* 极简 WKT -> GeoJSON
|
|
||||||
* 支持 POINT / LINESTRING / POLYGON
|
|
||||||
* @param {String} wkt
|
|
||||||
* @return {Object} GeoJSON
|
|
||||||
*/
|
|
||||||
function wktToGeoJSON (wkt) {
|
|
||||||
const trim = str => str.trim()
|
|
||||||
const split = (str, d) => str.split(d).map(trim)
|
|
||||||
|
|
||||||
// 1. 提取类型 + 坐标字符串
|
|
||||||
const [head, coords] = split(wkt, '(')
|
|
||||||
const type = trim(head).toUpperCase()
|
|
||||||
const coordStr = coords.replace(/\)$/, '')
|
|
||||||
|
|
||||||
// 2. 通用“数字对”解析
|
|
||||||
const parseRing = ring =>
|
|
||||||
split(ring, ',').map(p => {
|
|
||||||
const [x, y] = split(p, ' ').map(Number)
|
|
||||||
return [x, y] // GeoJSON 里经度在前
|
|
||||||
})
|
|
||||||
|
|
||||||
// 3. 按类型组装
|
|
||||||
switch (type) {
|
|
||||||
case 'POINT': {
|
|
||||||
const [x, y] = split(coordStr, ' ').map(Number)
|
|
||||||
return { type: 'Point', coordinates: [x, y] }
|
|
||||||
}
|
|
||||||
case 'LINESTRING':
|
|
||||||
return { type: 'LineString', coordinates: parseRing(coordStr) }
|
|
||||||
case 'POLYGON': {
|
|
||||||
// 可能带孔,先按“), (”切
|
|
||||||
const rings = coordStr.split(/\s*\),\s*\(/).map(parseRing)
|
|
||||||
return { type: 'Polygon', coordinates: rings }
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new Error('暂不支持该 WKT 类型:' + type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 拿当前视野四至(容错版) */
|
|
||||||
function getBounds(map) {
|
|
||||||
let rect = map.camera.computeViewRectangle()
|
|
||||||
|
|
||||||
if (!rect) {
|
|
||||||
// 高空/2D 模式下 computeViewRectangle 会失效,用四个角点兜底
|
|
||||||
const canvas = map.scene.canvas
|
|
||||||
const ellipsoid = Cesium.Ellipsoid.WGS84
|
|
||||||
const cv = (x, y) => map.camera.pickEllipsoid(
|
|
||||||
new Cesium.Cartesian2(x, y), ellipsoid
|
|
||||||
)
|
|
||||||
|
|
||||||
const ptNW = cv(0, 0)
|
|
||||||
const ptSE = cv(canvas.clientWidth, canvas.clientHeight)
|
|
||||||
|
|
||||||
// 如果仍 pick 不到,就缩小范围再试(再不行就返回一个默认矩形)
|
|
||||||
if (!ptNW || !ptSE) {
|
|
||||||
rect = map.camera.computeViewRectangle(
|
|
||||||
ellipsoid,
|
|
||||||
Cesium.Math.toRadians(0.1) // 0.1° 容差
|
|
||||||
)
|
|
||||||
if (!rect) {
|
|
||||||
// 终极保底:以相机位置为中心 ±0.1°
|
|
||||||
const c = map.camera.positionCartographic
|
|
||||||
const d = 0.1
|
|
||||||
return {
|
|
||||||
west : Cesium.Math.toDegrees(c.longitude) - d,
|
|
||||||
east : Cesium.Math.toDegrees(c.longitude) + d,
|
|
||||||
south: Cesium.Math.toDegrees(c.latitude) - d,
|
|
||||||
north: Cesium.Math.toDegrees(c.latitude) + d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// pick 成功
|
|
||||||
const nw = Cesium.Cartographic.fromCartesian(ptNW)
|
|
||||||
const se = Cesium.Cartographic.fromCartesian(ptSE)
|
|
||||||
return {
|
|
||||||
west : Cesium.Math.toDegrees(nw.longitude),
|
|
||||||
north: Cesium.Math.toDegrees(nw.latitude),
|
|
||||||
east : Cesium.Math.toDegrees(se.longitude),
|
|
||||||
south: Cesium.Math.toDegrees(se.latitude)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正常能算到
|
|
||||||
return {
|
|
||||||
west : Cesium.Math.toDegrees(rect.west),
|
|
||||||
south: Cesium.Math.toDegrees(rect.south),
|
|
||||||
east : Cesium.Math.toDegrees(rect.east),
|
|
||||||
north: Cesium.Math.toDegrees(rect.north)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* 计算当前地图层级(zoom) */
|
|
||||||
function getZoom(map) {
|
|
||||||
const height = map.camera.positionCartographic.height
|
|
||||||
// 经验公式:Cesium 高度 -> WebMercator zoom
|
|
||||||
return Math.floor(29.5 - Math.log(height) / Math.LN2)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 画布像素边界 */
|
|
||||||
function getViewportPxBounds(map) {
|
|
||||||
const cvs = map.scene.canvas
|
|
||||||
return { left: 0, top: 0, right: cvs.clientWidth, bottom: cvs.clientHeight }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 像素 -> 经纬度四至 */
|
|
||||||
function pxToLatLngBounds(px, map) {
|
|
||||||
const pick = (x, y) => {
|
|
||||||
const cart = map.camera.pickEllipsoid(
|
|
||||||
new Cesium.Cartesian2(x, y),
|
|
||||||
Cesium.Ellipsoid.WGS84
|
|
||||||
)
|
|
||||||
if (!cart) return null
|
|
||||||
const c = Cesium.Cartographic.fromCartesian(cart)
|
|
||||||
return { lng: Cesium.Math.toDegrees(c.longitude), lat: Cesium.Math.toDegrees(c.latitude) }
|
|
||||||
}
|
|
||||||
const nw = pick(px.left, px.top)
|
|
||||||
const se = pick(px.right, px.bottom)
|
|
||||||
// 兜底:高空 pick 不到就用相机矩形
|
|
||||||
if (!nw || !se) {
|
|
||||||
const rect = map.camera.computeViewRectangle() || {}
|
|
||||||
return {
|
|
||||||
west : Cesium.Math.toDegrees(rect.west || 0),
|
|
||||||
south: Cesium.Math.toDegrees(rect.south || 0),
|
|
||||||
east : Cesium.Math.toDegrees(rect.east || 0),
|
|
||||||
north: Cesium.Math.toDegrees(rect.north || 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { west: nw.lng, north: nw.lat, east: se.lng, south: se.lat }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var GRID_SIZE_DEG = 0.05 // 0.05° ≈ 5 km 一格
|
|
||||||
var CACHE_MIN = 30 * 60 * 1000 // 30 min
|
|
||||||
/* 经纬度 → 格子 key */
|
|
||||||
function lonLatToGridKey (lon, lat) {
|
|
||||||
const x = Math.floor(lon / GRID_SIZE_DEG)
|
|
||||||
const y = Math.floor(lat / GRID_SIZE_DEG)
|
|
||||||
return `${x}_${y}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 取格子中心点(方便防抖) */
|
|
||||||
function gridKeyToCenter (key) {
|
|
||||||
const [x, y] = key.split('_').map(Number)
|
|
||||||
return {
|
|
||||||
lon: (x + 0.5) * GRID_SIZE_DEG,
|
|
||||||
lat: (y + 0.5) * GRID_SIZE_DEG
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
wktToGeoJSON,
|
|
||||||
getBounds,
|
|
||||||
getZoom,
|
|
||||||
getViewportPxBounds,
|
|
||||||
pxToLatLngBounds,
|
|
||||||
lonLatToGridKey,
|
|
||||||
gridKeyToCenter,
|
|
||||||
}
|
|
||||||
@ -16,22 +16,18 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {setStorage, getStorage} from '@/utils/localStorage.js'
|
import {setStorage, getStorage} from '@/utils/localStorage.js'
|
||||||
import iniParser from 'ini-parser'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: '',
|
name: '',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tabList: [
|
tabList: [
|
||||||
{id: 1, label: '道路通过性分析'},
|
// {id: 1, label: '机动路线规划'},
|
||||||
// {id: 2, label: '临时部署驻地分析'},
|
{id: 1, label: '临时部署驻地分析'},
|
||||||
],
|
],
|
||||||
activeIndex: 1,
|
activeIndex: 1,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.getTitle()
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.activeIndex = getStorage('activeIndex') || 1
|
this.activeIndex = getStorage('activeIndex') || 1
|
||||||
},
|
},
|
||||||
@ -40,17 +36,6 @@ export default {
|
|||||||
setStorage('activeIndex', 1)
|
setStorage('activeIndex', 1)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getTitle() {
|
|
||||||
fetch('./config.ini')
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(text => {
|
|
||||||
const parsedData = iniParser.parse(text);
|
|
||||||
this.tabList[0].label = parsedData.title.msg || '道路通过性分析'
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.tabList[0].label = '道路通过性分析'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setActiveIndex(id) {
|
setActiveIndex(id) {
|
||||||
if (this.activeIndex !== id) {
|
if (this.activeIndex !== id) {
|
||||||
this.activeIndex = id
|
this.activeIndex = id
|
||||||
|
|||||||
@ -81,21 +81,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<input
|
<input type="file" ref="fileInput" @change="handleFileUpload" style="display: none" />
|
||||||
type="file"
|
|
||||||
ref="fileInput"
|
|
||||||
@change="handleFileUpload"
|
|
||||||
style="display: none"
|
|
||||||
/>
|
|
||||||
<div class="importJson" @click="triggerFileUpload">导入json文件</div>
|
<div class="importJson" @click="triggerFileUpload">导入json文件</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-panel">
|
<div class="control-panel">
|
||||||
<div class="title">隐蔽添加</div>
|
<div class="title">隐蔽添加</div>
|
||||||
<el-form
|
<el-form label-width="120px" label-position="left" size="mini">
|
||||||
label-width="120px"
|
|
||||||
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>
|
||||||
@ -112,12 +103,7 @@
|
|||||||
<span>参与路线规划</span>
|
<span>参与路线规划</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-form
|
<el-form @submit.native.prevent="calculateShortestPath" label-width="120px" label-position="left" size="mini">
|
||||||
@submit.native.prevent="calculateShortestPath"
|
|
||||||
label-width="120px"
|
|
||||||
label-position="left"
|
|
||||||
size="mini"
|
|
||||||
>
|
|
||||||
<el-form-item label="宽度">
|
<el-form-item label="宽度">
|
||||||
<el-input v-model="inputform.width"></el-input>
|
<el-input v-model="inputform.width"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -125,10 +111,7 @@
|
|||||||
<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="最小转弯半径">
|
||||||
<el-input
|
<el-input v-model="inputform.minTurnRadius" placeholder=""></el-input>
|
||||||
v-model="inputform.minTurnRadius"
|
|
||||||
placeholder=""
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div class="importJson" @click="openDialog">数据选择</div>
|
<div class="importJson" @click="openDialog">数据选择</div>
|
||||||
@ -156,7 +139,7 @@
|
|||||||
inputform.minTurnRadius || 0
|
inputform.minTurnRadius || 0
|
||||||
}},最大车辆载重{{ inputform.load || 0 }}吨
|
}},最大车辆载重{{ inputform.load || 0 }}吨
|
||||||
</div>
|
</div>
|
||||||
<vxe-table ref="xTable" :data="infoList" style="max-height: 50vh;overflow: hidden;overflow-y: auto;">
|
<vxe-table ref="xTable" :data="infoList" style="max-height: 50vh; overflow: hidden; overflow-y: auto">
|
||||||
<vxe-column field="编码" title="编码" width="65px"></vxe-column>
|
<vxe-column field="编码" title="编码" width="65px"></vxe-column>
|
||||||
<vxe-column field="名称" title="名称" width="80px"></vxe-column>
|
<vxe-column field="名称" title="名称" width="80px"></vxe-column>
|
||||||
<vxe-column field="宽度" title="路宽"></vxe-column>
|
<vxe-column field="宽度" title="路宽"></vxe-column>
|
||||||
@ -165,7 +148,7 @@
|
|||||||
<vxe-column field="水深" title="水深"></vxe-column>
|
<vxe-column field="水深" title="水深"></vxe-column>
|
||||||
<vxe-column field="净空高" title="净空高" width="65px"></vxe-column>
|
<vxe-column field="净空高" title="净空高" width="65px"></vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
<vxe-table :data="factoriesWithVehicles" style="margin-top: 10px;">
|
<vxe-table :data="factoriesWithVehicles" style="margin-top: 10px">
|
||||||
<vxe-column field="options.style.properties.FID_1" title="厂房id"></vxe-column>
|
<vxe-column field="options.style.properties.FID_1" title="厂房id"></vxe-column>
|
||||||
<vxe-column field="area" title="面积(㎡)">
|
<vxe-column field="area" title="面积(㎡)">
|
||||||
<template v-slot="{row}">
|
<template v-slot="{row}">
|
||||||
@ -174,14 +157,14 @@
|
|||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column field="vehicles" title="车辆">
|
<vxe-column field="vehicles" title="车辆">
|
||||||
<template v-slot="{row}">
|
<template v-slot="{row}">
|
||||||
{{ (row.vehicles.map(item => item.name)).join(',')}}
|
{{ row.vehicles.map((item) => item.name).join(',') }}
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog :visible.sync="dialogVisible" title="车辆选择" width="800px">
|
<el-dialog :visible.sync="dialogVisible" title="车辆选择" width="800px">
|
||||||
<div style="margin-bottom: 10px;">
|
<div style="margin-bottom: 10px">
|
||||||
<el-button type="primary" size="mini" @click="handleAdd">新增</el-button>
|
<el-button type="primary" size="mini" @click="handleAdd">新增</el-button>
|
||||||
</div>
|
</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
@ -248,7 +231,7 @@
|
|||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import iniParser from 'ini-parser'
|
import iniParser from 'ini-parser'
|
||||||
import configIni from '/config.ini';
|
import configIni from '/public/config.ini'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -321,7 +304,7 @@ export default {
|
|||||||
.catch((error) => {})
|
.catch((error) => {})
|
||||||
},
|
},
|
||||||
async initMap() {
|
async initMap() {
|
||||||
const parsedData = iniParser.parse(configIni);
|
const parsedData = iniParser.parse(configIni)
|
||||||
this.viewer = new window.mars3d.Map(
|
this.viewer = new window.mars3d.Map(
|
||||||
'map',
|
'map',
|
||||||
{
|
{
|
||||||
@ -450,7 +433,7 @@ export default {
|
|||||||
outline: false,
|
outline: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
this.graphicLayer.addGraphic(graphicLine);
|
this.graphicLayer.addGraphic(graphicLine)
|
||||||
})
|
})
|
||||||
this.roadNetworkGeoJSONBuild = this.buildGraph(this.roadNetworkGeoJSON)
|
this.roadNetworkGeoJSONBuild = this.buildGraph(this.roadNetworkGeoJSON)
|
||||||
this.loadFactoryGeoJson() // 拿到厂房数据
|
this.loadFactoryGeoJson() // 拿到厂房数据
|
||||||
@ -500,7 +483,7 @@ export default {
|
|||||||
this.accordPoint = graphic
|
this.accordPoint = graphic
|
||||||
try {
|
try {
|
||||||
// 创建缓冲区的宽度
|
// 创建缓冲区的宽度
|
||||||
const bufferWidth = this.hideform.radius; // 避让区的宽度(单位:米)
|
const bufferWidth = this.hideform.radius // 避让区的宽度(单位:米)
|
||||||
// 获取绘制的点的坐标
|
// 获取绘制的点的坐标
|
||||||
const pointCoordinates = [
|
const pointCoordinates = [
|
||||||
graphic.toGeoJSON().geometry.coordinates[0],
|
graphic.toGeoJSON().geometry.coordinates[0],
|
||||||
@ -514,12 +497,12 @@ export default {
|
|||||||
coordinates: pointCoordinates,
|
coordinates: pointCoordinates,
|
||||||
},
|
},
|
||||||
properties: {},
|
properties: {},
|
||||||
};
|
}
|
||||||
// 使用 Turf.js 计算缓冲区
|
// 使用 Turf.js 计算缓冲区
|
||||||
let buffered = turf.buffer(pointFeature, bufferWidth / 1000, { units: 'kilometers' });
|
let buffered = turf.buffer(pointFeature, bufferWidth / 1000, {units: 'kilometers'})
|
||||||
|
|
||||||
// 将缓冲区存储到数组中
|
// 将缓冲区存储到数组中
|
||||||
this.bufferLayerList.push(buffered);
|
this.bufferLayerList.push(buffered)
|
||||||
const polygon = new window.mars3d.graphic.PolygonEntity({
|
const polygon = new window.mars3d.graphic.PolygonEntity({
|
||||||
positions: buffered.geometry.coordinates[0],
|
positions: buffered.geometry.coordinates[0],
|
||||||
style: {
|
style: {
|
||||||
@ -529,13 +512,13 @@ export default {
|
|||||||
outlineWidth: 1,
|
outlineWidth: 1,
|
||||||
outlineColor: '#ffffff',
|
outlineColor: '#ffffff',
|
||||||
},
|
},
|
||||||
time: new Date().getTime()
|
time: new Date().getTime(),
|
||||||
})
|
})
|
||||||
this.accordFactoryLayer.addGraphic(polygon);
|
this.accordFactoryLayer.addGraphic(polygon)
|
||||||
// 检查缓冲区内的厂房
|
// 检查缓冲区内的厂房
|
||||||
this.checkFactoryInBuffer('point', pointCoordinates);
|
this.checkFactoryInBuffer('point', pointCoordinates)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("缓冲区生成或检查异常:", error);
|
console.error('缓冲区生成或检查异常:', error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -555,30 +538,30 @@ export default {
|
|||||||
for (const feature of this.shortestPathList[0]) {
|
for (const feature of this.shortestPathList[0]) {
|
||||||
// this.shortestPathList[0].forEach((feature) => {
|
// this.shortestPathList[0].forEach((feature) => {
|
||||||
// 创建缓冲区的宽度
|
// 创建缓冲区的宽度
|
||||||
const bufferWidth = this.hideform.radius; // 避让区的宽度(单位:米)
|
const bufferWidth = this.hideform.radius // 避让区的宽度(单位:米)
|
||||||
if (feature.geometry.type === 'LineString') {
|
if (feature.geometry.type === 'LineString') {
|
||||||
const positions = feature.geometry.coordinates[0];
|
const positions = feature.geometry.coordinates[0]
|
||||||
// 确保每条线至少有 2 个点
|
// 确保每条线至少有 2 个点
|
||||||
if (positions.length < 2) {
|
if (positions.length < 2) {
|
||||||
console.warn('无效的线,跳过:', feature);
|
console.warn('无效的线,跳过:', feature)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// 使用 Turf.js 计算缓冲区
|
// 使用 Turf.js 计算缓冲区
|
||||||
var buffered = turf.buffer(feature, bufferWidth / 1000, { units: 'kilometers' });
|
var buffered = turf.buffer(feature, bufferWidth / 1000, {units: 'kilometers'})
|
||||||
|
|
||||||
// 将缓冲区存储到数组中
|
// 将缓冲区存储到数组中
|
||||||
this.bufferLayerList.push(buffered);
|
this.bufferLayerList.push(buffered)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("缓冲分析异常:", error);
|
console.error('缓冲分析异常:', error)
|
||||||
}
|
}
|
||||||
} else if (feature.geometry.type === 'MultiLineString') {
|
} else if (feature.geometry.type === 'MultiLineString') {
|
||||||
// 遍历每个线段
|
// 遍历每个线段
|
||||||
feature.geometry.coordinates.forEach((lineCoordinates) => {
|
feature.geometry.coordinates.forEach((lineCoordinates) => {
|
||||||
// 检查每个线段是否有效
|
// 检查每个线段是否有效
|
||||||
if (lineCoordinates.length < 2) {
|
if (lineCoordinates.length < 2) {
|
||||||
console.warn('多线段中的无效的线段,跳过:', lineCoordinates);
|
console.warn('多线段中的无效的线段,跳过:', lineCoordinates)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建单个线段的 GeoJSON 特征
|
// 创建单个线段的 GeoJSON 特征
|
||||||
@ -588,27 +571,27 @@ export default {
|
|||||||
type: 'LineString',
|
type: 'LineString',
|
||||||
coordinates: lineCoordinates,
|
coordinates: lineCoordinates,
|
||||||
},
|
},
|
||||||
properties: feature.properties
|
properties: feature.properties,
|
||||||
};
|
}
|
||||||
try {
|
try {
|
||||||
// 使用 Turf.js 计算缓冲区
|
// 使用 Turf.js 计算缓冲区
|
||||||
var buffered = turf.buffer(lineFeature, bufferWidth / 1000, { units: 'kilometers' });
|
var buffered = turf.buffer(lineFeature, bufferWidth / 1000, {units: 'kilometers'})
|
||||||
|
|
||||||
// 将缓冲区存储到数组中
|
// 将缓冲区存储到数组中
|
||||||
this.bufferLayerList.push(buffered);
|
this.bufferLayerList.push(buffered)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("缓冲分析异常:", error);
|
console.error('缓冲分析异常:', error)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
console.warn('不支持的几何类型:', feature.geometry.type);
|
console.warn('不支持的几何类型:', feature.geometry.type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去筛选在缓存区的厂房
|
// 去筛选在缓存区的厂房
|
||||||
this.checkFactoryInBuffer('line');
|
this.checkFactoryInBuffer('line')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("处理路径列表时发生错误:", error);
|
console.error('处理路径列表时发生错误:', error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkFactoryInBuffer(type, pointInfo) {
|
checkFactoryInBuffer(type, pointInfo) {
|
||||||
@ -629,106 +612,106 @@ export default {
|
|||||||
// 检查几何对象的类型
|
// 检查几何对象的类型
|
||||||
if (factory.geometry.type === 'MultiPolygon') {
|
if (factory.geometry.type === 'MultiPolygon') {
|
||||||
// 创建多边形集合(MultiPolygon)
|
// 创建多边形集合(MultiPolygon)
|
||||||
const factoryMultiPoly = turf.multiPolygon(factory.geometry.coordinates);
|
const factoryMultiPoly = turf.multiPolygon(factory.geometry.coordinates)
|
||||||
// 计算整个多边形集合的面积
|
// 计算整个多边形集合的面积
|
||||||
const area = turf.area(factoryMultiPoly);
|
const area = turf.area(factoryMultiPoly)
|
||||||
// 计算工厂多边形集合的边界框
|
// 计算工厂多边形集合的边界框
|
||||||
const [minX, minY, maxX, maxY] = turf.bbox(factoryMultiPoly);
|
const [minX, minY, maxX, maxY] = turf.bbox(factoryMultiPoly)
|
||||||
|
|
||||||
// 筛选与工厂边界框相交的缓冲区
|
// 筛选与工厂边界框相交的缓冲区
|
||||||
const candidates = this.bufferLayerList.filter((buffer) => {
|
const candidates = this.bufferLayerList.filter((buffer) => {
|
||||||
const [bminX, bminY, bmaxX, bmaxY] = turf.bbox(buffer);
|
const [bminX, bminY, bmaxX, bmaxY] = turf.bbox(buffer)
|
||||||
return !(bmaxX < minX || bminX > maxX || bmaxY < minY || bminY > maxY);
|
return !(bmaxX < minX || bminX > maxX || bmaxY < minY || bminY > maxY)
|
||||||
});
|
})
|
||||||
// 精确相交检测
|
// 精确相交检测
|
||||||
let isInside = false;
|
let isInside = false
|
||||||
for (const buffer of candidates) {
|
for (const buffer of candidates) {
|
||||||
if (turf.booleanIntersects(factoryMultiPoly, buffer)) {
|
if (turf.booleanIntersects(factoryMultiPoly, buffer)) {
|
||||||
// 如果工厂在缓冲区内,绘制所有多边形
|
// 如果工厂在缓冲区内,绘制所有多边形
|
||||||
isInside = true;
|
isInside = true
|
||||||
this.drawFactory(factory.geometry.coordinates, factory, area, pointInfo);
|
this.drawFactory(factory.geometry.coordinates, factory, area, pointInfo)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 如果工厂不在任何缓冲区内
|
// 如果工厂不在任何缓冲区内
|
||||||
resolve();
|
resolve()
|
||||||
} else {
|
} else {
|
||||||
reject(new Error('不支持的几何类型'));
|
reject(new Error('不支持的几何类型'))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
// 确保所有工厂处理完成后进行排序
|
// 确保所有工厂处理完成后进行排序
|
||||||
Promise.allSettled(factoryPromises)
|
Promise.allSettled(factoryPromises)
|
||||||
.then(async (results) => {
|
.then(async (results) => {
|
||||||
// 只有路线隐蔽规划需要塞入车辆 点的不需要
|
// 只有路线隐蔽规划需要塞入车辆 点的不需要
|
||||||
// 所有工厂处理完成,进行排序 按照距离近到远
|
// 所有工厂处理完成,进行排序 按照距离近到远
|
||||||
this.accordFactoryInfo.sort((a, b) => a.distance - b.distance);
|
this.accordFactoryInfo.sort((a, b) => a.distance - b.distance)
|
||||||
// 拿到当前的车队车辆信息 并且算出面积 按照从大到小排序
|
// 拿到当前的车队车辆信息 并且算出面积 按照从大到小排序
|
||||||
const areaList = JSON.parse(Cookies.get('minTurnRadius'))
|
const areaList = JSON.parse(Cookies.get('minTurnRadius'))
|
||||||
areaList.map((item) => {
|
areaList.map((item) => {
|
||||||
item.area = Number(item.long) * Number(item.width)
|
item.area = Number(item.long) * Number(item.width)
|
||||||
})
|
})
|
||||||
areaList.sort((a, b) => b.area - a.area);
|
areaList.sort((a, b) => b.area - a.area)
|
||||||
// 初始化每个工厂的剩余面积
|
// 初始化每个工厂的剩余面积
|
||||||
this.accordFactoryInfo.forEach(factory => {
|
this.accordFactoryInfo.forEach((factory) => {
|
||||||
factory.remainingArea = factory.area; // 记录每个工厂的剩余可用面积
|
factory.remainingArea = factory.area // 记录每个工厂的剩余可用面积
|
||||||
factory.vehicles = []; // 确保每个工厂都有一个 vehicles 属性
|
factory.vehicles = [] // 确保每个工厂都有一个 vehicles 属性
|
||||||
});
|
})
|
||||||
// 遍历每个车辆,尝试塞入工厂
|
// 遍历每个车辆,尝试塞入工厂
|
||||||
areaList.forEach((vehicle) => {
|
areaList.forEach((vehicle) => {
|
||||||
let isPlaced = false;
|
let isPlaced = false
|
||||||
|
|
||||||
// 遍历每个工厂,尝试塞入车辆
|
// 遍历每个工厂,尝试塞入车辆
|
||||||
for (let i = 0; i < this.accordFactoryInfo.length; i++) {
|
for (let i = 0; i < this.accordFactoryInfo.length; i++) {
|
||||||
const factory = this.accordFactoryInfo[i];
|
const factory = this.accordFactoryInfo[i]
|
||||||
|
|
||||||
// 检查工厂是否还有剩余面积
|
// 检查工厂是否还有剩余面积
|
||||||
if (factory.remainingArea >= vehicle.area) {
|
if (factory.remainingArea >= vehicle.area) {
|
||||||
// 塞入车辆
|
// 塞入车辆
|
||||||
factory.vehicles.push({
|
factory.vehicles.push({
|
||||||
...vehicle,
|
...vehicle,
|
||||||
remainingArea: factory.remainingArea - vehicle.area // 记录车辆塞入后的工厂剩余面积
|
remainingArea: factory.remainingArea - vehicle.area, // 记录车辆塞入后的工厂剩余面积
|
||||||
});
|
})
|
||||||
|
|
||||||
// 更新工厂的剩余面积
|
// 更新工厂的剩余面积
|
||||||
factory.remainingArea -= vehicle.area;
|
factory.remainingArea -= vehicle.area
|
||||||
|
|
||||||
// 标记车辆已放置
|
// 标记车辆已放置
|
||||||
isPlaced = true;
|
isPlaced = true
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果车辆没有被放置,可以在这里处理(例如记录日志或显示警告)
|
// 如果车辆没有被放置,可以在这里处理(例如记录日志或显示警告)
|
||||||
if (!isPlaced) {
|
if (!isPlaced) {
|
||||||
console.warn(`车辆 ${vehicle.id} 无法放置在任何工厂中`);
|
console.warn(`车辆 ${vehicle.id} 无法放置在任何工厂中`)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// 更新 this.accordFactoryInfo
|
// 更新 this.accordFactoryInfo
|
||||||
this.accordFactoryInfo = this.accordFactoryInfo.map(factory => {
|
this.accordFactoryInfo = this.accordFactoryInfo.map((factory) => {
|
||||||
// 确保每个工厂都有 vehicles 属性
|
// 确保每个工厂都有 vehicles 属性
|
||||||
if (!factory.vehicles) {
|
if (!factory.vehicles) {
|
||||||
factory.vehicles = [];
|
factory.vehicles = []
|
||||||
}
|
}
|
||||||
// 确保 remainingArea 是一个数字且大于 0
|
// 确保 remainingArea 是一个数字且大于 0
|
||||||
factory.area = factory.area > 0 ? factory.area : 0;
|
factory.area = factory.area > 0 ? factory.area : 0
|
||||||
return factory;
|
return factory
|
||||||
});
|
})
|
||||||
// 过滤出有车辆的工厂
|
// 过滤出有车辆的工厂
|
||||||
const factoriesWithVehicles = this.accordFactoryInfo.filter(factory => factory.vehicles.length > 0);
|
const factoriesWithVehicles = this.accordFactoryInfo.filter((factory) => factory.vehicles.length > 0)
|
||||||
this.factoriesWithVehicles = factoriesWithVehicles
|
this.factoriesWithVehicles = factoriesWithVehicles
|
||||||
await this.showAreaInfoDialog(factoriesWithVehicles);
|
await this.showAreaInfoDialog(factoriesWithVehicles)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('数据有问题', error)
|
console.error('数据有问题', error)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
drawFactory(factoryMultiPoly, factory, area, pointInfo) {
|
drawFactory(factoryMultiPoly, factory, area, pointInfo) {
|
||||||
// 获取当前多边形集合的中心点
|
// 获取当前多边形集合的中心点
|
||||||
const center = turf.center(turf.multiPolygon(factory.geometry.coordinates));
|
const center = turf.center(turf.multiPolygon(factory.geometry.coordinates))
|
||||||
// 将中心点转换为 Mars3D 的 LngLatPoint 对象
|
// 将中心点转换为 Mars3D 的 LngLatPoint 对象
|
||||||
const popupPosition = new mars3d.LngLatPoint(center.geometry.coordinates[0], center.geometry.coordinates[1]);
|
const popupPosition = new mars3d.LngLatPoint(center.geometry.coordinates[0], center.geometry.coordinates[1])
|
||||||
// 遍历每个多边形集合
|
// 遍历每个多边形集合
|
||||||
factory.geometry.coordinates[0].forEach((coordGroup) => {
|
factory.geometry.coordinates[0].forEach((coordGroup) => {
|
||||||
// 创建一个多边形图形
|
// 创建一个多边形图形
|
||||||
@ -742,18 +725,19 @@ export default {
|
|||||||
outlineColor: '#ffffff',
|
outlineColor: '#ffffff',
|
||||||
properties: {...factory.properties},
|
properties: {...factory.properties},
|
||||||
},
|
},
|
||||||
time: new Date().getTime()
|
time: new Date().getTime(),
|
||||||
})
|
})
|
||||||
this.accordFactoryLayer.addGraphic(graphic);
|
this.accordFactoryLayer.addGraphic(graphic)
|
||||||
// 计算多边形中心与参考点的距离
|
// 计算多边形中心与参考点的距离
|
||||||
const point = pointInfo && pointInfo.length > 0 ? pointInfo : this.form.endPoint.split(',').map(Number)
|
const point = pointInfo && pointInfo.length > 0 ? pointInfo : this.form.endPoint.split(',').map(Number)
|
||||||
const distance = turf.distance(center.geometry.coordinates, point, { units: 'kilometers' }) * 1000; // 米
|
const distance = turf.distance(center.geometry.coordinates, point, {units: 'kilometers'}) * 1000 // 米
|
||||||
// 将多边形图形添加到地图层
|
// 将多边形图形添加到地图层
|
||||||
this.accordFactoryInfo.push({
|
this.accordFactoryInfo.push({
|
||||||
...graphic,
|
...graphic,
|
||||||
distance: distance,
|
distance: distance,
|
||||||
popupPosition: popupPosition,
|
popupPosition: popupPosition,
|
||||||
area: this.hideform.redundancy && this.hideform.redundancy != 0 && area > 0
|
area:
|
||||||
|
this.hideform.redundancy && this.hideform.redundancy != 0 && area > 0
|
||||||
? area * (100 - this.hideform.redundancy) * 0.01
|
? area * (100 - this.hideform.redundancy) * 0.01
|
||||||
: area > 0
|
: area > 0
|
||||||
? area
|
? area
|
||||||
@ -763,12 +747,15 @@ export default {
|
|||||||
},
|
},
|
||||||
// 显示面积信息弹框的方法
|
// 显示面积信息弹框的方法
|
||||||
async showAreaInfoDialog(info) {
|
async showAreaInfoDialog(info) {
|
||||||
const graphics = this.accordFactoryLayer.getGraphics();
|
const graphics = this.accordFactoryLayer.getGraphics()
|
||||||
const promises = [];
|
const promises = []
|
||||||
info.forEach((item) => {
|
info.forEach((item) => {
|
||||||
graphics.forEach((ele) => {
|
graphics.forEach((ele) => {
|
||||||
if (ele.options.time === item.options.time && ele.options.style.properties.FID_1 === item.options.style.properties.FID_1) {
|
if (
|
||||||
const textInfo = `${item.area.toFixed(2)}㎡ \n(${item.vehicles.map(e => e.name).join(',')})`
|
ele.options.time === item.options.time &&
|
||||||
|
ele.options.style.properties.FID_1 === item.options.style.properties.FID_1
|
||||||
|
) {
|
||||||
|
const textInfo = `${item.area.toFixed(2)}㎡ \n(${item.vehicles.map((e) => e.name).join(',')})`
|
||||||
const labelGraphic = new window.mars3d.graphic.LabelEntity({
|
const labelGraphic = new window.mars3d.graphic.LabelEntity({
|
||||||
id: 'label',
|
id: 'label',
|
||||||
position: item.popupPosition,
|
position: item.popupPosition,
|
||||||
@ -781,34 +768,34 @@ export default {
|
|||||||
outlineColor: '#000000',
|
outlineColor: '#000000',
|
||||||
background: true,
|
background: true,
|
||||||
backgroundColor: '#2f705f',
|
backgroundColor: '#2f705f',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
// 使用 Promise 确保标签添加完成后继续执行
|
// 使用 Promise 确保标签添加完成后继续执行
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
this.accordFactoryLayer.addGraphic(labelGraphic, () => {
|
this.accordFactoryLayer.addGraphic(labelGraphic, () => {
|
||||||
resolve();
|
resolve()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
promises.push(promise);
|
promises.push(promise)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.$message.success("路线隐蔽规划成功")
|
this.$message.success('路线隐蔽规划成功')
|
||||||
const popupPositionList = info.map(item => item.popupPosition)
|
const popupPositionList = info.map((item) => item.popupPosition)
|
||||||
let minLng = Infinity;
|
let minLng = Infinity
|
||||||
let maxLng = -Infinity;
|
let maxLng = -Infinity
|
||||||
let minLat = Infinity;
|
let minLat = Infinity
|
||||||
let maxLat = -Infinity;
|
let maxLat = -Infinity
|
||||||
|
|
||||||
popupPositionList.forEach(point => {
|
popupPositionList.forEach((point) => {
|
||||||
if (point.lng < minLng) minLng = point.lng;
|
if (point.lng < minLng) minLng = point.lng
|
||||||
if (point.lng > maxLng) maxLng = point.lng;
|
if (point.lng > maxLng) maxLng = point.lng
|
||||||
if (point.lat < minLat) minLat = point.lat;
|
if (point.lat < minLat) minLat = point.lat
|
||||||
if (point.lat > maxLat) maxLat = point.lat;
|
if (point.lat > maxLat) maxLat = point.lat
|
||||||
});
|
})
|
||||||
// 创建一个矩形区域,包含所有点
|
// 创建一个矩形区域,包含所有点
|
||||||
const rectangle = Cesium.Rectangle.fromDegrees(minLng, minLat, maxLng, maxLat);
|
const rectangle = Cesium.Rectangle.fromDegrees(minLng, minLat, maxLng, maxLat)
|
||||||
|
|
||||||
this.viewer.scene.camera.flyTo({
|
this.viewer.scene.camera.flyTo({
|
||||||
destination: Cesium.Cartesian3.fromDegrees(
|
destination: Cesium.Cartesian3.fromDegrees(
|
||||||
@ -819,10 +806,10 @@ export default {
|
|||||||
orientation: {
|
orientation: {
|
||||||
heading: Cesium.Math.toRadians(0), // 方位角
|
heading: Cesium.Math.toRadians(0), // 方位角
|
||||||
pitch: Cesium.Math.toRadians(-90), // 俯仰角
|
pitch: Cesium.Math.toRadians(-90), // 俯仰角
|
||||||
roll: 0.0 // 翻滚角
|
roll: 0.0, // 翻滚角
|
||||||
},
|
},
|
||||||
duration: 2 // 飞行动画持续时间,单位为秒
|
duration: 2, // 飞行动画持续时间,单位为秒
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
// 弹框
|
// 弹框
|
||||||
async openDialog() {
|
async openDialog() {
|
||||||
@ -832,7 +819,7 @@ export default {
|
|||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then(async (data) => {
|
.then(async (data) => {
|
||||||
this.dialogVisible = true;
|
this.dialogVisible = true
|
||||||
this.tableData = data
|
this.tableData = data
|
||||||
await this.$nextTick(() => {
|
await this.$nextTick(() => {
|
||||||
this.multipleSelection = JSON.parse(Cookies.get('minTurnRadius'))
|
this.multipleSelection = JSON.parse(Cookies.get('minTurnRadius'))
|
||||||
@ -879,35 +866,39 @@ export default {
|
|||||||
load: null,
|
load: null,
|
||||||
minTurnRadius: null,
|
minTurnRadius: null,
|
||||||
editing: true,
|
editing: true,
|
||||||
};
|
}
|
||||||
this.tableData.push(newRow);
|
this.tableData.push(newRow)
|
||||||
},
|
},
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
const index = this.tableData.findIndex(item => item.id === row.id);
|
const index = this.tableData.findIndex((item) => item.id === row.id)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.tableData.splice(index, 1);
|
this.tableData.splice(index, 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleEdit(row) {
|
handleEdit(row) {
|
||||||
this.$set(row, 'editing', true);
|
this.$set(row, 'editing', true)
|
||||||
},
|
},
|
||||||
handleSave(row) {
|
handleSave(row) {
|
||||||
this.$set(row, 'editing', false);
|
this.$set(row, 'editing', false)
|
||||||
},
|
},
|
||||||
// 保存列表数据
|
// 保存列表数据
|
||||||
suerCofirm() {
|
suerCofirm() {
|
||||||
const parsedData = iniParser.parse(configIni);
|
const parsedData = iniParser.parse(configIni)
|
||||||
axios.post(`http://${parsedData.http.address}:${parsedData.http.port}/api/equpment`, JSON.stringify(this.tableData), {
|
axios
|
||||||
|
.post(
|
||||||
|
`http://${parsedData.http.address}:${parsedData.http.port}/api/equpment`,
|
||||||
|
JSON.stringify(this.tableData),
|
||||||
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer your_token_here',
|
Authorization: 'Bearer your_token_here',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
this.$message.success('保存成功')
|
this.$message.success('保存成功')
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {})
|
||||||
});
|
|
||||||
},
|
},
|
||||||
// 导入json文件
|
// 导入json文件
|
||||||
triggerFileUpload() {
|
triggerFileUpload() {
|
||||||
@ -974,21 +965,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
addPointToMap(type, point, time) {
|
addPointToMap(type, point, time) {
|
||||||
const coords = time
|
const coords = time ? point.points.split(',').map(Number) : point.split(',').map(Number)
|
||||||
? point.points.split(',').map(Number)
|
|
||||||
: point.split(',').map(Number)
|
|
||||||
const graphic = new window.mars3d.graphic.PointEntity({
|
const graphic = new window.mars3d.graphic.PointEntity({
|
||||||
position: new window.mars3d.LngLatPoint(coords[0], coords[1]),
|
position: new window.mars3d.LngLatPoint(coords[0], coords[1]),
|
||||||
style: {
|
style: {
|
||||||
pixelSize: 10,
|
pixelSize: 10,
|
||||||
color:
|
color: type === 'startPoint' ? 'red' : type === 'endPoint' ? 'red' : type === 'viaPoints' ? 'blue' : 'orange',
|
||||||
type === 'startPoint'
|
|
||||||
? 'red'
|
|
||||||
: type === 'endPoint'
|
|
||||||
? 'red'
|
|
||||||
: type === 'viaPoints'
|
|
||||||
? 'blue'
|
|
||||||
: 'orange',
|
|
||||||
label: {
|
label: {
|
||||||
text:
|
text:
|
||||||
type === 'startPoint'
|
type === 'startPoint'
|
||||||
@ -1022,9 +1004,7 @@ export default {
|
|||||||
addPolygonToMap(type, area, time) {
|
addPolygonToMap(type, area, time) {
|
||||||
const coords = JSON.parse(area.points)
|
const coords = JSON.parse(area.points)
|
||||||
const graphic = new window.mars3d.graphic.PolygonEntity({
|
const graphic = new window.mars3d.graphic.PolygonEntity({
|
||||||
positions: coords.map(
|
positions: coords.map((coord) => new window.mars3d.LngLatPoint(coord[0], coord[1])),
|
||||||
(coord) => new window.mars3d.LngLatPoint(coord[0], coord[1])
|
|
||||||
),
|
|
||||||
style: {
|
style: {
|
||||||
color: 'red',
|
color: 'red',
|
||||||
opacity: 0.4,
|
opacity: 0.4,
|
||||||
@ -1079,94 +1059,55 @@ export default {
|
|||||||
}
|
}
|
||||||
} else if (type === 'viaPoints') {
|
} else if (type === 'viaPoints') {
|
||||||
if (!row.points) {
|
if (!row.points) {
|
||||||
const graphic = this.viaPoints.find(
|
const graphic = this.viaPoints.find((viaPoint) => viaPoint.style.time === row.time)
|
||||||
(viaPoint) => viaPoint.style.time === row.time
|
if (this.form.viaPoints.length === 1 && this.form.viaPoints[0].points === '') {
|
||||||
)
|
|
||||||
if (
|
|
||||||
this.form.viaPoints.length === 1 &&
|
|
||||||
this.form.viaPoints[0].points === ''
|
|
||||||
) {
|
|
||||||
// 如果只剩下一个空项,不删除图形,清空输入框值
|
// 如果只剩下一个空项,不删除图形,清空输入框值
|
||||||
this.form.viaPoints[0].points = ''
|
this.form.viaPoints[0].points = ''
|
||||||
graphic?.remove()
|
graphic?.remove()
|
||||||
this.viaPoints = this.viaPoints.filter(
|
this.viaPoints = this.viaPoints.filter((viaPoint) => viaPoint.style.time !== row.time)
|
||||||
(viaPoint) => viaPoint.style.time !== row.time
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
this.form.viaPoints = this.form.viaPoints.filter(
|
this.form.viaPoints = this.form.viaPoints.filter((viaPoint) => viaPoint.time !== row.time)
|
||||||
(viaPoint) => viaPoint.time !== row.time
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
graphic?.remove()
|
graphic?.remove()
|
||||||
this.viaPoints = this.viaPoints.filter(
|
this.viaPoints = this.viaPoints.filter((viaPoint) => viaPoint.style.time !== row.time)
|
||||||
(viaPoint) => viaPoint.style.time !== row.time
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
const graphic = this.viaPoints.find(
|
const graphic = this.viaPoints.find((viaPoint) => viaPoint.style.time === row.time)
|
||||||
(viaPoint) => viaPoint.style.time === row.time
|
|
||||||
)
|
|
||||||
this.updatePointPosition(graphic, row.points)
|
this.updatePointPosition(graphic, row.points)
|
||||||
}
|
}
|
||||||
} else if (type === 'avoidPoints') {
|
} else if (type === 'avoidPoints') {
|
||||||
if (!row.points) {
|
if (!row.points) {
|
||||||
const graphic = this.avoidPoints.find(
|
const graphic = this.avoidPoints.find((avoidPoint) => avoidPoint.style.time === row.time)
|
||||||
(avoidPoint) => avoidPoint.style.time === row.time
|
if (this.form.avoidPoints.length === 1 && this.form.avoidPoints[0].points === '') {
|
||||||
)
|
|
||||||
if (
|
|
||||||
this.form.avoidPoints.length === 1 &&
|
|
||||||
this.form.avoidPoints[0].points === ''
|
|
||||||
) {
|
|
||||||
// 如果只剩下一个空项,不删除图形,清空输入框值
|
// 如果只剩下一个空项,不删除图形,清空输入框值
|
||||||
this.form.avoidPoints[0].points = ''
|
this.form.avoidPoints[0].points = ''
|
||||||
} else {
|
} else {
|
||||||
this.form.avoidPoints = this.form.avoidPoints.filter(
|
this.form.avoidPoints = this.form.avoidPoints.filter((avoidPoint) => avoidPoint.time !== row.time)
|
||||||
(avoidPoint) => avoidPoint.time !== row.time
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
graphic?.remove()
|
graphic?.remove()
|
||||||
this.avoidPoints = this.avoidPoints.filter(
|
this.avoidPoints = this.avoidPoints.filter((avoidPoint) => avoidPoint.style.time !== row.time)
|
||||||
(avoidPoint) => avoidPoint.style.time !== row.time
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
const graphic = this.avoidPoints.find(
|
const graphic = this.avoidPoints.find((avoidPoint) => avoidPoint.style.time === row.time)
|
||||||
(avoidPoint) => avoidPoint.style.time === row.time
|
|
||||||
)
|
|
||||||
this.updatePointPosition(graphic, row.points)
|
this.updatePointPosition(graphic, row.points)
|
||||||
}
|
}
|
||||||
} else if (type === 'avoidAreas') {
|
} else if (type === 'avoidAreas') {
|
||||||
if (!row.points) {
|
if (!row.points) {
|
||||||
const graphic = this.avoidAreas.find(
|
const graphic = this.avoidAreas.find((avoidArea) => avoidArea.style.time === row.time)
|
||||||
(avoidArea) => avoidArea.style.time === row.time
|
if (this.form.avoidAreas.length === 1 && this.form.avoidAreas[0].points === '') {
|
||||||
)
|
|
||||||
if (
|
|
||||||
this.form.avoidAreas.length === 1 &&
|
|
||||||
this.form.avoidAreas[0].points === ''
|
|
||||||
) {
|
|
||||||
// 如果只剩下一个空项,不删除图形,清空输入框值
|
// 如果只剩下一个空项,不删除图形,清空输入框值
|
||||||
this.form.avoidAreas[0].points = ''
|
this.form.avoidAreas[0].points = ''
|
||||||
} else {
|
} else {
|
||||||
this.form.avoidAreas = this.form.avoidAreas.filter(
|
this.form.avoidAreas = this.form.avoidAreas.filter((avoidArea) => avoidArea.time !== row.time)
|
||||||
(avoidArea) => avoidArea.time !== row.time
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
graphic?.remove()
|
graphic?.remove()
|
||||||
this.avoidAreas = this.avoidAreas.filter(
|
this.avoidAreas = this.avoidAreas.filter((avoidArea) => avoidArea.style.time !== row.time)
|
||||||
(avoidArea) => avoidArea.style.time !== row.time
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
const graphic = this.avoidAreas.find(
|
const graphic = this.avoidAreas.find((avoidArea) => avoidArea.style.time === row.time)
|
||||||
(avoidArea) => avoidArea.style.time === row.time
|
|
||||||
)
|
|
||||||
this.updatePolygonPosition(graphic, row.points)
|
this.updatePolygonPosition(graphic, row.points)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleEmptyArray(type) {
|
handleEmptyArray(type) {
|
||||||
if (
|
if (type === 'viaPoints' && this.form.viaPoints.length === 1 && this.form.viaPoints[0].points === '') {
|
||||||
type === 'viaPoints' &&
|
|
||||||
this.form.viaPoints.length === 1 &&
|
|
||||||
this.form.viaPoints[0].points === ''
|
|
||||||
) {
|
|
||||||
this.form.viaPoints = []
|
this.form.viaPoints = []
|
||||||
} else if (
|
} else if (
|
||||||
type === 'avoidPoints' &&
|
type === 'avoidPoints' &&
|
||||||
@ -1174,11 +1115,7 @@ export default {
|
|||||||
this.form.avoidPoints[0].points === ''
|
this.form.avoidPoints[0].points === ''
|
||||||
) {
|
) {
|
||||||
this.form.avoidPoints = []
|
this.form.avoidPoints = []
|
||||||
} else if (
|
} else if (type === 'avoidAreas' && this.form.avoidAreas.length === 1 && this.form.avoidAreas[0].points === '') {
|
||||||
type === 'avoidAreas' &&
|
|
||||||
this.form.avoidAreas.length === 1 &&
|
|
||||||
this.form.avoidAreas[0].points === ''
|
|
||||||
) {
|
|
||||||
this.form.avoidAreas = []
|
this.form.avoidAreas = []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1193,9 +1130,7 @@ export default {
|
|||||||
if (!graphic) return
|
if (!graphic) return
|
||||||
try {
|
try {
|
||||||
const coords = JSON.parse(pointsStr)
|
const coords = JSON.parse(pointsStr)
|
||||||
graphic.positions = coords.map(
|
graphic.positions = coords.map((coord) => new window.mars3d.LngLatPoint(coord[0], coord[1]))
|
||||||
(coord) => new window.mars3d.LngLatPoint(coord[0], coord[1])
|
|
||||||
)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
graphic.remove()
|
graphic.remove()
|
||||||
}
|
}
|
||||||
@ -1350,9 +1285,7 @@ export default {
|
|||||||
success: (graphic) => {
|
success: (graphic) => {
|
||||||
this.avoidAreas.push(graphic)
|
this.avoidAreas.push(graphic)
|
||||||
const avoidAreasGeoJSON = graphic.toGeoJSON()
|
const avoidAreasGeoJSON = graphic.toGeoJSON()
|
||||||
const points = JSON.stringify(
|
const points = JSON.stringify(avoidAreasGeoJSON.geometry.coordinates[0])
|
||||||
avoidAreasGeoJSON.geometry.coordinates[0]
|
|
||||||
)
|
|
||||||
if (
|
if (
|
||||||
this.form.avoidAreas.length == 1 &&
|
this.form.avoidAreas.length == 1 &&
|
||||||
this.form.avoidAreas[0].points == '' &&
|
this.form.avoidAreas[0].points == '' &&
|
||||||
@ -1385,11 +1318,8 @@ export default {
|
|||||||
}
|
}
|
||||||
// 计算边权重(距离,单位:米)
|
// 计算边权重(距离,单位:米)
|
||||||
const distance =
|
const distance =
|
||||||
turf.distance(
|
turf.distance(turf.point(coords[0][0]), turf.point(coords[coords.length - 1][0]), {units: 'kilometers'}) *
|
||||||
turf.point(coords[0][0]),
|
1000
|
||||||
turf.point(coords[coords.length - 1][0]),
|
|
||||||
{units: 'kilometers'}
|
|
||||||
) * 1000
|
|
||||||
|
|
||||||
// 构建邻接表(双向图)
|
// 构建邻接表(双向图)
|
||||||
graph[startNode] = graph[startNode] || {}
|
graph[startNode] = graph[startNode] || {}
|
||||||
@ -1418,22 +1348,17 @@ export default {
|
|||||||
return nearestNode
|
return nearestNode
|
||||||
},
|
},
|
||||||
// 3. 路径规划主函数(经纬度坐标输入) - 支持途经点 + 避让点/区域
|
// 3. 路径规划主函数(经纬度坐标输入) - 支持途经点 + 避让点/区域
|
||||||
async planRoute(
|
async planRoute(startCoord, endCoord, viaPoints = [], avoidObstacles = null) {
|
||||||
startCoord,
|
|
||||||
endCoord,
|
|
||||||
viaPoints = [],
|
|
||||||
avoidObstacles = null
|
|
||||||
) {
|
|
||||||
const {graph, nodeCoords} = this.roadNetworkGeoJSONBuild
|
const {graph, nodeCoords} = this.roadNetworkGeoJSONBuild
|
||||||
|
|
||||||
// 按顺序组合点:起点 -> 途经点 -> 终点
|
// 按顺序组合点:起点 -> 途经点 -> 终点
|
||||||
const points = [startCoord];
|
const points = [startCoord]
|
||||||
if (viaPoints && viaPoints.length > 0) {
|
if (viaPoints && viaPoints.length > 0) {
|
||||||
viaPoints.forEach((viaPoint) => {
|
viaPoints.forEach((viaPoint) => {
|
||||||
points.push(viaPoint.geometry.coordinates);
|
points.push(viaPoint.geometry.coordinates)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
points.push(endCoord);
|
points.push(endCoord)
|
||||||
const fullPath = []
|
const fullPath = []
|
||||||
const infoList = []
|
const infoList = []
|
||||||
for (let i = 0; i < points.length - 1; i++) {
|
for (let i = 0; i < points.length - 1; i++) {
|
||||||
@ -1457,8 +1382,7 @@ export default {
|
|||||||
for (const [node, coord] of Object.entries(nodeCoords)) {
|
for (const [node, coord] of Object.entries(nodeCoords)) {
|
||||||
const pt = turf.point(coord)
|
const pt = turf.point(coord)
|
||||||
avoidObstacles.features.forEach((ob) => {
|
avoidObstacles.features.forEach((ob) => {
|
||||||
if (turf.booleanPointInPolygon(pt.geometry, ob.geometry))
|
if (turf.booleanPointInPolygon(pt.geometry, ob.geometry)) obstacleNodes.push(node)
|
||||||
obstacleNodes.push(node)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
obstacleNodes.forEach((node) => delete tempGraph[node])
|
obstacleNodes.forEach((node) => delete tempGraph[node])
|
||||||
@ -1471,10 +1395,7 @@ export default {
|
|||||||
delete tempGraph[node][targetNode]
|
delete tempGraph[node][targetNode]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const line = turf.lineString([
|
const line = turf.lineString([nodeCoords[node], nodeCoords[targetNode]])
|
||||||
nodeCoords[node],
|
|
||||||
nodeCoords[targetNode],
|
|
||||||
])
|
|
||||||
avoidObstacles.features.forEach((area) => {
|
avoidObstacles.features.forEach((area) => {
|
||||||
if (turf.booleanCrosses(line, area)) {
|
if (turf.booleanCrosses(line, area)) {
|
||||||
delete tempGraph[node][targetNode]
|
delete tempGraph[node][targetNode]
|
||||||
@ -1500,18 +1421,14 @@ export default {
|
|||||||
if (!this.join) {
|
if (!this.join) {
|
||||||
segment = this.roadNetworkGeoJSON.features.find(
|
segment = this.roadNetworkGeoJSON.features.find(
|
||||||
(f) =>
|
(f) =>
|
||||||
(f.properties.FNODE_ == currentNode &&
|
(f.properties.FNODE_ == currentNode && f.properties.TNODE_ == nextNode) ||
|
||||||
f.properties.TNODE_ == nextNode) ||
|
(f.properties.FNODE_ == nextNode && f.properties.TNODE_ == currentNode)
|
||||||
(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.FNODE_ == currentNode && f.properties.TNODE_ == nextNode) ||
|
||||||
f.properties.TNODE_ == nextNode) ||
|
(f.properties.FNODE_ == nextNode && f.properties.TNODE_ == currentNode)) &&
|
||||||
(f.properties.FNODE_ == nextNode &&
|
|
||||||
f.properties.TNODE_ == currentNode) ) &&
|
|
||||||
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
|
||||||
@ -1534,20 +1451,13 @@ export default {
|
|||||||
this.shortestPathLayer.clear()
|
this.shortestPathLayer.clear()
|
||||||
this.shortestPathList = []
|
this.shortestPathList = []
|
||||||
this.infoList = []
|
this.infoList = []
|
||||||
const startPoint = turf.point(
|
const startPoint = turf.point(this.pointQD.toGeoJSON().geometry.coordinates).geometry.coordinates // 起点
|
||||||
this.pointQD.toGeoJSON().geometry.coordinates
|
const endPoint = turf.point(this.pointZD.toGeoJSON().geometry.coordinates).geometry.coordinates // 终点
|
||||||
).geometry.coordinates // 起点
|
|
||||||
const endPoint = turf.point(this.pointZD.toGeoJSON().geometry.coordinates)
|
|
||||||
.geometry.coordinates // 终点
|
|
||||||
// 途经点
|
// 途经点
|
||||||
const viaPointsGeoJSON =
|
const viaPointsGeoJSON = this.viaPoints.map((point) => point.toGeoJSON()) || []
|
||||||
this.viaPoints.map((point) => point.toGeoJSON()) || []
|
const viaPointsTurf = viaPointsGeoJSON.map((p) => turf.point(p.geometry.coordinates))
|
||||||
const viaPointsTurf = viaPointsGeoJSON.map((p) =>
|
|
||||||
turf.point(p.geometry.coordinates)
|
|
||||||
)
|
|
||||||
// 避让点
|
// 避让点
|
||||||
const avoidPointsGeoJSON =
|
const avoidPointsGeoJSON = this.avoidPoints.map((point) => point.toGeoJSON()) || []
|
||||||
this.avoidPoints.map((point) => point.toGeoJSON()) || []
|
|
||||||
const avoidPointsPolygons = avoidPointsGeoJSON.map((point) => {
|
const avoidPointsPolygons = avoidPointsGeoJSON.map((point) => {
|
||||||
return turf.circle(
|
return turf.circle(
|
||||||
turf.point(point.geometry.coordinates),
|
turf.point(point.geometry.coordinates),
|
||||||
@ -1556,18 +1466,9 @@ export default {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
// 避让区域
|
// 避让区域
|
||||||
const avoidAreasGeoJSON =
|
const avoidAreasGeoJSON = this.avoidAreas.map((area) => area.toGeoJSON({closure: true})) || []
|
||||||
this.avoidAreas.map((area) => area.toGeoJSON({closure: true})) || []
|
const obstaclesGeoJSON = turf.featureCollection([...avoidPointsPolygons, ...avoidAreasGeoJSON])
|
||||||
const obstaclesGeoJSON = turf.featureCollection([
|
const route = await this.planRoute(startPoint, endPoint, viaPointsTurf, obstaclesGeoJSON)
|
||||||
...avoidPointsPolygons,
|
|
||||||
...avoidAreasGeoJSON,
|
|
||||||
])
|
|
||||||
const route = await this.planRoute(
|
|
||||||
startPoint,
|
|
||||||
endPoint,
|
|
||||||
viaPointsTurf,
|
|
||||||
obstaclesGeoJSON
|
|
||||||
)
|
|
||||||
this.drawPath(route)
|
this.drawPath(route)
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1588,9 +1489,7 @@ export default {
|
|||||||
this.infoList = path.infoList.map((item) => item.properties)
|
this.infoList = path.infoList.map((item) => item.properties)
|
||||||
},
|
},
|
||||||
/** 隐蔽规划 */
|
/** 隐蔽规划 */
|
||||||
concealed() {
|
concealed() {},
|
||||||
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -1721,6 +1620,4 @@ export default {
|
|||||||
.popDiloag .popDiloag-title {
|
.popDiloag .popDiloag-title {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.popDiloag .popDiloag-p {
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -2,21 +2,11 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="home-header">
|
<div class="home-header">
|
||||||
<div class="home-header-left">
|
<div class="home-header-left">
|
||||||
<el-tooltip content="起点绘制">
|
|
||||||
<img @click="drawStartPoint" src="@/assets/image/start.png" />
|
<img @click="drawStartPoint" src="@/assets/image/start.png" />
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip content="终点绘制">
|
|
||||||
<img @click="drawEndPoint" src="@/assets/image/end.png" />
|
<img @click="drawEndPoint" src="@/assets/image/end.png" />
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip content="途径点绘制">
|
|
||||||
<img @click="drawViaPoint" src="@/assets/image/add.png" />
|
<img @click="drawViaPoint" src="@/assets/image/add.png" />
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip content="避让点绘制">
|
|
||||||
<img @click="drawAvoidPoint" src="@/assets/image/avoidP.png" />
|
<img @click="drawAvoidPoint" src="@/assets/image/avoidP.png" />
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip content="避让区域绘制">
|
|
||||||
<img @click="drawAvoidArea" src="@/assets/image/updown.png" />
|
<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>
|
||||||
@ -42,11 +32,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-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')"
|
||||||
@ -54,7 +45,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')"
|
||||||
@ -62,7 +53,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"
|
||||||
@ -73,7 +64,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"
|
||||||
@ -84,7 +75,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"
|
||||||
@ -107,13 +98,14 @@
|
|||||||
<div class="control-panel">
|
<div class="control-panel">
|
||||||
<div class="title">隐蔽添加</div>
|
<div class="title">隐蔽添加</div>
|
||||||
<el-form
|
<el-form
|
||||||
|
label-width="120px"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
size="mini"
|
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>
|
||||||
@ -128,16 +120,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-form
|
<el-form
|
||||||
@submit.native.prevent="calculateShortestPath"
|
@submit.native.prevent="calculateShortestPath"
|
||||||
|
label-width="120px"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
size="mini"
|
size="mini"
|
||||||
>
|
>
|
||||||
<el-form-item label="宽度(m):">
|
<el-form-item label="宽度">
|
||||||
<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="最小转弯半径(m):">
|
<el-form-item label="最小转弯半径">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="inputform.minTurnRadius"
|
v-model="inputform.minTurnRadius"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
@ -484,9 +477,6 @@ export default {
|
|||||||
pitch: -35,
|
pitch: -35,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultLabelStyle: {
|
|
||||||
font: '16px "DengXian",sans-serif',
|
|
||||||
}
|
|
||||||
// basemaps: [
|
// basemaps: [
|
||||||
// {
|
// {
|
||||||
// id: "image-tdss",
|
// id: "image-tdss",
|
||||||
@ -511,7 +501,7 @@ export default {
|
|||||||
this.viewer.addLayer(this.accordFactoryLayer)
|
this.viewer.addLayer(this.accordFactoryLayer)
|
||||||
this.loadShapefile() // 拿到路网数据
|
this.loadShapefile() // 拿到路网数据
|
||||||
// 添加地图点击事件监听,用于结束绘制
|
// 添加地图点击事件监听,用于结束绘制
|
||||||
this.viewer.on('rightClick', (event) => {
|
this.viewer.on(mars3d.EventType.dblClick, (event) => {
|
||||||
// 如果正在绘制,点击地图可以结束绘制(除了绘制点)
|
// 如果正在绘制,点击地图可以结束绘制(除了绘制点)
|
||||||
this.graphicLayer.stopDraw()
|
this.graphicLayer.stopDraw()
|
||||||
})
|
})
|
||||||
@ -615,7 +605,7 @@ export default {
|
|||||||
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: currentColor, // 使用当前颜色
|
||||||
width: 2,
|
width: 2,
|
||||||
outline: false,
|
outline: false,
|
||||||
},
|
},
|
||||||
@ -1089,7 +1079,7 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.$message.success(`保存在${response.data.path}成功`)
|
this.$message.success('保存成功')
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
});
|
});
|
||||||
@ -1288,16 +1278,10 @@ export default {
|
|||||||
(viaPoint) => viaPoint.style.time !== row.time
|
(viaPoint) => viaPoint.style.time !== row.time
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (this.viaPoints.length > 0) {
|
|
||||||
const graphic = this.viaPoints.find(
|
const graphic = this.viaPoints.find(
|
||||||
(viaPoint) => viaPoint.style.time === row.time
|
(viaPoint) => viaPoint.style.time === row.time
|
||||||
)
|
)
|
||||||
this.updatePointPosition(graphic, row.points)
|
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) {
|
||||||
@ -1320,16 +1304,10 @@ export default {
|
|||||||
(avoidPoint) => avoidPoint.style.time !== row.time
|
(avoidPoint) => avoidPoint.style.time !== row.time
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (this.avoidPoints.length > 0) {
|
|
||||||
const graphic = this.avoidPoints.find(
|
const graphic = this.avoidPoints.find(
|
||||||
(avoidPoint) => avoidPoint.style.time === row.time
|
(avoidPoint) => avoidPoint.style.time === row.time
|
||||||
)
|
)
|
||||||
this.updatePointPosition(graphic, row.points)
|
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) {
|
||||||
@ -1352,16 +1330,10 @@ export default {
|
|||||||
(avoidArea) => avoidArea.style.time !== row.time
|
(avoidArea) => avoidArea.style.time !== row.time
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (this.avoidAreas.length > 0) {
|
|
||||||
const graphic = this.avoidAreas.find(
|
const graphic = this.avoidAreas.find(
|
||||||
(avoidArea) => avoidArea.style.time === row.time
|
(avoidArea) => avoidArea.style.time === row.time
|
||||||
)
|
)
|
||||||
this.updatePolygonPosition(graphic, row.points)
|
this.updatePolygonPosition(graphic, row.points)
|
||||||
} else {
|
|
||||||
const time = new Date().getTime()
|
|
||||||
row.time = time
|
|
||||||
this.addPolygonToMap('avoidAreas', row, time)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1657,6 +1629,7 @@ export default {
|
|||||||
|
|
||||||
const fullPath = []
|
const fullPath = []
|
||||||
const infoList = []
|
const infoList = []
|
||||||
|
|
||||||
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]; // 下一个点(途经点/终点)
|
||||||
@ -1668,7 +1641,7 @@ export default {
|
|||||||
let endEdgeInfo = this.findNearestEdge(segmentEnd, tempGraph, nodeCoords);
|
let endEdgeInfo = this.findNearestEdge(segmentEnd, tempGraph, nodeCoords);
|
||||||
|
|
||||||
if (!startEdgeInfo || !endEdgeInfo) {
|
if (!startEdgeInfo || !endEdgeInfo) {
|
||||||
console.error("无法匹配到路网边")
|
console.error('无法匹配到路网边')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// 计算垂直连接点
|
// 计算垂直连接点
|
||||||
@ -1878,22 +1851,10 @@ export default {
|
|||||||
) {
|
) {
|
||||||
const segmentPath = [];
|
const segmentPath = [];
|
||||||
const segments = [];
|
const segments = [];
|
||||||
let hasValidSegment = false;
|
|
||||||
// 新增:存储途经点垂直点之后的路网后半截(供后续路径延续)
|
|
||||||
let viaPerpRemainingCoords = [];
|
|
||||||
|
|
||||||
// 1. 起点 -> 垂足(直线)
|
// 1. 起点 -> 垂足(直线)
|
||||||
if (startConnection.type !== "via") {
|
|
||||||
// 起点
|
|
||||||
segmentPath.push(segmentStart);
|
segmentPath.push(segmentStart);
|
||||||
segmentPath.push(startConnection.perpendicularPoint);
|
segmentPath.push(startConnection.perpendicularPoint);
|
||||||
// return { path: [], segments: [] };
|
|
||||||
} else {
|
|
||||||
// 途径点
|
|
||||||
segmentPath.push(segmentStart);
|
|
||||||
// segmentPath.push(startConnection.perpendicularPoint);
|
|
||||||
// return { path: [], segments: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 去掉 temp 节点,保留路网节点序列(但我们需要完整 pathNodes 来判断是否直接由 temp -> temp)
|
// 2. 去掉 temp 节点,保留路网节点序列(但我们需要完整 pathNodes 来判断是否直接由 temp -> temp)
|
||||||
// 但在拼接线段时我们会遍历 pathNodes 的相邻 pair(包含真实节点)
|
// 但在拼接线段时我们会遍历 pathNodes 的相邻 pair(包含真实节点)
|
||||||
@ -1911,31 +1872,16 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// curNode 与 nextNode 都是真实节点:找到你路网 feature(可能有多段匹配,取第一个匹配)并决定方向
|
// curNode 与 nextNode 都是真实节点:找到你路网 feature(可能有多段匹配,取第一个匹配)并决定方向
|
||||||
let segment = null;
|
const segment = this.roadNetworkGeoJSON.features.find(f =>
|
||||||
if (!this.join) {
|
|
||||||
segment = this.roadNetworkGeoJSON.features.find(f =>
|
|
||||||
(String(f.properties.FNODE_) === String(curNode) && String(f.properties.TNODE_) === String(nextNode)) ||
|
(String(f.properties.FNODE_) === String(curNode) && String(f.properties.TNODE_) === String(nextNode)) ||
|
||||||
(String(f.properties.FNODE_) === String(nextNode) && String(f.properties.TNODE_) === String(curNode))
|
(String(f.properties.FNODE_) === String(nextNode) && String(f.properties.TNODE_) === String(curNode))
|
||||||
);
|
);
|
||||||
} else{
|
|
||||||
segment = this.roadNetworkGeoJSON.features.find(
|
|
||||||
(f) =>
|
|
||||||
((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) {
|
if (!segment) {
|
||||||
console.warn(`找不到符合条件的路段: ${curNode} -> ${nextNode}`);
|
|
||||||
// 若找不到对应路段(可能被避让删掉或数据不一致),则跳过
|
// 若找不到对应路段(可能被避让删掉或数据不一致),则跳过
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment) {
|
|
||||||
hasValidSegment = true;
|
|
||||||
segments.push(segment);
|
segments.push(segment);
|
||||||
// 取线数组(LineString 或 MultiLine 的第一条)
|
// 取线数组(LineString 或 MultiLine 的第一条)
|
||||||
let segCoords = [];
|
let segCoords = [];
|
||||||
@ -1949,47 +1895,18 @@ export default {
|
|||||||
const isForward = String(segment.properties.FNODE_) === String(curNode);
|
const isForward = String(segment.properties.FNODE_) === String(curNode);
|
||||||
const coordsToUse = isForward ? segCoords : [...segCoords].reverse();
|
const coordsToUse = isForward ? segCoords : [...segCoords].reverse();
|
||||||
|
|
||||||
// === 使用第一个版本的精细连接逻辑,但加入第二个版本的途经点切片 ===
|
// 拼接:如果 segmentPath 的最后点与 coordsToUse 的第一个点相同(或非常接近),只接后续;否则需要从最接近的点开始接
|
||||||
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 distLastToFirst = this.calculateDistance(lastPt, firstOfSeg);
|
||||||
|
|
||||||
// 如果是途经点,需要找到终点垂足在当前位置
|
|
||||||
const endPerpNearest = this.findNearestPointWithIndex(coordsToUse, endConnection.perpendicularPoint);
|
|
||||||
const endNi = endPerpNearest.index;
|
|
||||||
|
|
||||||
if (distLastToFirst < 1e-6) {
|
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));
|
segmentPath.push(...coordsToUse.slice(1));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// 找到 coordsToUse 上与 lastPt 最近的索引
|
// 找到 coordsToUse 上与 lastPt 最近的索引
|
||||||
const nearestInfo = this.findNearestPointWithIndex(coordsToUse, lastPt);
|
const nearestInfo = this.findNearestPointWithIndex(coordsToUse, lastPt);
|
||||||
const ni = nearestInfo.index;
|
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) {
|
if (ni === 0) {
|
||||||
// 从头开始接(直接接)
|
// 从头开始接(直接接)
|
||||||
segmentPath.push(...coordsToUse);
|
segmentPath.push(...coordsToUse);
|
||||||
@ -2028,32 +1945,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果没有任何有效的路段,返回空路径
|
|
||||||
if (!hasValidSegment) {
|
|
||||||
this.$message.warning('整段路径中没有找到任何符合条件的路段');
|
|
||||||
return { path: [], segments: [] };
|
|
||||||
}
|
|
||||||
// 3. 最后加上终点垂足 -> 终点 的直线
|
// 3. 最后加上终点垂足 -> 终点 的直线
|
||||||
|
|
||||||
// === 处理终点和途径点的垂足连线 ===
|
// === 处理终点和途径点的垂足连线 ===
|
||||||
// 确保终点/途径点垂足处理
|
if (endConnection.type === 'end' || endConnection.type === 'via') {
|
||||||
if (endConnection.type === 'end') {
|
// ✅ 确保垂足连线来自最近的路网(endConnection.lineCoords)
|
||||||
const endLineCoords = endConnection.lineCoords;
|
const endLineCoords = endConnection.lineCoords;
|
||||||
const lastPt = segmentPath[segmentPath.length - 1];
|
const nearestIdx = this.findNearestIndexOnLine(endLineCoords, endConnection.perpendicularPoint).index;
|
||||||
|
const subLine = endLineCoords.slice(0, nearestIdx + 1); // 只取垂足前的部分
|
||||||
// 找到路径末尾点在线段上最近的点索引
|
|
||||||
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);
|
this.connectSegmentInternally(segmentPath, subLine);
|
||||||
@ -2061,24 +1961,12 @@ export default {
|
|||||||
// 最后垂足 -> 点
|
// 最后垂足 -> 点
|
||||||
segmentPath.push(endConnection.perpendicularPoint);
|
segmentPath.push(endConnection.perpendicularPoint);
|
||||||
segmentPath.push(segmentEnd);
|
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 {
|
} else {
|
||||||
// 默认逻辑
|
// 默认逻辑(起点)
|
||||||
segmentPath.push(endConnection.perpendicularPoint);
|
segmentPath.push(endConnection.perpendicularPoint);
|
||||||
segmentPath.push(segmentEnd);
|
segmentPath.push(segmentEnd);
|
||||||
}
|
}
|
||||||
if (startConnection.type == 'via' && segmentPath.length > 0) {
|
|
||||||
segmentPath.shift(); // 移除数组第一个元素
|
|
||||||
}
|
|
||||||
return { path: segmentPath, segments };
|
return { path: segmentPath, segments };
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2223,6 +2111,23 @@ 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
|
||||||
@ -2530,7 +2435,7 @@ export default {
|
|||||||
.then(response => {
|
.then(response => {
|
||||||
this.jsonInfo = {
|
this.jsonInfo = {
|
||||||
path: response.data.path,
|
path: response.data.path,
|
||||||
json: JSON.stringify(this.decodeEscapedJson(response.data.json), null, 2)
|
json: JSON.stringify(response.data.json, null, 2)
|
||||||
}
|
}
|
||||||
this.jsonLoading = false
|
this.jsonLoading = false
|
||||||
})
|
})
|
||||||
File diff suppressed because it is too large
Load Diff
@ -21,35 +21,25 @@ module.exports = defineConfig({
|
|||||||
})
|
})
|
||||||
|
|
||||||
/* 2. 追加 ES5 规则(只转 src,不转 node_modules) */
|
/* 2. 追加 ES5 规则(只转 src,不转 node_modules) */
|
||||||
// config.module.rules.push({
|
config.module.rules.push({
|
||||||
// test: /\.js$/,
|
test: /\.js$/,
|
||||||
// include: path.resolve(__dirname, 'src'),
|
include: path.resolve(__dirname, 'src'),
|
||||||
// use: {
|
use: {
|
||||||
// loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
// options: {
|
options: {
|
||||||
// presets: [
|
presets: [
|
||||||
// [
|
[
|
||||||
// '@babel/preset-env',
|
'@babel/preset-env',
|
||||||
// {
|
{
|
||||||
// targets: {ie: '11'}, // 强制 ES5
|
targets: {ie: '11'}, // 强制 ES5
|
||||||
// corejs: 3,
|
corejs: 3,
|
||||||
// useBuiltIns: 'entry',
|
useBuiltIns: 'entry',
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
},
|
},
|
||||||
devServer: {
|
],
|
||||||
client: { overlay: false },
|
],
|
||||||
proxy: {
|
|
||||||
'/api': {
|
|
||||||
target: 'http://192.168.3.35:8381',
|
|
||||||
changeOrigin: true,
|
|
||||||
pathRewrite: { '^/api': '' },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
css: {
|
css: {
|
||||||
|
|||||||
Reference in New Issue
Block a user