Compare commits
17 Commits
3e7ccea682
...
pathPlan
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e606513f5 | |||
| 1c76c7d23c | |||
| 802df4d446 | |||
| 46c6b952fc | |||
| a562e2cea8 | |||
| af252cd65e | |||
| 444bded88f | |||
| 964ce7cede | |||
| ce70fd98ec | |||
| 7d22506f18 | |||
| 328700ec89 | |||
| 2ad6bc1b47 | |||
| 41f9034f54 | |||
| ae2f9d94e1 | |||
| c1876d23bd | |||
| e2b73640df | |||
| f084fc92e9 |
28
README.md
28
README.md
@ -1,29 +1,3 @@
|
|||||||
# kxfx
|
# kxfx
|
||||||
|
|
||||||
## Project setup
|
## 机动路线规划
|
||||||
|
|
||||||
```
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run serve
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiles and minifies for production
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Lints and fixes files
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run lint
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customize configuration
|
|
||||||
|
|
||||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
|
||||||
|
|||||||
11
package.json
11
package.json
@ -9,12 +9,15 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "0.21.0",
|
"@babel/preset-env": "^7.28.3",
|
||||||
"core-js": "^3.8.3",
|
"axios": "^1.13.2",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"element-ui": "2.9.2",
|
"element-ui": "2.9.2",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"gsap": "^3.13.0",
|
"gsap": "^3.13.0",
|
||||||
|
"ini-parser": "^0.0.2",
|
||||||
"js-cookie": "2.2.1",
|
"js-cookie": "2.2.1",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
"vue-router": "^3.5.1",
|
"vue-router": "^3.5.1",
|
||||||
"vuex": "^3.6.2",
|
"vuex": "^3.6.2",
|
||||||
@ -22,13 +25,15 @@
|
|||||||
"vxe-table": "~3.18.9"
|
"vxe-table": "~3.18.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.16",
|
"@babel/core": "^7.28.4",
|
||||||
"@babel/eslint-parser": "^7.12.16",
|
"@babel/eslint-parser": "^7.12.16",
|
||||||
"@vue/cli-plugin-babel": "~5.0.0",
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||||
"@vue/cli-plugin-router": "~5.0.0",
|
"@vue/cli-plugin-router": "~5.0.0",
|
||||||
"@vue/cli-plugin-vuex": "~5.0.0",
|
"@vue/cli-plugin-vuex": "~5.0.0",
|
||||||
"@vue/cli-service": "~5.0.0",
|
"@vue/cli-service": "~5.0.0",
|
||||||
|
"babel-loader": "^10.0.0",
|
||||||
|
"core-js": "^3.46.0",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
|||||||
6
public/config.ini
Normal file
6
public/config.ini
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[http]
|
||||||
|
port=8381
|
||||||
|
address=192.168.3.35
|
||||||
|
|
||||||
|
[title]
|
||||||
|
msg=道路堪选分析
|
||||||
4
public/config/config.js
Normal file
4
public/config/config.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
window.config = {
|
||||||
|
// baseUrl: 'http://192.168.3.35:8381',
|
||||||
|
baseUrl: '/api'
|
||||||
|
}
|
||||||
1325
public/config/factory.geojson
Normal file
1325
public/config/factory.geojson
Normal file
File diff suppressed because it is too large
Load Diff
@ -59,7 +59,7 @@
|
|||||||
"id": "image-tdss",
|
"id": "image-tdss",
|
||||||
"name": "影像图",
|
"name": "影像图",
|
||||||
"type": "xyz",
|
"type": "xyz",
|
||||||
"url": "http:/www.tdss.website:280/tiles/img_c/{z}/{x}/{y}",
|
"url": "http://www.tdss.website:280/tiles/img_c/{z}/{x}/{y}",
|
||||||
"crs": "EPSG:4490",
|
"crs": "EPSG:4490",
|
||||||
"show": true
|
"show": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,32 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
"name": "车1",
|
||||||
|
"long": 10,
|
||||||
"width": 7,
|
"width": 7,
|
||||||
"load": 3,
|
"load": 3,
|
||||||
"minTurnRadius": 5
|
"minTurnRadius": 5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
|
"name": "车2",
|
||||||
|
"long": 10,
|
||||||
"width": 11,
|
"width": 11,
|
||||||
"load": 1,
|
"load": 1,
|
||||||
"minTurnRadius": 6
|
"minTurnRadius": 6
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
|
"name": "车3",
|
||||||
|
"long": 10,
|
||||||
"width": 8,
|
"width": 8,
|
||||||
"load": 7,
|
"load": 7,
|
||||||
"minTurnRadius": 10
|
"minTurnRadius": 10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
|
"name": "车4",
|
||||||
|
"long": 10,
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"load": 5,
|
"load": 5,
|
||||||
"minTurnRadius": 2
|
"minTurnRadius": 2
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"startPoint": "114.26344,27.800982",
|
"startPoint": "114.312888,27.796612",
|
||||||
"endPoint": "114.284668,27.794961",
|
"endPoint": "114.336525,27.767989",
|
||||||
"viaPoints": [
|
"viaPoints": [
|
||||||
{
|
{
|
||||||
"time": "1694352000000",
|
"time": "1694352000000",
|
||||||
"points": "114.272329,27.797299"
|
"points": "114.334239,27.779261"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"avoidPoints": [
|
"avoidPoints": [
|
||||||
{
|
{
|
||||||
"time": "1694352003000",
|
"time": "1694352003000",
|
||||||
"points": "114.27882,27.792857"
|
"points": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"avoidAreas": [
|
"avoidAreas": [
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
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>
|
||||||
|
|||||||
BIN
src/assets/scss/Dengxian.ttf
Normal file
BIN
src/assets/scss/Dengxian.ttf
Normal file
Binary file not shown.
@ -1,11 +1,57 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Dengxian';
|
||||||
|
src: url('@/assets/scss/Dengxian.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
/* 全局强制继承 */
|
||||||
|
html, body, #app, .cesium-widget, .mars3d-container,
|
||||||
|
.mars3d-popup, .mars3d-tooltip, .mars3d-contextmenu,
|
||||||
|
.mars3d-measure-result, .cesium-credit-text,
|
||||||
|
.el-button, .el-input, .el-select, .el-table, .el-form, .el-dialog,
|
||||||
|
.el-message, .el-notification, .el-tooltip,
|
||||||
|
.el-menu, .el-breadcrumb, .el-pagination,
|
||||||
|
.el-radio, .el-checkbox, .el-tag, .el-badge,
|
||||||
|
.el-alert, .el-steps, .el-tabs, .el-calendar,
|
||||||
|
.el-date-picker, .el-cascader, .el-transfer,
|
||||||
|
.el-slider, .el-upload,
|
||||||
|
.el-empty, .el-result,
|
||||||
|
.el-loading-text,.el-table,
|
||||||
|
.el-table__body-wrapper span,
|
||||||
|
.el-table__footer-wrapper span,
|
||||||
|
.el-table__header-wrapper span,
|
||||||
|
.el-table__fixed span,
|
||||||
|
.el-table__fixed-right span,
|
||||||
|
.vxe-table--render-default .vxe-cell--title,
|
||||||
|
.vxe-table--render-default .vxe-cell--label,
|
||||||
|
.vxe-toolbar *,
|
||||||
|
.vxe-pager *,
|
||||||
|
.vxe-modal--wrapper *,
|
||||||
|
.vxe-tooltip--wrapper * {
|
||||||
|
font-family: 'DengXian', sans-serif !important;
|
||||||
|
}
|
||||||
|
/* 覆盖 mars3d 所有内部 UI */
|
||||||
|
.mars3d-draw-tooltip,
|
||||||
|
.mars3d-measure-tooltip,
|
||||||
|
.mars3d-contextmenu *,
|
||||||
|
.mars3d-popup *,
|
||||||
|
.mars3d-measure-result *,
|
||||||
|
.mars3d-control-btn {
|
||||||
|
font-family: 'DengXian', sans-serif !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Dengxian {
|
||||||
|
font-family: 'Dengxian' !important;
|
||||||
|
}
|
||||||
|
|
||||||
body,
|
body,
|
||||||
html {
|
html {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
|
font-family: 'Dengxian' !important;
|
||||||
Microsoft YaHei, Arial, sans-serif;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
@ -33,3 +79,20 @@ html {
|
|||||||
.flex-warp {
|
.flex-warp {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
.el-form-item {
|
||||||
|
display: flex!important;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px!important;
|
||||||
|
}
|
||||||
|
.el-form-item__label {
|
||||||
|
letter-spacing: -1px;
|
||||||
|
min-width: 130px!important;
|
||||||
|
padding: 0!important;
|
||||||
|
white-space: nowrap; /* 强制不换行 */
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 16px!important;
|
||||||
|
}
|
||||||
|
.el-input__inner {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
@ -10,6 +10,9 @@ 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
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueRouter from 'vue-router'
|
import VueRouter from 'vue-router'
|
||||||
import HomeView from '../views/home.vue'
|
import HomeView from '../views/home/home.vue'
|
||||||
import residentAnalysis from '@/views/residentAnalysis/index.vue'
|
import residentAnalysis from '@/views/residentAnalysis/index.vue'
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
|
|||||||
162
src/utils/map.js
Normal file
162
src/utils/map.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* 极简 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,18 +16,22 @@
|
|||||||
|
|
||||||
<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: 2, label: '临时部署驻地分析'},
|
||||||
],
|
],
|
||||||
activeIndex: 1,
|
activeIndex: 1,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.getTitle()
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.activeIndex = getStorage('activeIndex') || 1
|
this.activeIndex = getStorage('activeIndex') || 1
|
||||||
},
|
},
|
||||||
@ -36,6 +40,17 @@ 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
|
||||||
@ -64,7 +79,7 @@ $label_height: 50px;
|
|||||||
line-height: $label_height;
|
line-height: $label_height;
|
||||||
padding: 0 30px;
|
padding: 0 30px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
font-family: 'HarmonyOS Sans';
|
// font-family: 'HarmonyOS Sans';
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@ -8,10 +8,12 @@
|
|||||||
<img @click="drawAvoidArea" src="@/assets/image/updown.png" />
|
<img @click="drawAvoidArea" src="@/assets/image/updown.png" />
|
||||||
<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="pointBuffer" class="sure">点隐蔽规划</div>
|
||||||
<!-- <div class="control-panel">
|
<!-- <div class="control-panel">
|
||||||
<button @click="drawStartPoint">绘制起点</button>
|
<button @click="drawStartPoint">绘制起点</button>
|
||||||
<button @click="drawEndPoint">绘制终点</button>
|
<button @click="drawEndPoint">绘制终点</button>
|
||||||
<button @click="drawViaPoint">绘制途径点</button>
|
<button @click="drawViaPoint">绘制途经点</button>
|
||||||
<button @click="drawAvoidPoint">绘制避让点</button>
|
<button @click="drawAvoidPoint">绘制避让点</button>
|
||||||
<button @click="drawAvoidArea">绘制避让区域</button>
|
<button @click="drawAvoidArea">绘制避让区域</button>
|
||||||
<button @click="calculateShortestPath">计算最短路径</button>
|
<button @click="calculateShortestPath">计算最短路径</button>
|
||||||
@ -24,7 +26,7 @@
|
|||||||
<div class="title">参数</div>
|
<div class="title">参数</div>
|
||||||
<el-form
|
<el-form
|
||||||
@submit.native.prevent="calculateShortestPath"
|
@submit.native.prevent="calculateShortestPath"
|
||||||
label-width="100px"
|
label-width="120px"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
size="mini"
|
size="mini"
|
||||||
:model="form"
|
:model="form"
|
||||||
@ -45,7 +47,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"
|
||||||
@ -78,10 +80,6 @@
|
|||||||
></el-input>
|
></el-input>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item>
|
|
||||||
<el-button type="primary" @click="calculateShortestPath">计算最短路径</el-button>
|
|
||||||
<el-button @click="clear">清除所有</el-button>
|
|
||||||
</el-form-item> -->
|
|
||||||
</el-form>
|
</el-form>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
@ -92,10 +90,31 @@
|
|||||||
<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
|
||||||
|
label-width="120px"
|
||||||
|
label-position="left"
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
|
<el-form-item label="缓冲半径(m)">
|
||||||
|
<el-input v-model="hideform.radius"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="面积冗余(%)">
|
||||||
|
<el-input v-model="hideform.redundancy" placeholder=""></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="control-panel">
|
||||||
|
<div class="title">
|
||||||
|
<span>机动属性</span>
|
||||||
|
<div class="joinCheck">
|
||||||
|
<el-checkbox v-model="join"></el-checkbox>
|
||||||
|
<span>参与路线规划</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<el-form
|
<el-form
|
||||||
@submit.native.prevent="calculateShortestPath"
|
@submit.native.prevent="calculateShortestPath"
|
||||||
label-width="100px"
|
label-width="120px"
|
||||||
label-position="left"
|
label-position="left"
|
||||||
size="mini"
|
size="mini"
|
||||||
>
|
>
|
||||||
@ -116,12 +135,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<!-- <div id="mapbox"></div> -->
|
|
||||||
<div class="main-container" style="width: 452px">
|
<div class="main-container" style="width: 452px">
|
||||||
<div class="control-panel" style="width: 452px">
|
<div class="control-panel" style="width: 452px">
|
||||||
<div style="font-size: 14px; margin-bottom: 10px">
|
<div style="font-size: 14px; margin-bottom: 10px">
|
||||||
详细路线:<br />
|
详细路线:<br />
|
||||||
起点({{ form.startPoint }}), 途径点({{
|
起点({{ form.startPoint }}), 途经点({{
|
||||||
form.viaPoints.length > 0 && form.viaPoints[0].points
|
form.viaPoints.length > 0 && form.viaPoints[0].points
|
||||||
? form.viaPoints.map((item) => item.points).join(';')
|
? form.viaPoints.map((item) => item.points).join(';')
|
||||||
: ''
|
: ''
|
||||||
@ -138,7 +156,7 @@
|
|||||||
inputform.minTurnRadius || 0
|
inputform.minTurnRadius || 0
|
||||||
}},最大车辆载重{{ inputform.load || 0 }}吨
|
}},最大车辆载重{{ inputform.load || 0 }}吨
|
||||||
</div>
|
</div>
|
||||||
<vxe-table ref="xTable" :data="infoList">
|
<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>
|
||||||
@ -147,24 +165,79 @@
|
|||||||
<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-column field="options.style.properties.FID_1" title="厂房id"></vxe-column>
|
||||||
|
<vxe-column field="area" title="面积(㎡)">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{ row.area.toFixed(2)}}
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column field="vehicles" title="车辆">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{ (row.vehicles.map(item => item.name)).join(',')}}
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
</vxe-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog :visible.sync="dialogVisible" title="机动属性" width="600px">
|
<el-dialog :visible.sync="dialogVisible" title="车辆选择" width="800px">
|
||||||
|
<div style="margin-bottom: 10px;">
|
||||||
|
<el-button type="primary" size="mini" @click="handleAdd">新增</el-button>
|
||||||
|
</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
height="300px"
|
height="300px"
|
||||||
|
ref="vxeTable"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
:row-config="{isCurrent: true, isHover: true}"
|
:row-config="{isCurrent: true, isHover: true, keyField: 'id'}"
|
||||||
:checkbox-config="{checkField: 'checked', highlight: true}"
|
:checkbox-config="{checkField: 'checked', highlight: true}"
|
||||||
@checkbox-change="handleSelectionChange"
|
@checkbox-change="handleSelectionChange"
|
||||||
|
@checkbox-all="handleSelectionChange"
|
||||||
|
@close="closeSelection"
|
||||||
|
align="center"
|
||||||
>
|
>
|
||||||
<vxe-column type="checkbox" width="50"></vxe-column>
|
<vxe-column type="checkbox" width="50"></vxe-column>
|
||||||
<vxe-column field="width" title="宽度"></vxe-column>
|
<vxe-column field="name" title="名称" width="100">
|
||||||
<vxe-column field="load" title="载重(吨)"></vxe-column>
|
<template v-slot="{ row }">
|
||||||
<vxe-column field="minTurnRadius" title="最小转弯半径"></vxe-column>
|
<el-input v-if="row.editing" v-model="row.name" size="mini"></el-input>
|
||||||
|
<span v-else>{{ row.name }}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column field="long" title="长度" width="100">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-input v-if="row.editing" v-model="row.long" size="mini"></el-input>
|
||||||
|
<span v-else>{{ row.long }}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column field="width" title="宽度" width="100">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-input v-if="row.editing" v-model="row.width" size="mini"></el-input>
|
||||||
|
<span v-else>{{ row.width }}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column field="load" title="载重(吨)" width="100">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-input v-if="row.editing" v-model="row.load" size="mini"></el-input>
|
||||||
|
<span v-else>{{ row.load }}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column field="minTurnRadius" title="最小转弯半径">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-input v-if="row.editing" v-model="row.minTurnRadius" size="mini"></el-input>
|
||||||
|
<span v-else>{{ row.minTurnRadius }}</span>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
|
<vxe-column title="操作" width="100">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<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 type="text" size="mini" @click="handleDelete(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button size="mini" @click="closeSelection">取消</el-button>
|
||||||
<el-button type="primary" @click="confirmSelection">确定</el-button>
|
<el-button type="primary" size="mini" @click="confirmSelection">确定</el-button>
|
||||||
|
<el-button type="primary" size="mini" @click="suerCofirm">保存</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@ -172,7 +245,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import router from '@/router'
|
import Cookies from 'js-cookie'
|
||||||
|
import axios from 'axios'
|
||||||
|
import iniParser from 'ini-parser'
|
||||||
|
import configIni from '/config.ini';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -198,14 +274,26 @@ export default {
|
|||||||
pointQD: null,
|
pointQD: null,
|
||||||
pointZD: null,
|
pointZD: null,
|
||||||
shortestPathLayer: null,
|
shortestPathLayer: null,
|
||||||
|
shortestPathList: [],
|
||||||
infoList: [],
|
infoList: [],
|
||||||
roadNetworkLayer: null,
|
roadNetworkLayer: null,
|
||||||
roadNetworkGeoJSON: null,
|
roadNetworkGeoJSON: null,
|
||||||
mapOptions: null,
|
mapOptions: null,
|
||||||
viaPoints: [], // 途径点
|
viaPoints: [], // 途经点
|
||||||
avoidPoints: [], // 避让点
|
avoidPoints: [], // 避让点
|
||||||
avoidAreas: [], // 避让区域
|
avoidAreas: [], // 避让区域
|
||||||
roadNetworkGeoJSONBuild: null,
|
roadNetworkGeoJSONBuild: null,
|
||||||
|
hideform: {
|
||||||
|
radius: 3000,
|
||||||
|
redundancy: 10,
|
||||||
|
},
|
||||||
|
join: false,
|
||||||
|
bufferLayerList: [], // 缓冲区信息
|
||||||
|
factoryGeoJSON: [], // 拿到的所有的厂房数据
|
||||||
|
accordFactoryInfo: [], // 在缓冲区 符合条件的厂房
|
||||||
|
factoriesWithVehicles: [], // 塞入车的厂房集合
|
||||||
|
accordFactoryLayer: null, // 隐蔽规划
|
||||||
|
accordPoint: null, // 隐蔽规划点
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
@ -220,10 +308,6 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
destroyMap() {
|
destroyMap() {
|
||||||
// if (this.viewer) {
|
|
||||||
// this.viewer.destroy();
|
|
||||||
// this.viewer = null;
|
|
||||||
// }
|
|
||||||
this.clear()
|
this.clear()
|
||||||
},
|
},
|
||||||
async getMapOption() {
|
async getMapOption() {
|
||||||
@ -237,6 +321,7 @@ export default {
|
|||||||
.catch((error) => {})
|
.catch((error) => {})
|
||||||
},
|
},
|
||||||
async initMap() {
|
async initMap() {
|
||||||
|
const parsedData = iniParser.parse(configIni);
|
||||||
this.viewer = new window.mars3d.Map(
|
this.viewer = new window.mars3d.Map(
|
||||||
'map',
|
'map',
|
||||||
{
|
{
|
||||||
@ -247,11 +332,23 @@ export default {
|
|||||||
center: {
|
center: {
|
||||||
lat: 27.729862392917948,
|
lat: 27.729862392917948,
|
||||||
lng: 114.27980291774088,
|
lng: 114.27980291774088,
|
||||||
alt: 3808,
|
alt: 45000,
|
||||||
heading: 5,
|
heading: 5,
|
||||||
pitch: -35,
|
pitch: -35,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// basemaps: [
|
||||||
|
// {
|
||||||
|
// id: "image-tdss",
|
||||||
|
// name: "影像图",
|
||||||
|
// type: "xyz",
|
||||||
|
// // url: `http:/www.tdss.website:280/tiles/img_c/{z}/{x}/{y}`,
|
||||||
|
// url: `http://${parsedData.http.address}:${parsedData.http.port}/web/imgs/{z}/{x}/{y}`,
|
||||||
|
// // crs: "EPSG:4490",
|
||||||
|
// crs: "EPSG:3857",
|
||||||
|
// show: true
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
} || {}
|
} || {}
|
||||||
)
|
)
|
||||||
this.graphicLayer = new window.mars3d.layer.GraphicLayer()
|
this.graphicLayer = new window.mars3d.layer.GraphicLayer()
|
||||||
@ -259,7 +356,10 @@ 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.loadShapefile()
|
|
||||||
|
this.accordFactoryLayer = new window.mars3d.layer.GraphicLayer()
|
||||||
|
this.viewer.addLayer(this.accordFactoryLayer)
|
||||||
|
this.loadShapefile() // 拿到路网数据
|
||||||
// 添加地图点击事件监听,用于结束绘制
|
// 添加地图点击事件监听,用于结束绘制
|
||||||
this.viewer.on(mars3d.EventType.dblClick, (event) => {
|
this.viewer.on(mars3d.EventType.dblClick, (event) => {
|
||||||
// 如果正在绘制,点击地图可以结束绘制(除了绘制点)
|
// 如果正在绘制,点击地图可以结束绘制(除了绘制点)
|
||||||
@ -267,6 +367,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
clear() {
|
clear() {
|
||||||
|
this.graphicLayer.stopDraw()
|
||||||
// 清除起点
|
// 清除起点
|
||||||
if (this.pointQD) {
|
if (this.pointQD) {
|
||||||
this.pointQD.remove()
|
this.pointQD.remove()
|
||||||
@ -281,7 +382,7 @@ export default {
|
|||||||
this.form.endPoint = ''
|
this.form.endPoint = ''
|
||||||
}
|
}
|
||||||
if (this.viaPoints.length > 0) {
|
if (this.viaPoints.length > 0) {
|
||||||
// 清除途径点
|
// 清除途经点
|
||||||
this.viaPoints.forEach((point) => {
|
this.viaPoints.forEach((point) => {
|
||||||
point.remove()
|
point.remove()
|
||||||
})
|
})
|
||||||
@ -310,6 +411,14 @@ export default {
|
|||||||
// 清除最短路径图层
|
// 清除最短路径图层
|
||||||
this.shortestPathLayer.clear()
|
this.shortestPathLayer.clear()
|
||||||
this.infoList = []
|
this.infoList = []
|
||||||
|
this.shortestPathList = []
|
||||||
|
}
|
||||||
|
if (this.accordFactoryLayer) {
|
||||||
|
// 清除路线隐蔽规划
|
||||||
|
this.accordFactoryLayer.clear()
|
||||||
|
this.accordFactoryInfo = []
|
||||||
|
this.bufferLayerList = []
|
||||||
|
this.factoriesWithVehicles = []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async loadShapefile() {
|
async loadShapefile() {
|
||||||
@ -330,45 +439,422 @@ export default {
|
|||||||
|
|
||||||
// 2. 处理 GeoJSON 数据
|
// 2. 处理 GeoJSON 数据
|
||||||
this.roadNetworkGeoJSON = shpBuffer
|
this.roadNetworkGeoJSON = shpBuffer
|
||||||
|
|
||||||
/// 3. 将 GeoJSON 数据添加到 graphicLayer
|
/// 3. 将 GeoJSON 数据添加到 graphicLayer
|
||||||
this.roadNetworkGeoJSON.features.forEach((feature) => {
|
this.roadNetworkGeoJSON.features.forEach((feature) => {
|
||||||
const positions = feature.geometry.coordinates[0]
|
// ====网路图=====
|
||||||
const graphic = new window.mars3d.graphic.PolylineEntity({
|
const graphicLine = new window.mars3d.graphic.PolylineEntity({
|
||||||
positions: positions,
|
positions: feature.geometry.coordinates[0],
|
||||||
style: {
|
style: {
|
||||||
color: '#FF0000',
|
color: '#FF0000',
|
||||||
width: 2,
|
width: 2,
|
||||||
outline: false,
|
outline: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
this.graphicLayer.addGraphic(graphic)
|
this.graphicLayer.addGraphic(graphicLine);
|
||||||
})
|
})
|
||||||
this.roadNetworkGeoJSONBuild = this.buildGraph(this.roadNetworkGeoJSON)
|
this.roadNetworkGeoJSONBuild = this.buildGraph(this.roadNetworkGeoJSON)
|
||||||
|
this.loadFactoryGeoJson() // 拿到厂房数据
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载 Shapefile 数据失败:', error)
|
console.error('加载 Shapefile 数据失败:', error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 获取厂房的数据
|
||||||
|
async loadFactoryGeoJson() {
|
||||||
|
try {
|
||||||
|
const shpBuffer = await fetch('./config/factory.geojson')
|
||||||
|
.then((response) => {
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
.catch((error) => {})
|
||||||
|
this.factoryGeoJSON = shpBuffer
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载厂房数据失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 去绘制点 进行隐蔽规划
|
||||||
|
pointBuffer() {
|
||||||
|
if (this.shortestPathList.length > 0 || this.accordFactoryInfo > 0) {
|
||||||
|
this.$message.warning('请先清空路线以及路线隐蔽规划')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.accordPoint) {
|
||||||
|
this.accordPoint.remove()
|
||||||
|
this.accordPoint = null
|
||||||
|
}
|
||||||
|
if (this.accordFactoryLayer) {
|
||||||
|
// 清除点的隐蔽规划
|
||||||
|
this.accordFactoryLayer.clear()
|
||||||
|
this.accordFactoryInfo = []
|
||||||
|
this.factoriesWithVehicles = []
|
||||||
|
}
|
||||||
|
this.accordFactoryLayer.startDraw({
|
||||||
|
type: 'point',
|
||||||
|
style: {
|
||||||
|
pixelSize: 10,
|
||||||
|
color: '#55ff33',
|
||||||
|
},
|
||||||
|
success: (graphic) => {
|
||||||
|
this.accordPoint = graphic
|
||||||
|
try {
|
||||||
|
// 创建缓冲区的宽度
|
||||||
|
const bufferWidth = this.hideform.radius; // 避让区的宽度(单位:米)
|
||||||
|
// 获取绘制的点的坐标
|
||||||
|
const pointCoordinates = [
|
||||||
|
graphic.toGeoJSON().geometry.coordinates[0],
|
||||||
|
graphic.toGeoJSON().geometry.coordinates[1],
|
||||||
|
]
|
||||||
|
// 创建点的 GeoJSON 特征
|
||||||
|
const pointFeature = {
|
||||||
|
type: 'Feature',
|
||||||
|
geometry: {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: pointCoordinates,
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
};
|
||||||
|
// 使用 Turf.js 计算缓冲区
|
||||||
|
let buffered = turf.buffer(pointFeature, bufferWidth / 1000, { units: 'kilometers' });
|
||||||
|
|
||||||
|
// 将缓冲区存储到数组中
|
||||||
|
this.bufferLayerList.push(buffered);
|
||||||
|
const polygon = new window.mars3d.graphic.PolygonEntity({
|
||||||
|
positions: buffered.geometry.coordinates[0],
|
||||||
|
style: {
|
||||||
|
color: '#d4e5db',
|
||||||
|
opacity: 0.5,
|
||||||
|
outline: true,
|
||||||
|
outlineWidth: 1,
|
||||||
|
outlineColor: '#ffffff',
|
||||||
|
},
|
||||||
|
time: new Date().getTime()
|
||||||
|
})
|
||||||
|
this.accordFactoryLayer.addGraphic(polygon);
|
||||||
|
// 检查缓冲区内的厂房
|
||||||
|
this.checkFactoryInBuffer('point', pointCoordinates);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("缓冲区生成或检查异常:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获取当前路线的缓冲区
|
||||||
|
hadBuffer () {
|
||||||
|
if (this.shortestPathList.length == 0) {
|
||||||
|
this.$message.warning('请先进行路线规划')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (JSON.parse(Cookies.get('minTurnRadius')).length == 0) {
|
||||||
|
this.$message.warning('请先选择车辆')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// =======检查几何对象的类型 缓冲区========
|
||||||
|
for (const feature of this.shortestPathList[0]) {
|
||||||
|
// this.shortestPathList[0].forEach((feature) => {
|
||||||
|
// 创建缓冲区的宽度
|
||||||
|
const bufferWidth = this.hideform.radius; // 避让区的宽度(单位:米)
|
||||||
|
if (feature.geometry.type === 'LineString') {
|
||||||
|
const positions = feature.geometry.coordinates[0];
|
||||||
|
// 确保每条线至少有 2 个点
|
||||||
|
if (positions.length < 2) {
|
||||||
|
console.warn('无效的线,跳过:', feature);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 使用 Turf.js 计算缓冲区
|
||||||
|
var buffered = turf.buffer(feature, bufferWidth / 1000, { units: 'kilometers' });
|
||||||
|
|
||||||
|
// 将缓冲区存储到数组中
|
||||||
|
this.bufferLayerList.push(buffered);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("缓冲分析异常:", error);
|
||||||
|
}
|
||||||
|
} else if (feature.geometry.type === 'MultiLineString') {
|
||||||
|
// 遍历每个线段
|
||||||
|
feature.geometry.coordinates.forEach((lineCoordinates) => {
|
||||||
|
// 检查每个线段是否有效
|
||||||
|
if (lineCoordinates.length < 2) {
|
||||||
|
console.warn('多线段中的无效的线段,跳过:', lineCoordinates);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建单个线段的 GeoJSON 特征
|
||||||
|
const lineFeature = {
|
||||||
|
type: 'Feature',
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: lineCoordinates,
|
||||||
|
},
|
||||||
|
properties: feature.properties
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
// 使用 Turf.js 计算缓冲区
|
||||||
|
var buffered = turf.buffer(lineFeature, bufferWidth / 1000, { units: 'kilometers' });
|
||||||
|
|
||||||
|
// 将缓冲区存储到数组中
|
||||||
|
this.bufferLayerList.push(buffered);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("缓冲分析异常:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('不支持的几何类型:', feature.geometry.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去筛选在缓存区的厂房
|
||||||
|
this.checkFactoryInBuffer('line');
|
||||||
|
}catch (error) {
|
||||||
|
console.error("处理路径列表时发生错误:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
checkFactoryInBuffer(type, pointInfo) {
|
||||||
|
if (!this.factoryGeoJSON || !this.bufferLayerList.length) {
|
||||||
|
this.$message.warning('厂房数据或缓冲区数据未加载')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (JSON.parse(Cookies.get('minTurnRadius')).length == 0) {
|
||||||
|
this.$message.warning('请先选择车辆')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将缓冲区数据转换为 Turf.js 的 FeatureCollection
|
||||||
|
// 遍历每个厂房
|
||||||
|
const factoryGeoJSON = JSON.parse(JSON.stringify(this.factoryGeoJSON))
|
||||||
|
const factoryPromises = factoryGeoJSON.features.map((factory) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 检查几何对象的类型
|
||||||
|
if (factory.geometry.type === 'MultiPolygon') {
|
||||||
|
// 创建多边形集合(MultiPolygon)
|
||||||
|
const factoryMultiPoly = turf.multiPolygon(factory.geometry.coordinates);
|
||||||
|
// 计算整个多边形集合的面积
|
||||||
|
const area = turf.area(factoryMultiPoly);
|
||||||
|
// 计算工厂多边形集合的边界框
|
||||||
|
const [minX, minY, maxX, maxY] = turf.bbox(factoryMultiPoly);
|
||||||
|
|
||||||
|
// 筛选与工厂边界框相交的缓冲区
|
||||||
|
const candidates = this.bufferLayerList.filter((buffer) => {
|
||||||
|
const [bminX, bminY, bmaxX, bmaxY] = turf.bbox(buffer);
|
||||||
|
return !(bmaxX < minX || bminX > maxX || bmaxY < minY || bminY > maxY);
|
||||||
|
});
|
||||||
|
// 精确相交检测
|
||||||
|
let isInside = false;
|
||||||
|
for (const buffer of candidates) {
|
||||||
|
if (turf.booleanIntersects(factoryMultiPoly, buffer)) {
|
||||||
|
// 如果工厂在缓冲区内,绘制所有多边形
|
||||||
|
isInside = true;
|
||||||
|
this.drawFactory(factory.geometry.coordinates, factory, area, pointInfo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果工厂不在任何缓冲区内
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(new Error('不支持的几何类型'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
// 确保所有工厂处理完成后进行排序
|
||||||
|
Promise.allSettled(factoryPromises)
|
||||||
|
.then(async (results) => {
|
||||||
|
// 只有路线隐蔽规划需要塞入车辆 点的不需要
|
||||||
|
// 所有工厂处理完成,进行排序 按照距离近到远
|
||||||
|
this.accordFactoryInfo.sort((a, b) => a.distance - b.distance);
|
||||||
|
// 拿到当前的车队车辆信息 并且算出面积 按照从大到小排序
|
||||||
|
const areaList = JSON.parse(Cookies.get('minTurnRadius'))
|
||||||
|
areaList.map((item) => {
|
||||||
|
item.area = Number(item.long) * Number(item.width)
|
||||||
|
})
|
||||||
|
areaList.sort((a, b) => b.area - a.area);
|
||||||
|
// 初始化每个工厂的剩余面积
|
||||||
|
this.accordFactoryInfo.forEach(factory => {
|
||||||
|
factory.remainingArea = factory.area; // 记录每个工厂的剩余可用面积
|
||||||
|
factory.vehicles = []; // 确保每个工厂都有一个 vehicles 属性
|
||||||
|
});
|
||||||
|
// 遍历每个车辆,尝试塞入工厂
|
||||||
|
areaList.forEach((vehicle) => {
|
||||||
|
let isPlaced = false;
|
||||||
|
|
||||||
|
// 遍历每个工厂,尝试塞入车辆
|
||||||
|
for (let i = 0; i < this.accordFactoryInfo.length; i++) {
|
||||||
|
const factory = this.accordFactoryInfo[i];
|
||||||
|
|
||||||
|
// 检查工厂是否还有剩余面积
|
||||||
|
if (factory.remainingArea >= vehicle.area) {
|
||||||
|
// 塞入车辆
|
||||||
|
factory.vehicles.push({
|
||||||
|
...vehicle,
|
||||||
|
remainingArea: factory.remainingArea - vehicle.area // 记录车辆塞入后的工厂剩余面积
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新工厂的剩余面积
|
||||||
|
factory.remainingArea -= vehicle.area;
|
||||||
|
|
||||||
|
// 标记车辆已放置
|
||||||
|
isPlaced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果车辆没有被放置,可以在这里处理(例如记录日志或显示警告)
|
||||||
|
if (!isPlaced) {
|
||||||
|
console.warn(`车辆 ${vehicle.id} 无法放置在任何工厂中`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新 this.accordFactoryInfo
|
||||||
|
this.accordFactoryInfo = this.accordFactoryInfo.map(factory => {
|
||||||
|
// 确保每个工厂都有 vehicles 属性
|
||||||
|
if (!factory.vehicles) {
|
||||||
|
factory.vehicles = [];
|
||||||
|
}
|
||||||
|
// 确保 remainingArea 是一个数字且大于 0
|
||||||
|
factory.area = factory.area > 0 ? factory.area : 0;
|
||||||
|
return factory;
|
||||||
|
});
|
||||||
|
// 过滤出有车辆的工厂
|
||||||
|
const factoriesWithVehicles = this.accordFactoryInfo.filter(factory => factory.vehicles.length > 0);
|
||||||
|
this.factoriesWithVehicles = factoriesWithVehicles
|
||||||
|
await this.showAreaInfoDialog(factoriesWithVehicles);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('数据有问题', error)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
drawFactory(factoryMultiPoly, factory, area, pointInfo) {
|
||||||
|
// 获取当前多边形集合的中心点
|
||||||
|
const center = turf.center(turf.multiPolygon(factory.geometry.coordinates));
|
||||||
|
// 将中心点转换为 Mars3D 的 LngLatPoint 对象
|
||||||
|
const popupPosition = new mars3d.LngLatPoint(center.geometry.coordinates[0], center.geometry.coordinates[1]);
|
||||||
|
// 遍历每个多边形集合
|
||||||
|
factory.geometry.coordinates[0].forEach((coordGroup) => {
|
||||||
|
// 创建一个多边形图形
|
||||||
|
const graphic = new window.mars3d.graphic.PolygonEntity({
|
||||||
|
positions: coordGroup.map((coord) => Cesium.Cartesian3.fromDegrees(coord[0], coord[1], 0)),
|
||||||
|
style: {
|
||||||
|
color: 'red',
|
||||||
|
opacity: 0.4,
|
||||||
|
outline: true,
|
||||||
|
outlineWidth: 1,
|
||||||
|
outlineColor: '#ffffff',
|
||||||
|
properties: { ...factory.properties },
|
||||||
|
},
|
||||||
|
time: new Date().getTime()
|
||||||
|
})
|
||||||
|
this.accordFactoryLayer.addGraphic(graphic);
|
||||||
|
// 计算多边形中心与参考点的距离
|
||||||
|
const point = pointInfo && pointInfo.length > 0 ? pointInfo : this.form.endPoint.split(',').map(Number)
|
||||||
|
const distance = turf.distance(center.geometry.coordinates, point, { units: 'kilometers' }) * 1000; // 米
|
||||||
|
// 将多边形图形添加到地图层
|
||||||
|
this.accordFactoryInfo.push({
|
||||||
|
...graphic,
|
||||||
|
distance: distance,
|
||||||
|
popupPosition: popupPosition,
|
||||||
|
area: this.hideform.redundancy && this.hideform.redundancy != 0 && area > 0
|
||||||
|
? area * (100 - this.hideform.redundancy) * 0.01
|
||||||
|
: area > 0
|
||||||
|
? area
|
||||||
|
: 0, // 如果 area 不是有效值,设置为 0 或其他默认值
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 显示面积信息弹框的方法
|
||||||
|
async showAreaInfoDialog(info) {
|
||||||
|
const graphics = this.accordFactoryLayer.getGraphics();
|
||||||
|
const promises = [];
|
||||||
|
info.forEach((item) => {
|
||||||
|
graphics.forEach((ele) => {
|
||||||
|
if (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({
|
||||||
|
id: 'label',
|
||||||
|
position: item.popupPosition,
|
||||||
|
style: {
|
||||||
|
text: String(textInfo), // 必须字符串
|
||||||
|
font_family: '楷体',
|
||||||
|
font_size: 24,
|
||||||
|
color: '#ffffff',
|
||||||
|
outline: true,
|
||||||
|
outlineColor: '#000000',
|
||||||
|
background: true,
|
||||||
|
backgroundColor: '#2f705f',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 使用 Promise 确保标签添加完成后继续执行
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
this.accordFactoryLayer.addGraphic(labelGraphic, () => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
promises.push(promise);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.$message.success("路线隐蔽规划成功")
|
||||||
|
const popupPositionList = info.map(item => item.popupPosition)
|
||||||
|
let minLng = Infinity;
|
||||||
|
let maxLng = -Infinity;
|
||||||
|
let minLat = Infinity;
|
||||||
|
let maxLat = -Infinity;
|
||||||
|
|
||||||
|
popupPositionList.forEach(point => {
|
||||||
|
if (point.lng < minLng) minLng = point.lng;
|
||||||
|
if (point.lng > maxLng) maxLng = point.lng;
|
||||||
|
if (point.lat < minLat) minLat = point.lat;
|
||||||
|
if (point.lat > maxLat) maxLat = point.lat;
|
||||||
|
});
|
||||||
|
// 创建一个矩形区域,包含所有点
|
||||||
|
const rectangle = Cesium.Rectangle.fromDegrees(minLng, minLat, maxLng, maxLat);
|
||||||
|
|
||||||
|
this.viewer.scene.camera.flyTo({
|
||||||
|
destination: Cesium.Cartesian3.fromDegrees(
|
||||||
|
(minLng + maxLng) / 2, // 中心经度
|
||||||
|
(minLat + maxLat) / 2, // 中心纬度
|
||||||
|
1000 // 增加高度,确保视野范围足够大
|
||||||
|
),
|
||||||
|
orientation: {
|
||||||
|
heading: Cesium.Math.toRadians(0), // 方位角
|
||||||
|
pitch: Cesium.Math.toRadians(-90), // 俯仰角
|
||||||
|
roll: 0.0 // 翻滚角
|
||||||
|
},
|
||||||
|
duration: 2 // 飞行动画持续时间,单位为秒
|
||||||
|
});
|
||||||
|
},
|
||||||
// 弹框
|
// 弹框
|
||||||
async openDialog() {
|
async openDialog() {
|
||||||
|
this.multipleSelection = []
|
||||||
await fetch('./data/minTurnRadius.json')
|
await fetch('./data/minTurnRadius.json')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then(async (data) => {
|
||||||
|
this.dialogVisible = true;
|
||||||
this.tableData = data
|
this.tableData = data
|
||||||
|
await this.$nextTick(() => {
|
||||||
|
this.multipleSelection = JSON.parse(Cookies.get('minTurnRadius'))
|
||||||
|
this.multipleSelection.forEach((item) => {
|
||||||
|
this.$refs.vxeTable.setCheckboxRowKey(item.id, true)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.catch((error) => {})
|
.catch((error) => {})
|
||||||
this.dialogVisible = true;
|
|
||||||
},
|
},
|
||||||
handleSelectionChange({records}) {
|
handleSelectionChange({records}) {
|
||||||
this.multipleSelection = records
|
this.multipleSelection = records
|
||||||
},
|
},
|
||||||
|
closeSelection() {
|
||||||
|
this.dialogVisible = false
|
||||||
|
},
|
||||||
confirmSelection() {
|
confirmSelection() {
|
||||||
if (this.multipleSelection.length === 0) {
|
if (this.multipleSelection.length === 0) {
|
||||||
this.$message.warning('请至少选择一行数据')
|
this.$message.warning('请至少选择一行数据')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Cookies.set('minTurnRadius', this.multipleSelection)
|
||||||
const maxValues = this.multipleSelection.reduce(
|
const maxValues = this.multipleSelection.reduce(
|
||||||
(acc, item) => {
|
(acc, item) => {
|
||||||
acc.width = Math.max(acc.width, item.width)
|
acc.width = Math.max(acc.width, item.width)
|
||||||
@ -381,7 +867,47 @@ export default {
|
|||||||
this.inputform.width = maxValues.width
|
this.inputform.width = maxValues.width
|
||||||
this.inputform.load = maxValues.load
|
this.inputform.load = maxValues.load
|
||||||
this.inputform.minTurnRadius = maxValues.minTurnRadius
|
this.inputform.minTurnRadius = maxValues.minTurnRadius
|
||||||
this.dialogVisible = false
|
this.closeSelection()
|
||||||
|
},
|
||||||
|
/** 增删改查 */
|
||||||
|
handleAdd() {
|
||||||
|
const newRow = {
|
||||||
|
id: this.tableData.length + 1,
|
||||||
|
name: '',
|
||||||
|
long: null,
|
||||||
|
width: null,
|
||||||
|
load: null,
|
||||||
|
minTurnRadius: null,
|
||||||
|
editing: true,
|
||||||
|
};
|
||||||
|
this.tableData.push(newRow);
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
const index = this.tableData.findIndex(item => item.id === row.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.tableData.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleEdit(row) {
|
||||||
|
this.$set(row, 'editing', true);
|
||||||
|
},
|
||||||
|
handleSave(row) {
|
||||||
|
this.$set(row, 'editing', false);
|
||||||
|
},
|
||||||
|
// 保存列表数据
|
||||||
|
suerCofirm() {
|
||||||
|
const parsedData = iniParser.parse(configIni);
|
||||||
|
axios.post(`http://${parsedData.http.address}:${parsedData.http.port}/api/equpment`, JSON.stringify(this.tableData), {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer your_token_here',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
});
|
||||||
},
|
},
|
||||||
// 导入json文件
|
// 导入json文件
|
||||||
triggerFileUpload() {
|
triggerFileUpload() {
|
||||||
@ -422,7 +948,7 @@ export default {
|
|||||||
this.addPointToMap('endPoint', this.form.endPoint)
|
this.addPointToMap('endPoint', this.form.endPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载途径点
|
// 加载途经点
|
||||||
if (uploadedJson.viaPoints) {
|
if (uploadedJson.viaPoints) {
|
||||||
this.form.viaPoints = uploadedJson.viaPoints
|
this.form.viaPoints = uploadedJson.viaPoints
|
||||||
uploadedJson.viaPoints.forEach((viaPoint) => {
|
uploadedJson.viaPoints.forEach((viaPoint) => {
|
||||||
@ -470,7 +996,7 @@ export default {
|
|||||||
: type === 'endPoint'
|
: type === 'endPoint'
|
||||||
? '终点'
|
? '终点'
|
||||||
: type === 'viaPoints'
|
: type === 'viaPoints'
|
||||||
? '途径点'
|
? '途经点'
|
||||||
: '避让点',
|
: '避让点',
|
||||||
font_size: 20,
|
font_size: 20,
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
@ -517,7 +1043,6 @@ export default {
|
|||||||
},
|
},
|
||||||
// 输入框失去焦点 反向编辑点
|
// 输入框失去焦点 反向编辑点
|
||||||
pointsChange(type, row) {
|
pointsChange(type, row) {
|
||||||
console.log('111', type)
|
|
||||||
if (type === 'startPoint') {
|
if (type === 'startPoint') {
|
||||||
if (
|
if (
|
||||||
(!this.form.startPoint ||
|
(!this.form.startPoint ||
|
||||||
@ -530,7 +1055,11 @@ export default {
|
|||||||
this.pointQD = null
|
this.pointQD = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.updatePointPosition(this.pointQD, this.form.startPoint)
|
if (this.pointQD == null || this.pointQD == undefined || this.pointQD == '') {
|
||||||
|
this.addPointToMap('startPoint', this.form.startPoint)
|
||||||
|
} else {
|
||||||
|
this.updatePointPosition(this.pointQD, this.form.startPoint)
|
||||||
|
}
|
||||||
} else if (type === 'endPoint') {
|
} else if (type === 'endPoint') {
|
||||||
if (
|
if (
|
||||||
(!this.form.endPoint ||
|
(!this.form.endPoint ||
|
||||||
@ -543,7 +1072,11 @@ export default {
|
|||||||
this.pointZD = null
|
this.pointZD = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.updatePointPosition(this.pointZD, this.form.endPoint)
|
if (this.pointZD == null || this.pointZD == undefined || this.pointZD == '') {
|
||||||
|
this.addPointToMap('endPoint', this.form.endPoint)
|
||||||
|
} else {
|
||||||
|
this.updatePointPosition(this.pointZD, this.form.endPoint)
|
||||||
|
}
|
||||||
} else if (type === 'viaPoints') {
|
} else if (type === 'viaPoints') {
|
||||||
if (!row.points) {
|
if (!row.points) {
|
||||||
const graphic = this.viaPoints.find(
|
const graphic = this.viaPoints.find(
|
||||||
@ -725,7 +1258,7 @@ export default {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 途径点
|
// 途经点
|
||||||
drawViaPoint() {
|
drawViaPoint() {
|
||||||
const time = Date.now()
|
const time = Date.now()
|
||||||
this.graphicLayer.startDraw({
|
this.graphicLayer.startDraw({
|
||||||
@ -734,7 +1267,7 @@ export default {
|
|||||||
pixelSize: 10,
|
pixelSize: 10,
|
||||||
color: 'blue',
|
color: 'blue',
|
||||||
label: {
|
label: {
|
||||||
text: '途径点',
|
text: '途经点',
|
||||||
font_size: 20,
|
font_size: 20,
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
outline: true,
|
outline: true,
|
||||||
@ -884,7 +1417,7 @@ export default {
|
|||||||
}
|
}
|
||||||
return nearestNode
|
return nearestNode
|
||||||
},
|
},
|
||||||
// 3. 路径规划主函数(经纬度坐标输入) - 支持途径点 + 避让点/区域
|
// 3. 路径规划主函数(经纬度坐标输入) - 支持途经点 + 避让点/区域
|
||||||
async planRoute(
|
async planRoute(
|
||||||
startCoord,
|
startCoord,
|
||||||
endCoord,
|
endCoord,
|
||||||
@ -893,14 +1426,16 @@ export default {
|
|||||||
) {
|
) {
|
||||||
const {graph, nodeCoords} = this.roadNetworkGeoJSONBuild
|
const {graph, nodeCoords} = this.roadNetworkGeoJSONBuild
|
||||||
|
|
||||||
// 按顺序组合点:起点 -> 途径点 -> 终点
|
// 按顺序组合点:起点 -> 途经点 -> 终点
|
||||||
const points =
|
const points = [startCoord];
|
||||||
viaPoints && viaPoints.length > 0
|
if (viaPoints && viaPoints.length > 0) {
|
||||||
? [startCoord, viaPoints[0].geometry.coordinates, endCoord]
|
viaPoints.forEach((viaPoint) => {
|
||||||
: [startCoord, endCoord]
|
points.push(viaPoint.geometry.coordinates);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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++) {
|
||||||
const segmentStart = points[i]
|
const segmentStart = points[i]
|
||||||
const segmentEnd = points[i + 1]
|
const segmentEnd = points[i + 1]
|
||||||
@ -960,16 +1495,28 @@ export default {
|
|||||||
for (let j = 0; j < pathNodes.length - 1; j++) {
|
for (let j = 0; j < pathNodes.length - 1; j++) {
|
||||||
const currentNode = pathNodes[j]
|
const currentNode = pathNodes[j]
|
||||||
const nextNode = pathNodes[j + 1]
|
const nextNode = pathNodes[j + 1]
|
||||||
const segment = this.roadNetworkGeoJSON.features.find(
|
let segment = {}
|
||||||
|
// 是否参与路线规划 是 加入机动属性的判断 否就正常走
|
||||||
|
if (!this.join) {
|
||||||
|
segment = this.roadNetworkGeoJSON.features.find(
|
||||||
|
(f) =>
|
||||||
|
(f.properties.FNODE_ == currentNode &&
|
||||||
|
f.properties.TNODE_ == nextNode) ||
|
||||||
|
(f.properties.FNODE_ == nextNode &&
|
||||||
|
f.properties.TNODE_ == currentNode)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
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.FNODE_ == nextNode &&
|
||||||
f.properties.TNODE_ == currentNode) )&&
|
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
|
||||||
)
|
)
|
||||||
|
}
|
||||||
if (segment) {
|
if (segment) {
|
||||||
fullPath.push(...segment.geometry.coordinates[0])
|
fullPath.push(...segment.geometry.coordinates[0])
|
||||||
infoList.push(segment)
|
infoList.push(segment)
|
||||||
@ -985,13 +1532,14 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.shortestPathLayer.clear()
|
this.shortestPathLayer.clear()
|
||||||
|
this.shortestPathList = []
|
||||||
this.infoList = []
|
this.infoList = []
|
||||||
const startPoint = turf.point(
|
const startPoint = turf.point(
|
||||||
this.pointQD.toGeoJSON().geometry.coordinates
|
this.pointQD.toGeoJSON().geometry.coordinates
|
||||||
).geometry.coordinates // 起点
|
).geometry.coordinates // 起点
|
||||||
const endPoint = turf.point(this.pointZD.toGeoJSON().geometry.coordinates)
|
const endPoint = turf.point(this.pointZD.toGeoJSON().geometry.coordinates)
|
||||||
.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) =>
|
const viaPointsTurf = viaPointsGeoJSON.map((p) =>
|
||||||
@ -1003,7 +1551,7 @@ export default {
|
|||||||
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),
|
||||||
10, // 半径10米(根据需求调整)
|
1000, // 半径10米(根据需求调整)
|
||||||
{steps: 32, units: 'meters'} // 显式指定单位
|
{steps: 32, units: 'meters'} // 显式指定单位
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1036,8 +1584,13 @@ export default {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
this.shortestPathLayer.addGraphic(polyline)
|
this.shortestPathLayer.addGraphic(polyline)
|
||||||
|
this.shortestPathList.push(path.infoList)
|
||||||
this.infoList = path.infoList.map((item) => item.properties)
|
this.infoList = path.infoList.map((item) => item.properties)
|
||||||
},
|
},
|
||||||
|
/** 隐蔽规划 */
|
||||||
|
concealed() {
|
||||||
|
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -1067,7 +1620,8 @@ export default {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
background: #176363;
|
background: #176363;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
width: 54px;
|
min-width: 14px;
|
||||||
|
padding: 0 10px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@ -1107,7 +1661,17 @@ export default {
|
|||||||
/* text-align: center; */
|
/* text-align: center; */
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
color: #1c1c1c;
|
color: #1c1c1c;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
.control-panel .title .joinCheck {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.el-checkbox {
|
||||||
|
margin-right: 5px!important;
|
||||||
|
}
|
||||||
|
|
||||||
.importJson {
|
.importJson {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -1148,4 +1712,15 @@ export default {
|
|||||||
width: calc(100vw- 792px);
|
width: calc(100vw- 792px);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.popDiloag {
|
||||||
|
width: 100px;
|
||||||
|
font-size: 16px;
|
||||||
|
background: #d4e5db;
|
||||||
|
color: #1c1c1c;
|
||||||
|
}
|
||||||
|
.popDiloag .popDiloag-title {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.popDiloag .popDiloag-p {
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
2275
src/views/home/home.vue
Normal file
2275
src/views/home/home.vue
Normal file
File diff suppressed because it is too large
Load Diff
2711
src/views/home/home_offlineData.vue
Normal file
2711
src/views/home/home_offlineData.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -74,19 +74,28 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="center" id="cesiumContainer"></div>
|
<div class="center" id="cesiumContainer"></div>
|
||||||
|
|
||||||
<div class="right flex column j-s">
|
<div
|
||||||
|
class="right flex column"
|
||||||
|
v-loading="showLoading"
|
||||||
|
element-loading-text="数据加载中"
|
||||||
|
>
|
||||||
<!-- 路点 -->
|
<!-- 路点 -->
|
||||||
|
<div class="table-title" v-if="roadPointList.length > 0">
|
||||||
|
道路附属点
|
||||||
|
</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
class="item"
|
class="item"
|
||||||
:data="roadPointList"
|
:data="roadPointList"
|
||||||
v-if="roadPointList.length > 0"
|
v-if="roadPointList.length > 0"
|
||||||
>
|
>
|
||||||
<vxe-column type="seq" width="70"></vxe-column>
|
<vxe-column type="seq" width="70"></vxe-column>
|
||||||
<vxe-column field="name" title="Name"></vxe-column>
|
<vxe-column field="编码" title="编码"></vxe-column>
|
||||||
<vxe-column field="sex" title="Sex"></vxe-column>
|
<vxe-column field="名称" title="名称"></vxe-column>
|
||||||
<vxe-column field="age" title="Age"></vxe-column>
|
<vxe-column field="A" title="A"></vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
|
|
||||||
<!-- 路线 -->
|
<!-- 路线 -->
|
||||||
|
<div class="table-title" v-if="roadLineList.length > 0">道路附属线</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
class="item"
|
class="item"
|
||||||
:data="roadLineList"
|
:data="roadLineList"
|
||||||
@ -94,30 +103,38 @@
|
|||||||
>
|
>
|
||||||
<vxe-column type="seq" width="70"></vxe-column>
|
<vxe-column type="seq" width="70"></vxe-column>
|
||||||
<vxe-column field="编码" title="编码"></vxe-column>
|
<vxe-column field="编码" title="编码"></vxe-column>
|
||||||
<vxe-column field="A" title="名称"></vxe-column>
|
<vxe-column field="名称" title="名称"></vxe-column>
|
||||||
<vxe-column field="图形特" title="图形特"></vxe-column>
|
<vxe-column field="A" title="A"></vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
|
|
||||||
<!-- 水点 -->
|
<!-- 水点 -->
|
||||||
|
<div class="table-title" v-if="waterPointList.length > 0">
|
||||||
|
水系附属点
|
||||||
|
</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
class="item"
|
class="item"
|
||||||
:data="waterPointList"
|
:data="waterPointList"
|
||||||
v-if="waterPointList.length > 0"
|
v-if="waterPointList.length > 0"
|
||||||
>
|
>
|
||||||
<vxe-column type="seq" width="70"></vxe-column>
|
<vxe-column type="seq" width="70"></vxe-column>
|
||||||
<vxe-column field="name" title="Name"></vxe-column>
|
<vxe-column field="编码" title="编码"></vxe-column>
|
||||||
<vxe-column field="sex" title="Sex"></vxe-column>
|
<vxe-column field="名称" title="名称"></vxe-column>
|
||||||
<vxe-column field="age" title="Age"></vxe-column>
|
<vxe-column field="A" title="A"></vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
|
|
||||||
<!-- 水线 -->
|
<!-- 水线 -->
|
||||||
|
<div class="table-title" v-if="waterLineList.length > 0">
|
||||||
|
水系附属线
|
||||||
|
</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
class="item"
|
class="item"
|
||||||
:data="waterLineList"
|
:data="waterLineList"
|
||||||
v-if="waterLineList.length > 0"
|
v-if="waterLineList.length > 0"
|
||||||
>
|
>
|
||||||
<vxe-column type="seq" width="70"></vxe-column>
|
<vxe-column type="seq" width="70"></vxe-column>
|
||||||
<vxe-column field="name" title="Name"></vxe-column>
|
<vxe-column field="编码" title="编码"></vxe-column>
|
||||||
<vxe-column field="sex" title="Sex"></vxe-column>
|
<vxe-column field="名称" title="名称"></vxe-column>
|
||||||
<vxe-column field="age" title="Age"></vxe-column>
|
<vxe-column field="A" title="A"></vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -133,6 +150,7 @@ export default {
|
|||||||
name: '',
|
name: '',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
showLoading: false,
|
||||||
imagesList: [{id: 1, src: require('@/assets/image/crop.png')}],
|
imagesList: [{id: 1, src: require('@/assets/image/crop.png')}],
|
||||||
roadPointList: [],
|
roadPointList: [],
|
||||||
roadLineList: [],
|
roadLineList: [],
|
||||||
@ -682,6 +700,8 @@ export default {
|
|||||||
// 清除之前的矩形
|
// 清除之前的矩形
|
||||||
this.clearRectangles()
|
this.clearRectangles()
|
||||||
|
|
||||||
|
console.log('this.rectangles===>', this.rectangles)
|
||||||
|
|
||||||
// 获取分割参数
|
// 获取分割参数
|
||||||
const xSplit = parseInt(this.form.xLength) || 3
|
const xSplit = parseInt(this.form.xLength) || 3
|
||||||
const ySplit = parseInt(this.form.yLength) || 3
|
const ySplit = parseInt(this.form.yLength) || 3
|
||||||
@ -813,6 +833,7 @@ export default {
|
|||||||
console.error('坡度分析失败', e)
|
console.error('坡度分析失败', e)
|
||||||
this.$message.error('坡度分析失败,请检查地形数据')
|
this.$message.error('坡度分析失败,请检查地形数据')
|
||||||
} finally {
|
} finally {
|
||||||
|
rectInfoMap.clear()
|
||||||
this.drawLabelAndRec()
|
this.drawLabelAndRec()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1014,7 +1035,11 @@ export default {
|
|||||||
|
|
||||||
// 选择矩形
|
// 选择矩形
|
||||||
selectRectangle(rect) {
|
selectRectangle(rect) {
|
||||||
console.log('rect===>', rect)
|
this.showLoading = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showLoading = false
|
||||||
|
}, 500)
|
||||||
|
|
||||||
if (this.selectedRect?.id === rect.id) return
|
if (this.selectedRect?.id === rect.id) return
|
||||||
this.selectedRect = {
|
this.selectedRect = {
|
||||||
id: rect.id,
|
id: rect.id,
|
||||||
@ -1092,17 +1117,17 @@ export default {
|
|||||||
// 清除所有矩形
|
// 清除所有矩形
|
||||||
clearRectangles() {
|
clearRectangles() {
|
||||||
if (this.rectangles.length > 0) {
|
if (this.rectangles.length > 0) {
|
||||||
this.rectangles.forEach((rect) => {
|
this.rectangles.forEach((item) => item.remove())
|
||||||
window.graphicLayer.removeGraphic(rect)
|
window.graphicLayer.removeGraphic(this.rectangles)
|
||||||
})
|
this.rectangles = []
|
||||||
|
this.rectCount = 0
|
||||||
|
this.validBlocks = []
|
||||||
}
|
}
|
||||||
if (this.labels.length > 0) {
|
if (this.labels.length > 0) {
|
||||||
this.labels.forEach((label) => {
|
this.labels.forEach((item) => item.remove())
|
||||||
window.graphicLayer.removeGraphic(label)
|
window.graphicLayer.removeGraphic(this.labels)
|
||||||
})
|
this.labels = []
|
||||||
}
|
}
|
||||||
this.rectangles = []
|
|
||||||
this.labels = []
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1192,11 +1217,20 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: #d4e5db;
|
background-color: #d4e5db;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
|
padding-right: 10px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: 'Pingfang';
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,30 +1,62 @@
|
|||||||
const {defineConfig} = require('@vue/cli-service')
|
const {defineConfig} = require('@vue/cli-service')
|
||||||
const postcssPxToViewport = require('postcss-px-to-viewport')
|
const postcssPxToViewport = require('postcss-px-to-viewport')
|
||||||
const path = require('path') // 需要引入 path 模块
|
const path = require('path')
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
publicPath: './',
|
publicPath: './',
|
||||||
transpileDependencies: false,
|
transpileDependencies: false, // 我们自己管,不让 CLI 插手
|
||||||
lintOnSave: false,
|
lintOnSave: false,
|
||||||
|
devServer: {client: {overlay: false}},
|
||||||
|
|
||||||
|
configureWebpack(config) {
|
||||||
|
// 调试
|
||||||
|
config.devtool = 'source-map'
|
||||||
|
// 别名
|
||||||
|
config.resolve.alias['@'] = path.resolve(__dirname, 'src')
|
||||||
|
|
||||||
|
/* 1. 追加 ini 规则 */
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.ini$/,
|
||||||
|
use: 'raw-loader',
|
||||||
|
})
|
||||||
|
|
||||||
|
/* 2. 追加 ES5 规则(只转 src,不转 node_modules) */
|
||||||
|
// config.module.rules.push({
|
||||||
|
// test: /\.js$/,
|
||||||
|
// include: path.resolve(__dirname, 'src'),
|
||||||
|
// use: {
|
||||||
|
// loader: 'babel-loader',
|
||||||
|
// options: {
|
||||||
|
// presets: [
|
||||||
|
// [
|
||||||
|
// '@babel/preset-env',
|
||||||
|
// {
|
||||||
|
// targets: {ie: '11'}, // 强制 ES5
|
||||||
|
// corejs: 3,
|
||||||
|
// useBuiltIns: 'entry',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
client: {
|
client: { overlay: false },
|
||||||
overlay: false,
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://192.168.3.35:8381',
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: { '^/api': '' },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
configureWebpack: (config) => {
|
|
||||||
//调试JS
|
|
||||||
config.devtool = 'source-map'
|
|
||||||
config.resolve = {
|
|
||||||
alias: {
|
|
||||||
'@': path.resolve(__dirname, 'src'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
css: {
|
css: {
|
||||||
|
/* 你的 px-to-viewport 配置不动 */
|
||||||
loaderOptions: {
|
loaderOptions: {
|
||||||
postcss: {
|
postcss: {
|
||||||
postcssOptions: {
|
postcssOptions: {
|
||||||
// 增加这一层 postcssOptions
|
|
||||||
plugins: [
|
plugins: [
|
||||||
postcssPxToViewport({
|
postcssPxToViewport({
|
||||||
unitToConvert: 'px',
|
unitToConvert: 'px',
|
||||||
@ -37,7 +69,7 @@ module.exports = defineConfig({
|
|||||||
minPixelValue: 1,
|
minPixelValue: 1,
|
||||||
mediaQuery: false,
|
mediaQuery: false,
|
||||||
replace: true,
|
replace: true,
|
||||||
exclude: /(\/|\\)(node_modules)(\/|\\)/,
|
exclude: /node_modules/,
|
||||||
include: [],
|
include: [],
|
||||||
landscape: false,
|
landscape: false,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user