Compare commits

..

7 Commits

Author SHA1 Message Date
2e606513f5 add 道路分析接口获取数据 2025-11-18 16:01:57 +08:00
1c76c7d23c add title 2025-11-17 09:28:04 +08:00
802df4d446 add title从ini取 2025-11-03 17:00:28 +08:00
46c6b952fc fix bug 2025-10-27 10:48:12 +08:00
a562e2cea8 add 样式 2025-10-27 09:46:51 +08:00
af252cd65e fix 2025-10-24 09:02:28 +08:00
444bded88f add 字体 2025-10-23 10:23:50 +08:00
13 changed files with 1018 additions and 1073 deletions

View File

@ -10,7 +10,7 @@
},
"dependencies": {
"@babel/preset-env": "^7.28.3",
"axios": "0.21.0",
"axios": "^1.13.2",
"echarts": "^5.4.3",
"element-ui": "2.9.2",
"file-saver": "^2.0.5",

View File

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

4
public/config/config.js Normal file
View File

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

View File

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

View File

@ -11,6 +11,7 @@
rel="stylesheet"
type="text/css"
/>
<script src="./config/config.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" />
<script src="./map/mars3d/mars3d.js" type="text/javascript"></script>

Binary file not shown.

View File

@ -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,
html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
Microsoft YaHei, Arial, sans-serif;
font-family: 'Dengxian' !important;
user-select: none;
scrollbar-width: none;
-ms-overflow-style: none;
@ -33,3 +79,20 @@ html {
.flex-warp {
flex-wrap: wrap;
}
.el-form-item {
display: flex!important;
justify-content: space-between;
margin-bottom: 12px!important;
}
.el-form-item__label {
letter-spacing: -1px;
min-width: 130px!important;
padding: 0!important;
white-space: nowrap; /* 强制不换行 */
flex-shrink: 0;
font-size: 16px!important;
}
.el-input__inner {
height: 24px;
line-height: 24px;
}

View File

@ -10,6 +10,9 @@ import 'vxe-pc-ui/es/style.css'
import VxeUITable from 'vxe-table'
import 'vxe-table/es/style.css'
import axios from 'axios'
Vue.prototype.$http = axios
Vue.config.productionTip = false

162
src/utils/map.js Normal file
View 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,
}

View File

@ -16,18 +16,22 @@
<script>
import {setStorage, getStorage} from '@/utils/localStorage.js'
import iniParser from 'ini-parser'
export default {
name: '',
data() {
return {
tabList: [
{id: 1, label: '机动路线规划'},
{id: 1, label: '道路通过性分析'},
// {id: 2, label: '临时部署驻地分析'},
],
activeIndex: 1,
}
},
created() {
this.getTitle()
},
mounted() {
this.activeIndex = getStorage('activeIndex') || 1
},
@ -36,6 +40,17 @@ export default {
setStorage('activeIndex', 1)
},
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) {
if (this.activeIndex !== id) {
this.activeIndex = id
@ -64,7 +79,7 @@ $label_height: 50px;
line-height: $label_height;
padding: 0 30px;
margin-right: 20px;
font-family: 'HarmonyOS Sans';
// font-family: 'HarmonyOS Sans';
font-weight: 400;
font-size: 20px;
cursor: pointer;

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,21 @@
<div>
<div class="home-header">
<div class="home-header-left">
<img @click="drawStartPoint" src="@/assets/image/start.png" />
<img @click="drawEndPoint" src="@/assets/image/end.png" />
<img @click="drawViaPoint" src="@/assets/image/add.png" />
<img @click="drawAvoidPoint" src="@/assets/image/avoidP.png" />
<img @click="drawAvoidArea" src="@/assets/image/updown.png" />
<el-tooltip content="起点绘制">
<img @click="drawStartPoint" src="@/assets/image/start.png" />
</el-tooltip>
<el-tooltip content="终点绘制">
<img @click="drawEndPoint" src="@/assets/image/end.png" />
</el-tooltip>
<el-tooltip content="途径点绘制">
<img @click="drawViaPoint" src="@/assets/image/add.png" />
</el-tooltip>
<el-tooltip content="避让点绘制">
<img @click="drawAvoidPoint" src="@/assets/image/avoidP.png" />
</el-tooltip>
<el-tooltip content="避让区域绘制">
<img @click="drawAvoidArea" src="@/assets/image/updown.png" />
</el-tooltip>
<div @click="clear" class="sure">清除</div>
<div @click="calculateShortestPath" class="sure">确定</div>
<div @click="hadBuffer" class="sure">路线隐蔽规划</div>
@ -32,12 +42,11 @@
<div class="title">参数</div>
<el-form
@submit.native.prevent="calculateShortestPath"
label-width="120px"
label-position="left"
size="mini"
:model="form"
>
<el-form-item label="起点">
<el-form-item label="起点">
<el-input
v-model="form.startPoint"
@blur="pointsChange('startPoint')"
@ -45,7 +54,7 @@
clearable
></el-input>
</el-form-item>
<el-form-item label="终点">
<el-form-item label="终点">
<el-input
v-model="form.endPoint"
@blur="pointsChange('endPoint')"
@ -53,7 +62,7 @@
clearable
></el-input>
</el-form-item>
<el-form-item label="途经点">
<el-form-item label="途经点">
<div v-for="(item, index) in form.viaPoints" :key="index">
<el-input
v-model="item.points"
@ -64,7 +73,7 @@
></el-input>
</div>
</el-form-item>
<el-form-item label="避让点">
<el-form-item label="避让点">
<div v-for="(item, index) in form.avoidPoints" :key="index">
<el-input
v-model="item.points"
@ -75,7 +84,7 @@
></el-input>
</div>
</el-form-item>
<el-form-item label="避让区域">
<el-form-item label="避让区域">
<div v-for="(item, index) in form.avoidAreas" :key="index">
<el-input
v-model="item.points"
@ -98,14 +107,13 @@
<div class="control-panel">
<div class="title">隐蔽添加</div>
<el-form
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-form-item>
<el-form-item label="面积冗余%">
<el-form-item label="面积冗余(%)">
<el-input v-model="hideform.redundancy" placeholder=""></el-input>
</el-form-item>
</el-form>
@ -120,17 +128,16 @@
</div>
<el-form
@submit.native.prevent="calculateShortestPath"
label-width="120px"
label-position="left"
size="mini"
>
<el-form-item label="宽度">
<el-form-item label="宽度(m)">
<el-input v-model="inputform.width"></el-input>
</el-form-item>
<el-form-item label="载重(吨)">
<el-form-item label="载重(吨)">
<el-input v-model="inputform.load" placeholder=""></el-input>
</el-form-item>
<el-form-item label="最小转弯半径">
<el-form-item label="最小转弯半径(m)">
<el-input
v-model="inputform.minTurnRadius"
placeholder=""
@ -477,6 +484,9 @@ export default {
pitch: -35,
},
},
defaultLabelStyle: {
font: '16px "DengXian",sans-serif',
}
// basemaps: [
// {
// id: "image-tdss",
@ -501,7 +511,7 @@ export default {
this.viewer.addLayer(this.accordFactoryLayer)
this.loadShapefile() //
//
this.viewer.on(mars3d.EventType.dblClick, (event) => {
this.viewer.on('rightClick', (event) => {
//
this.graphicLayer.stopDraw()
})
@ -605,7 +615,7 @@ export default {
const graphicLine = new window.mars3d.graphic.PolylineEntity({
positions: feature.geometry.coordinates[0],
style: {
color: currentColor, // 使
color: "#FF0000", // 使
width: 2,
outline: false,
},
@ -1079,7 +1089,7 @@ export default {
}
})
.then(response => {
this.$message.success('保存成功')
this.$message.success(`保存在${response.data.path}成功`)
})
.catch(error => {
});
@ -1278,10 +1288,16 @@ export default {
(viaPoint) => viaPoint.style.time !== row.time
)
} else {
const graphic = this.viaPoints.find(
(viaPoint) => viaPoint.style.time === row.time
)
this.updatePointPosition(graphic, row.points)
if (this.viaPoints.length > 0) {
const graphic = this.viaPoints.find(
(viaPoint) => viaPoint.style.time === row.time
)
this.updatePointPosition(graphic, row.points)
} else {
const time = new Date().getTime()
row.time = time
this.addPointToMap('viaPoints', row, time)
}
}
} else if (type === 'avoidPoints') {
if (!row.points) {
@ -1304,10 +1320,16 @@ export default {
(avoidPoint) => avoidPoint.style.time !== row.time
)
} else {
const graphic = this.avoidPoints.find(
(avoidPoint) => avoidPoint.style.time === row.time
)
this.updatePointPosition(graphic, row.points)
if (this.avoidPoints.length > 0) {
const graphic = this.avoidPoints.find(
(avoidPoint) => avoidPoint.style.time === row.time
)
this.updatePointPosition(graphic, row.points)
} else {
const time = new Date().getTime()
row.time = time
this.addPointToMap('avoidPoints', row, time)
}
}
} else if (type === 'avoidAreas') {
if (!row.points) {
@ -1330,10 +1352,16 @@ export default {
(avoidArea) => avoidArea.style.time !== row.time
)
} else {
const graphic = this.avoidAreas.find(
(avoidArea) => avoidArea.style.time === row.time
)
this.updatePolygonPosition(graphic, row.points)
if (this.avoidAreas.length > 0) {
const graphic = this.avoidAreas.find(
(avoidArea) => avoidArea.style.time === row.time
)
this.updatePolygonPosition(graphic, row.points)
} else {
const time = new Date().getTime()
row.time = time
this.addPolygonToMap('avoidAreas', row, time)
}
}
}
},
@ -1629,7 +1657,6 @@ export default {
const fullPath = []
const infoList = []
for (let i = 0; i < points.length - 1; i++) {
const currPoint = points[i]; // /
const nextPoint = points[i + 1]; // /
@ -1641,7 +1668,7 @@ export default {
let endEdgeInfo = this.findNearestEdge(segmentEnd, tempGraph, nodeCoords);
if (!startEdgeInfo || !endEdgeInfo) {
console.error('无法匹配到路网边')
console.error("无法匹配到路网边")
continue
}
//
@ -1851,10 +1878,22 @@ export default {
) {
const segmentPath = [];
const segments = [];
let hasValidSegment = false;
//
let viaPerpRemainingCoords = [];
// 1. -> 线
segmentPath.push(segmentStart);
segmentPath.push(startConnection.perpendicularPoint);
if (startConnection.type !== "via") {
//
segmentPath.push(segmentStart);
segmentPath.push(startConnection.perpendicularPoint);
// return { path: [], segments: [] };
} else {
//
segmentPath.push(segmentStart);
// segmentPath.push(startConnection.perpendicularPoint);
// return { path: [], segments: [] };
}
// 2. temp pathNodes temp -> temp
// 线 pathNodes pair
@ -1872,88 +1911,149 @@ export default {
}
// curNode nextNode feature
const segment = this.roadNetworkGeoJSON.features.find(f =>
(String(f.properties.FNODE_) === String(curNode) && String(f.properties.TNODE_) === String(nextNode)) ||
(String(f.properties.FNODE_) === String(nextNode) && String(f.properties.TNODE_) === String(curNode))
);
let segment = null;
if (!this.join) {
segment = this.roadNetworkGeoJSON.features.find(f =>
(String(f.properties.FNODE_) === String(curNode) && String(f.properties.TNODE_) === String(nextNode)) ||
(String(f.properties.FNODE_) === String(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) {
console.warn(`找不到符合条件的路段: ${curNode} -> ${nextNode}`);
//
continue;
}
segments.push(segment);
// 线LineString MultiLine
let segCoords = [];
if (segment.geometry.type === 'LineString') {
segCoords = segment.geometry.coordinates;
} else if (segment.geometry.type === 'MultiLineString') {
segCoords = segment.geometry.coordinates[0];
}
if (segment) {
hasValidSegment = true;
segments.push(segment);
// 线LineString MultiLine
let segCoords = [];
if (segment.geometry.type === 'LineString') {
segCoords = segment.geometry.coordinates;
} else if (segment.geometry.type === 'MultiLineString') {
segCoords = segment.geometry.coordinates[0];
}
// FNODE_ === curNode线 segCoords
const isForward = String(segment.properties.FNODE_) === String(curNode);
const coordsToUse = isForward ? segCoords : [...segCoords].reverse();
// FNODE_ === curNode线 segCoords
const isForward = String(segment.properties.FNODE_) === String(curNode);
const coordsToUse = isForward ? segCoords : [...segCoords].reverse();
// segmentPath coordsToUse
const lastPt = segmentPath[segmentPath.length - 1];
const firstOfSeg = coordsToUse[0];
// === 使 ===
const lastPt = segmentPath[segmentPath.length - 1];
const firstOfSeg = coordsToUse[0];
const distLastToFirst = this.calculateDistance(lastPt, firstOfSeg);
if (distLastToFirst < 1e-6) {
//
segmentPath.push(...coordsToUse.slice(1));
} else {
// coordsToUse lastPt
const nearestInfo = this.findNearestPointWithIndex(coordsToUse, lastPt);
const ni = nearestInfo.index;
if (ni === 0) {
//
segmentPath.push(...coordsToUse);
} else if (ni === coordsToUse.length - 1) {
//
const rev = [...coordsToUse].reverse();
// rev
if (this.calculateDistance(lastPt, rev[0]) < 1e-6) {
segmentPath.push(...rev.slice(1));
const distLastToFirst = this.calculateDistance(lastPt, firstOfSeg);
//
const endPerpNearest = this.findNearestPointWithIndex(coordsToUse, endConnection.perpendicularPoint);
const endNi = endPerpNearest.index;
if (distLastToFirst < 1e-6) {
//
//
if (endConnection.type === 'via') {
segmentPath.push(...coordsToUse.slice(1, endNi + 1));
//
viaPerpRemainingCoords = coordsToUse.slice(endNi);
} else {
//
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);
}
segmentPath.push(...coordsToUse.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);
// coordsToUse lastPt
const nearestInfo = this.findNearestPointWithIndex(coordsToUse, lastPt);
const ni = nearestInfo.index;
// === ===
if (endConnection.type === 'via') {
//
if (ni <= endNi) {
segmentPath.push(...coordsToUse.slice(ni, endNi + 1));
//
viaPerpRemainingCoords = coordsToUse.slice(endNi);
} else {
const reversedSlice = coordsToUse.slice(endNi, ni + 1).reverse();
segmentPath.push(...reversedSlice);
//
const originalRemaining = coordsToUse.slice(endNi);
viaPerpRemainingCoords = originalRemaining.reverse();
}
} else {
const toEnd = coordsToUse.slice(ni + 1);
segmentPath.push(...toEnd);
// 使
if (ni === 0) {
//
segmentPath.push(...coordsToUse);
} else if (ni === coordsToUse.length - 1) {
//
const rev = [...coordsToUse].reverse();
// rev
if (this.calculateDistance(lastPt, rev[0]) < 1e-6) {
segmentPath.push(...rev.slice(1));
} else {
//
segmentPath.push(coordsToUse[ni]);
//
const distToStart = this.calculateDistance(coordsToUse[ni], coordsToUse[0]);
const distToEnd = this.calculateDistance(coordsToUse[ni], coordsToUse[coordsToUse.length - 1]);
if (distToStart <= distToEnd) {
const toStart = coordsToUse.slice(0, ni).reverse();
segmentPath.push(...toStart);
} else {
const toEnd = coordsToUse.slice(ni + 1);
segmentPath.push(...toEnd);
}
}
} else {
//
segmentPath.push(coordsToUse[ni]);
const distToStart = this.calculateDistance(coordsToUse[ni], coordsToUse[0]);
const distToEnd = this.calculateDistance(coordsToUse[ni], coordsToUse[coordsToUse.length - 1]);
if (distToStart <= distToEnd) {
const toStart = coordsToUse.slice(0, ni).reverse();
segmentPath.push(...toStart);
} else {
const toEnd = coordsToUse.slice(ni + 1);
segmentPath.push(...toEnd);
}
}
}
}
}
}
//
if (!hasValidSegment) {
this.$message.warning('整段路径中没有找到任何符合条件的路段');
return { path: [], segments: [] };
}
// 3. -> 线
// === 线 ===
if (endConnection.type === 'end' || endConnection.type === 'via') {
// 线endConnection.lineCoords
// /
if (endConnection.type === 'end') {
const endLineCoords = endConnection.lineCoords;
const nearestIdx = this.findNearestIndexOnLine(endLineCoords, endConnection.perpendicularPoint).index;
const subLine = endLineCoords.slice(0, nearestIdx + 1); //
const lastPt = segmentPath[segmentPath.length - 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);
@ -1961,12 +2061,24 @@ export default {
// ->
segmentPath.push(endConnection.perpendicularPoint);
segmentPath.push(segmentEnd);
} else if (endConnection.type === 'via') {
segmentPath.push(endConnection.perpendicularPoint);
segmentPath.push(segmentEnd);
segmentPath.push(endConnection.perpendicularPoint);
//
// a.
if (viaPerpRemainingCoords.length > 1) {
//
segmentPath.push(...viaPerpRemainingCoords.slice(1));
}
} else {
//
//
segmentPath.push(endConnection.perpendicularPoint);
segmentPath.push(segmentEnd);
}
if (startConnection.type == 'via' && segmentPath.length > 0) {
segmentPath.shift(); //
}
return { path: segmentPath, segments };
},
@ -2111,23 +2223,6 @@ export default {
)
this.drawPath(route)
},
//
// drawPath(path) {
// const positions = path
// if (positions.fullPath.length == 0) return
// const polyline = new window.mars3d.graphic.PolylinePrimitive({
// positions: positions.fullPath,
// style: {
// clampToGround: true,
// color: '#55ff33',
// width: 8,
// },
// })
// this.shortestPathLayer.addGraphic(polyline)
// this.shortestPathList.push(path.infoList)
// this.infoList = path.infoList.map((item) => item.properties)
// },
// -
drawPath(path) {
const positions = path
@ -2435,7 +2530,7 @@ export default {
.then(response => {
this.jsonInfo = {
path: response.data.path,
json: JSON.stringify(response.data.json, null, 2)
json: JSON.stringify(this.decodeEscapedJson(response.data.json), null, 2)
}
this.jsonLoading = false
})

View File

@ -21,25 +21,35 @@ module.exports = defineConfig({
})
/* 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',
},
],
],
},
// 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: {
client: { overlay: false },
proxy: {
'/api': {
target: 'http://192.168.3.35:8381',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
})
},
},
css: {