Compare commits

..

10 Commits

18 changed files with 2320 additions and 2041 deletions

View File

@ -4,5 +4,6 @@
"semi": false, "semi": false,
"singleQuote": true, "singleQuote": true,
"trailingComma": "es5", "trailingComma": "es5",
"bracketSpacing": false "bracketSpacing": false,
"printWidth": 120
} }

BIN
dist.zip Normal file

Binary file not shown.

421
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

@ -1,6 +1,6 @@
[http] [http]
port=8381 port=8083
address=192.168.3.35 address=127.0.0.1
[title] [title]
msg=道路堪选分析 msg=道路堪选分析

View File

@ -1,4 +0,0 @@
window.config = {
// baseUrl: 'http://192.168.3.35:8381',
baseUrl: '/api'
}

View File

@ -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>

View File

@ -17,7 +17,7 @@ export default {
return { return {
routerMap: { routerMap: {
1: '/', 1: '/',
2: '/residentAnalysis', // 2: '/residentAnalysis ',
}, },
} }
}, },

View File

@ -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 *,

View File

@ -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

View File

@ -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,
}, },

View File

@ -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,
}

View File

@ -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

View File

@ -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,23 +148,23 @@
<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}">
{{ row.area.toFixed(2)}} {{ row.area.toFixed(2) }}
</template> </template>
</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
@ -197,37 +180,37 @@
> >
<vxe-column type="checkbox" width="50"></vxe-column> <vxe-column type="checkbox" width="50"></vxe-column>
<vxe-column field="name" title="名称" width="100"> <vxe-column field="name" title="名称" width="100">
<template v-slot="{ row }"> <template v-slot="{row}">
<el-input v-if="row.editing" v-model="row.name" size="mini"></el-input> <el-input v-if="row.editing" v-model="row.name" size="mini"></el-input>
<span v-else>{{ row.name }}</span> <span v-else>{{ row.name }}</span>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="long" title="长度" width="100"> <vxe-column field="long" title="长度" width="100">
<template v-slot="{ row }"> <template v-slot="{row}">
<el-input v-if="row.editing" v-model="row.long" size="mini"></el-input> <el-input v-if="row.editing" v-model="row.long" size="mini"></el-input>
<span v-else>{{ row.long }}</span> <span v-else>{{ row.long }}</span>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="width" title="宽度" width="100"> <vxe-column field="width" title="宽度" width="100">
<template v-slot="{ row }"> <template v-slot="{row}">
<el-input v-if="row.editing" v-model="row.width" size="mini"></el-input> <el-input v-if="row.editing" v-model="row.width" size="mini"></el-input>
<span v-else>{{ row.width }}</span> <span v-else>{{ row.width }}</span>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="load" title="载重(吨)" width="100"> <vxe-column field="load" title="载重(吨)" width="100">
<template v-slot="{ row }"> <template v-slot="{row}">
<el-input v-if="row.editing" v-model="row.load" size="mini"></el-input> <el-input v-if="row.editing" v-model="row.load" size="mini"></el-input>
<span v-else>{{ row.load }}</span> <span v-else>{{ row.load }}</span>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="minTurnRadius" title="最小转弯半径"> <vxe-column field="minTurnRadius" title="最小转弯半径">
<template v-slot="{ row }"> <template v-slot="{row}">
<el-input v-if="row.editing" v-model="row.minTurnRadius" size="mini"></el-input> <el-input v-if="row.editing" v-model="row.minTurnRadius" size="mini"></el-input>
<span v-else>{{ row.minTurnRadius }}</span> <span v-else>{{ row.minTurnRadius }}</span>
</template> </template>
</vxe-column> </vxe-column>
<vxe-column title="操作" width="100"> <vxe-column title="操作" width="100">
<template v-slot="{ row }"> <template v-slot="{row}">
<el-button v-if="!row.editing" type="text" size="mini" @click="handleEdit(row)">编辑</el-button> <el-button v-if="!row.editing" type="text" size="mini" @click="handleEdit(row)">编辑</el-button>
<el-button v-else type="text" size="mini" @click="handleSave(row)">保存</el-button> <el-button v-else type="text" size="mini" @click="handleSave(row)">保存</el-button>
<el-button type="text" size="mini" @click="handleDelete(row)">删除</el-button> <el-button type="text" size="mini" @click="handleDelete(row)">删除</el-button>
@ -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',
{ {
@ -356,7 +339,7 @@ export default {
this.shortestPathLayer = new window.mars3d.layer.GraphicLayer() this.shortestPathLayer = new window.mars3d.layer.GraphicLayer()
this.viewer.addLayer(this.shortestPathLayer) this.viewer.addLayer(this.shortestPathLayer)
this.accordFactoryLayer = new window.mars3d.layer.GraphicLayer() this.accordFactoryLayer = new window.mars3d.layer.GraphicLayer()
this.viewer.addLayer(this.accordFactoryLayer) this.viewer.addLayer(this.accordFactoryLayer)
this.loadShapefile() // 拿到路网数据 this.loadShapefile() // 拿到路网数据
@ -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,19 +512,19 @@ 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)
} }
}, },
}) })
}, },
// 获取当前路线的缓冲区 // 获取当前路线的缓冲区
hadBuffer () { hadBuffer() {
if (this.shortestPathList.length == 0) { if (this.shortestPathList.length == 0) {
this.$message.warning('请先进行路线规划') this.$message.warning('请先进行路线规划')
return return
@ -551,34 +534,34 @@ export default {
return return
} }
try { try {
// =======检查几何对象的类型 缓冲区======== // =======检查几何对象的类型 缓冲区========
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) => {
// 创建一个多边形图形 // 创建一个多边形图形
@ -740,35 +723,39 @@ export default {
outline: true, outline: true,
outlineWidth: 1, outlineWidth: 1,
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:
? area * (100 - this.hideform.redundancy) * 0.01 this.hideform.redundancy && this.hideform.redundancy != 0 && area > 0
: area > 0 ? area * (100 - this.hideform.redundancy) * 0.01
? area : area > 0
: 0, // 如果 area 不是有效值,设置为 0 或其他默认值 ? area
: 0, // 如果 area 不是有效值,设置为 0 或其他默认值
}) })
}) })
}, },
// 显示面积信息弹框的方法 // 显示面积信息弹框的方法
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,29 +806,29 @@ 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() {
this.multipleSelection = [] this.multipleSelection = []
await fetch('./data/minTurnRadius.json') await fetch('./data/minTurnRadius.json')
.then((response) => { .then((response) => {
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'))
this.multipleSelection.forEach((item) => { this.multipleSelection.forEach((item) => {
this.$refs.vxeTable.setCheckboxRowKey(item.id, true) this.$refs.vxeTable.setCheckboxRowKey(item.id, true)
})
}) })
}) })
}) .catch((error) => {})
.catch((error) => {})
}, },
handleSelectionChange({records}) { handleSelectionChange({records}) {
this.multipleSelection = records this.multipleSelection = records
@ -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
headers: { .post(
'Authorization': 'Bearer your_token_here', `http://${parsedData.http.address}:${parsedData.http.port}/api/equpment`,
'Content-Type': 'application/json' JSON.stringify(this.tableData),
} {
}) headers: {
.then(response => { Authorization: 'Bearer your_token_here',
this.$message.success('保存成功') 'Content-Type': 'application/json',
}) },
.catch(error => { }
}); )
.then((response) => {
this.$message.success('保存成功')
})
.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,21 +1421,17 @@ 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.载重吨 >= this.inputform.load &&
f.properties.TNODE_ == currentNode) ) && f.properties.宽度 >= this.inputform.width &&
f.properties.载重吨 >= this.inputform.load && f.properties.曲率半 <= this.inputform.minTurnRadius
f.properties.宽度 >= this.inputform.width &&
f.properties.曲率半 <= this.inputform.minTurnRadius
) )
} }
if (segment) { if (segment) {
@ -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>
@ -1664,14 +1563,14 @@ export default {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.control-panel .title .joinCheck { .control-panel .title .joinCheck {
font-size: 14px; font-size: 14px;
color: #555; color: #555;
} }
.el-checkbox { .el-checkbox {
margin-right: 5px!important; margin-right: 5px !important;
} }
.importJson { .importJson {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -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

View File

@ -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" /> <img @click="drawEndPoint" src="@/assets/image/end.png" />
</el-tooltip> <img @click="drawViaPoint" src="@/assets/image/add.png" />
<el-tooltip content="终点绘制"> <img @click="drawAvoidPoint" src="@/assets/image/avoidP.png" />
<img @click="drawEndPoint" src="@/assets/image/end.png" /> <img @click="drawAvoidArea" src="@/assets/image/updown.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>
@ -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(startConnection.perpendicularPoint);
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 // 2. temp pathNodes temp -> temp
// 线 pathNodes pair // 线 pathNodes pair
@ -1911,174 +1872,101 @@ export default {
} }
// curNode nextNode feature // curNode nextNode feature
let segment = null; const segment = this.roadNetworkGeoJSON.features.find(f =>
if (!this.join) { (String(f.properties.FNODE_) === String(curNode) && String(f.properties.TNODE_) === String(nextNode)) ||
segment = this.roadNetworkGeoJSON.features.find(f => (String(f.properties.FNODE_) === String(nextNode) && String(f.properties.TNODE_) === String(curNode))
(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{
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) { segments.push(segment);
hasValidSegment = true; // 线LineString MultiLine
segments.push(segment); let segCoords = [];
// 线LineString MultiLine if (segment.geometry.type === 'LineString') {
let segCoords = []; segCoords = segment.geometry.coordinates;
if (segment.geometry.type === 'LineString') { } else if (segment.geometry.type === 'MultiLineString') {
segCoords = segment.geometry.coordinates; segCoords = segment.geometry.coordinates[0];
} else if (segment.geometry.type === 'MultiLineString') { }
segCoords = segment.geometry.coordinates[0];
}
// FNODE_ === curNode线 segCoords // FNODE_ === curNode线 segCoords
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);
if (distLastToFirst < 1e-6) {
// //
const endPerpNearest = this.findNearestPointWithIndex(coordsToUse, endConnection.perpendicularPoint); segmentPath.push(...coordsToUse.slice(1));
const endNi = endPerpNearest.index; } else {
// coordsToUse lastPt
if (distLastToFirst < 1e-6) { const nearestInfo = this.findNearestPointWithIndex(coordsToUse, lastPt);
// const ni = nearestInfo.index;
// if (ni === 0) {
if (endConnection.type === 'via') { //
segmentPath.push(...coordsToUse.slice(1, endNi + 1)); segmentPath.push(...coordsToUse);
// } else if (ni === coordsToUse.length - 1) {
viaPerpRemainingCoords = coordsToUse.slice(endNi); //
const rev = [...coordsToUse].reverse();
// rev
if (this.calculateDistance(lastPt, rev[0]) < 1e-6) {
segmentPath.push(...rev.slice(1));
} else { } else {
segmentPath.push(...coordsToUse.slice(1)); //
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 { } else {
// coordsToUse lastPt //
const nearestInfo = this.findNearestPointWithIndex(coordsToUse, lastPt); segmentPath.push(coordsToUse[ni]);
const ni = nearestInfo.index; const distToStart = this.calculateDistance(coordsToUse[ni], coordsToUse[0]);
const distToEnd = this.calculateDistance(coordsToUse[ni], coordsToUse[coordsToUse.length - 1]);
// === === if (distToStart <= distToEnd) {
if (endConnection.type === 'via') { const toStart = coordsToUse.slice(0, ni).reverse();
// segmentPath.push(...toStart);
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 { } else {
// 使 const toEnd = coordsToUse.slice(ni + 1);
if (ni === 0) { segmentPath.push(...toEnd);
//
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. -> 线 // 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);
// -> // ->
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

View File

@ -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: {