Compare commits

22 Commits
Author SHA1 Message Date
gcw_IJ7DAiVL 2e606513f5 add 道路分析接口获取数据 2025-11-18 16:01:57 +08:00
gcw_IJ7DAiVL 1c76c7d23c add title 2025-11-17 09:28:04 +08:00
gcw_IJ7DAiVL 802df4d446 add title从ini取 2025-11-03 17:00:28 +08:00
gcw_IJ7DAiVL 46c6b952fc fix bug 2025-10-27 10:48:12 +08:00
gcw_IJ7DAiVL a562e2cea8 add 样式 2025-10-27 09:46:51 +08:00
gcw_IJ7DAiVL af252cd65e fix 2025-10-24 09:02:28 +08:00
gcw_IJ7DAiVL 444bded88f add 字体 2025-10-23 10:23:50 +08:00
gcw_IJ7DAiVL 964ce7cede fix bug修复 2025-10-16 16:02:23 +08:00
gcw_IJ7DAiVL ce70fd98ec add json编辑功能 2025-10-15 09:27:20 +08:00
gcw_IJ7DAiVL 7d22506f18 add json 编辑 2025-10-14 17:23:54 +08:00
gcw_IJ7DAiVL 328700ec89 fix 2025-10-10 18:31:44 +08:00
gcw_IJ7DAiVL 2ad6bc1b47 add 导出 2025-10-10 15:32:47 +08:00
gcw_IJ7DAiVL 41f9034f54 ## 机动路线规划 2025-10-10 11:38:34 +08:00
gcw_IJ7DAiVL ae2f9d94e1 fix 路线规划 隐蔽 2025-10-10 11:35:42 +08:00
gcw_IJ7DAiVL c1876d23bd add 隐蔽规划 2025-09-24 11:15:23 +08:00
yiqiuyang e2b73640df Merge branch 'main' of https://work.rangutech.cn:85/yiqiuyang/kxfx 2025-09-10 09:44:48 +08:00
yiqiuyang f084fc92e9 代码提交 2025-09-10 09:44:44 +08:00
gcw_IJ7DAiVL 3e7ccea682 fix 2025-09-10 09:34:52 +08:00
gcw_IJ7DAiVL 935846d7f2 fix 2025-09-10 09:13:58 +08:00
gcw_IJ7DAiVL cc774db444 fix 2025-09-10 08:55:33 +08:00
yiqiuyang c1370dcbc0 Merge branch 'main' of https://work.rangutech.cn:85/yiqiuyang/kxfx 2025-09-10 08:48:38 +08:00
yiqiuyang 8ea0f436b1 1 2025-09-10 08:47:28 +08:00
21 changed files with 7818 additions and 490 deletions
+1 -27
View File
@@ -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/).
+8 -3
View File
@@ -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
View File
@@ -0,0 +1,6 @@
[http]
port=8381
address=192.168.3.35
[title]
msg=道路堪选分析
+4
View File
@@ -0,0 +1,4 @@
window.config = {
// baseUrl: 'http://192.168.3.35:8381',
baseUrl: '/api'
}
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -15,7 +15,7 @@
"scene3DOnly": false, "scene3DOnly": false,
"shadows": false, "shadows": false,
"removeDblClick": true, "removeDblClick": true,
"sceneMode": 3, "sceneMode": 2,
"showSun": true, "showSun": true,
"showMoon": true, "showMoon": true,
"showSkyBox": true, "showSkyBox": true,
@@ -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
} }
+16 -8
View File
@@ -1,26 +1,34 @@
[ [
{ {
"id": 1, "id": 1,
"width": 30, "name": "车1",
"load": 500, "long": 10,
"width": 7,
"load": 3,
"minTurnRadius": 5 "minTurnRadius": 5
}, },
{ {
"id": 2, "id": 2,
"width": 10, "name": "车2",
"load": 600, "long": 10,
"width": 11,
"load": 1,
"minTurnRadius": 6 "minTurnRadius": 6
}, },
{ {
"id": 3, "id": 3,
"width": 40, "name": "车3",
"load": 700, "long": 10,
"width": 8,
"load": 7,
"minTurnRadius": 10 "minTurnRadius": 10
}, },
{ {
"id": 4, "id": 4,
"width": 20, "name": "车4",
"load": 700, "long": 10,
"width": 10,
"load": 5,
"minTurnRadius": 2 "minTurnRadius": 2
} }
] ]
+5 -21
View File
@@ -1,38 +1,22 @@
{ {
"startPoint": "114.262694,27.786216", "startPoint": "114.312888,27.796612",
"endPoint": "114.274729,27.777062", "endPoint": "114.336525,27.767989",
"viaPoints": [ "viaPoints": [
{ {
"time": "1694352000000", "time": "1694352000000",
"points": "114.263233,27.777047" "points": "114.334239,27.779261"
},
{
"time": "1694352001000",
"points": "114.265512,27.789935"
},
{
"time": "1694352002000",
"points": "114.256863,27.780437"
} }
], ],
"avoidPoints": [ "avoidPoints": [
{ {
"time": "1694352003000", "time": "1694352003000",
"points": "114.261166,27.800982" "points": ""
},
{
"time": "1694352004000",
"points": "114.251312,27.786932"
} }
], ],
"avoidAreas": [ "avoidAreas": [
{ {
"time": "1694352005000", "time": "1694352005000",
"points": "[[114.267217,27.777566,0],[114.276695,27.78048,0],[114.276001,27.772547,0],[114.276001,27.772486,0]]" "points": "[[114.272797,27.7935,0],[114.27268,27.794201,0],[114.27537,27.795838,0]]"
},
{
"time": "1694352006000",
"points": "[[114.272317,27.786501,0],[114.281341,27.79818,0],[114.290389,27.779594,0],[114.284888,27.773934,0]]"
} }
] ]
} }
+4
View File
@@ -0,0 +1,4 @@
{
"leftTop": "114.33,27.8",
"rightBottom": "114.34,27.79"
}
+1
View File
@@ -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>
Binary file not shown.
+65 -2
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, 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;
}
+3
View File
@@ -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 -1
View File
@@ -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
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,
}
+22 -3
View File
@@ -16,22 +16,41 @@
<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
}, },
beforeDestroy() {
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
@@ -60,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;
+921 -193
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+207 -183
View File
@@ -2,7 +2,7 @@
<div id="page"> <div id="page">
<div class="header"> <div class="header">
<div class="images flex a-c" v-for="item in imagesList" :key="item.id"> <div class="images flex a-c" v-for="item in imagesList" :key="item.id">
<img :src="item.src" /> <img :src="item.src" @click="drawRectangle" />
<el-button <el-button
class="form-btn" class="form-btn"
type="primary" type="primary"
@@ -11,7 +11,6 @@
>确定</el-button >确定</el-button
> >
</div> </div>
<el-button class="btn" type="primary" size="mini">确定</el-button>
</div> </div>
<div class="content flex j-s a-c"> <div class="content flex j-s a-c">
@@ -21,12 +20,21 @@
<div class="control-panel"> <div class="control-panel">
<div class="title">范围</div> <div class="title">范围</div>
<el-form-item label="左上角"> <el-form-item label="左上角">
<el-input v-model="form.leftTop"></el-input> <el-input v-model="form.leftTop" @blur="updateArea"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="右下角"> <el-form-item label="右下角">
<el-input v-model="form.rightBottom"></el-input> <el-input v-model="form.rightBottom" @blur="updateArea">
</el-input>
</el-form-item> </el-form-item>
<div class="importJson flex j-c a-c">导入json文件</div> <input
type="file"
ref="fileInput"
@change="handleFileUpload"
style="display: none"
/>
<div class="importJson flex j-c a-c" @click="triggerFileUpload">
导入json文件
</div>
</div> </div>
<!-- 权重 --> <!-- 权重 -->
@@ -66,50 +74,67 @@
</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"
v-if="roadLineList.length > 0" v-if="roadLineList.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="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>
@@ -125,115 +150,16 @@ 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: [],
id: 10001, waterPointList: [],
name: 'Test1', waterLineList: [],
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
],
roadLineList: [
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
],
waterPointList: [
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
],
waterLineList: [
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
{
id: 10001,
name: 'Test1',
role: 'Develop',
sex: 'Man',
age: 28,
address: 'test abc',
},
],
form: { form: {
leftTop: '114.33,27.8', leftTop: '',
rightBottom: '114.34, 27.79', rightBottom: '',
slopePer: 0.5, slopePer: 0.5,
peoplePer: 0.2, peoplePer: 0.2,
plantPer: 0.2, plantPer: 0.2,
@@ -245,9 +171,12 @@ export default {
}, },
analyzing: false, analyzing: false,
rectCount: 0, rectCount: 0,
isHand: false,
selectedRect: null, selectedRect: null,
rectangles: [], rectangles: [],
labels: [],
peopleGeo: null, peopleGeo: null,
plantGeo: null, plantGeo: null,
soilGeo: null, soilGeo: null,
@@ -283,22 +212,14 @@ export default {
}, },
}, },
watch: {
positions: {
deep: true,
handler(val) {
this.drawInitialArea(val)
},
},
},
mounted() { mounted() {
this.getMapJson() this.getMapJson()
this.getColorList() this.getColorList()
}, },
beforeDestroy() { beforeDestroy() {
delete window.mars3d delete window.viewer
delete window.graphicLayer
}, },
methods: { methods: {
@@ -321,6 +242,46 @@ export default {
}) })
}, },
// 导入json文件
triggerFileUpload() {
this.$refs.fileInput.click()
},
handleFileUpload(event) {
const file = event.target.files[0]
if (file) {
const reader = new FileReader()
reader.onload = (e) => {
try {
const uploadedJson = JSON.parse(e.target.result)
this.importJson(uploadedJson)
} catch (error) {
console.error('解析 JSON 文件失败:', error)
this.$message.error('解析 JSON 文件失败')
}
}
reader.readAsText(file)
}
},
async importJson(uploadedJson) {
if (!uploadedJson) {
this.$message.warning('请先选择一个 JSON 文件')
return
}
// 清除现有矩形
if (
this.polygon &&
(uploadedJson?.leftTop || uploadedJson?.rightBottom)
) {
window.graphicLayer.removeGraphic(this.polygon)
}
this.form.leftTop = uploadedJson.leftTop
this.form.rightBottom = uploadedJson.rightBottom
this.drawInitialArea()
},
getColorList() { getColorList() {
this.colorList = colorMap.createColormap({ this.colorList = colorMap.createColormap({
colormap: 'jet', colormap: 'jet',
@@ -342,7 +303,7 @@ export default {
window.shortestPathLayer = new window.mars3d.layer.GraphicLayer() window.shortestPathLayer = new window.mars3d.layer.GraphicLayer()
window.viewer.addLayer(window.shortestPathLayer) window.viewer.addLayer(window.shortestPathLayer)
this.drawInitialArea() // this.drawInitialArea()
this.loadGeoJson() this.loadGeoJson()
}, },
@@ -375,10 +336,12 @@ export default {
} }
// 默认只加载一个,其他按需加载 // 默认只加载一个,其他按需加载
this.loadLayers(['people', 'plant', 'soil'], (results) => { // this.loadLayers(['people', 'plant', 'soil'], (results) => {
console.log('所有图层加载完成', results) // console.log('所有图层加载完成', results)
}) // })
// this.loadLayer('people') this.loadLayer('people')
this.loadLayer('plant', false)
this.loadLayer('soil', false)
}, },
createLayerConfig(url, color, type = 'polygon') { createLayerConfig(url, color, type = 'polygon') {
@@ -449,7 +412,7 @@ export default {
return baseConfig return baseConfig
}, },
async loadLayer(layerName) { async loadLayer(layerName, isLoad = true) {
if (this.layers[layerName].layer) { if (this.layers[layerName].layer) {
this.layers[layerName].layer.show = !this.layers[layerName].layer.show this.layers[layerName].layer.show = !this.layers[layerName].layer.show
return return
@@ -459,11 +422,11 @@ export default {
this.layers[layerName].config this.layers[layerName].config
) )
await window.viewer.addLayer(this.layers[layerName].layer) isLoad ? await window.viewer.addLayer(this.layers[layerName].layer) : null
}, },
// 新增方法:同时加载多个图层 // 新增方法:同时加载多个图层
async loadLayers(names = [], cb) { async loadLayers(names = [], cb, show = true) {
const tasks = names.map((name) => { const tasks = names.map((name) => {
const cfg = this.layers[name] const cfg = this.layers[name]
if (!cfg) return Promise.resolve(null) // 无配置直接过 if (!cfg) return Promise.resolve(null) // 无配置直接过
@@ -544,14 +507,20 @@ export default {
}, },
// 判断矩形与 mars3d GeoJSON 图层是否相交(不转 GeoJSON) // 判断矩形与 mars3d GeoJSON 图层是否相交(不转 GeoJSON)
getIntersectId(position, layer, bufferDistance = 0.001) { getIntersectId(position, layer) {
console.log('position===>', position, layer) console.log('position===>', position, layer)
if (!position || !layer || !layer.graphics) return null if (!position || !layer || !layer.graphics) return null
const rectCoords = position.concat([position[0]]) // 闭合 const rectCoords = position.concat([position[0]]) // 闭合
console.log('rectCoords===>', rectCoords)
const rectPoly = turf.polygon([rectCoords]) const rectPoly = turf.polygon([rectCoords])
console.log('rectPoly===>', rectPoly)
const [minX, minY, maxX, maxY] = turf.bbox(rectPoly) // 矩形 bbox const [minX, minY, maxX, maxY] = turf.bbox(rectPoly) // 矩形 bbox
console.log('minX, minY, maxX, maxY===>', minX, minY, maxX, maxY)
let fc = this._polyCache.get(layer) let fc = this._polyCache.get(layer)
if (!fc) { if (!fc) {
@@ -636,7 +605,13 @@ export default {
// 手动绘制矩形 // 手动绘制矩形
async drawRectangle(clampToGround) { async drawRectangle(clampToGround) {
const graphic = await window.graphicLayer.startDraw({ this.clearRectangles()
if (this.polygon) {
window.graphicLayer.removeGraphic(this.polygon)
}
this.isHand = true
let isEntityGraphic = false
this.polygon = await window.graphicLayer.startDraw({
type: isEntityGraphic ? 'rectangle' : 'rectangleP', type: isEntityGraphic ? 'rectangle' : 'rectangleP',
style: { style: {
color: clampToGround ? '#ffff00' : '#3388ff', color: clampToGround ? '#ffff00' : '#3388ff',
@@ -648,7 +623,9 @@ export default {
}, },
}) })
console.log('标绘完成', graphic.toJSON()) let positions = this.polygon.toJSON().positions
this.form.leftTop = positions[0][0] + ',' + positions[0][1]
this.form.rightBottom = positions[1][0] + ',' + positions[1][1]
}, },
// 绘制矩形区域 // 绘制矩形区域
@@ -660,7 +637,7 @@ export default {
Cesium.Cartesian3.fromDegrees(p[0], p[1], 0) Cesium.Cartesian3.fromDegrees(p[0], p[1], 0)
), ),
style: { style: {
color: '#00ff00', color: '#ffff00',
opacity: 0.3, opacity: 0.3,
outline: true, outline: true,
outlineColor: '#ffffff', outlineColor: '#ffffff',
@@ -687,11 +664,15 @@ export default {
} }
}, },
updateArea(val) { updateArea() {
if (this.isHand) {
this.isHand = false
return
}
if (!this.polygon) return if (!this.polygon) return
// 将经纬度转换为笛卡尔坐标 // 将经纬度转换为笛卡尔坐标
const cartesianPositions = val.map((p) => const cartesianPositions = this.positions.map((p) =>
Cesium.Cartesian3.fromDegrees(p[0], p[1], 0) Cesium.Cartesian3.fromDegrees(p[0], p[1], 0)
) )
@@ -703,11 +684,15 @@ export default {
this.polygon.update() this.polygon.update()
} }
console.log('区域已更新', val) console.log('区域已更新', this.positions)
}, },
// 分析平均坡度 // 分析平均坡度
async analyzeAverageSlope() { async analyzeAverageSlope() {
if (!this.form.leftTop || !this.form.rightBottom) {
return this.$message.warning('请选择范围!')
}
console.log('开始坡度分析') console.log('开始坡度分析')
this.analyzing = true this.analyzing = true
this.selectedRect = null this.selectedRect = null
@@ -715,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
@@ -844,8 +831,9 @@ export default {
} }
} catch (e) { } catch (e) {
console.error('坡度分析失败', e) console.error('坡度分析失败', e)
alert('坡度分析失败,请检查地形数据') this.$message.error('坡度分析失败,请检查地形数据')
} finally { } finally {
rectInfoMap.clear()
this.drawLabelAndRec() this.drawLabelAndRec()
} }
}, },
@@ -919,32 +907,26 @@ export default {
} }
/* 3. 批量创建 Graphic(底层一次性构造,避免反复 new) */ /* 3. 批量创建 Graphic(底层一次性构造,避免反复 new) */
const rectGraphics = rectOpts.map( this.rectangles = rectOpts.map(
(opt) => new mars3d.graphic.PolygonEntity(opt) (opt) => new mars3d.graphic.PolygonEntity(opt)
) )
const labelGraphics = labelOpts.map( this.labels = labelOpts.map((opt) => new mars3d.graphic.LabelEntity(opt))
(opt) => new mars3d.graphic.LabelEntity(opt)
)
/* 4. 一次性添加到图层(mars3d 支持数组)*/ /* 4. 一次性添加到图层(mars3d 支持数组)*/
window.graphicLayer.addGraphic(rectGraphics) window.graphicLayer.addGraphic(this.rectangles)
window.graphicLayer.addGraphic(labelGraphics) window.graphicLayer.addGraphic(this.labels)
/* 5. 图层级事件委托:只监听矩形图形的点击事件 */ /* 5. 图层级事件委托:只监听矩形图形的点击事件 */
window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this) // 先清旧监听 window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this) // 先清旧监听
// 为每个矩形图形单独绑定点击事件 // 为每个矩形图形单独绑定点击事件
rectGraphics.forEach((rectGraphic) => { this.rectangles.forEach((rectGraphic) => {
rectGraphic.on(mars3d.EventType.click, (event) => { rectGraphic.on(mars3d.EventType.click, (event) => {
this._selectRect(event) this._selectRect(event)
}) })
}) })
/* 6. 保存引用 */ /* 6. 事件处理函数 */
this.rectangles = rectGraphics
this.labels = labelGraphics
/* 7. 事件处理函数 */
this._selectRect = (event) => { this._selectRect = (event) => {
const graphic = event.graphic const graphic = event.graphic
// 确保只有矩形图形会触发选择 // 确保只有矩形图形会触发选择
@@ -952,6 +934,7 @@ export default {
this.selectRectangle(graphic) this.selectRectangle(graphic)
} }
} }
this.$message.success('计算成功!')
}, },
/** /**
@@ -1052,21 +1035,21 @@ 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,
center: rect.center, center: rect.center,
} }
if (!this.roadPointGeo) {
this.loadLayer('roadPoint') this.roadPointList = []
} else if (!this.roadLineGeo) { this.roadLineList = []
this.loadLayer('roadLine') this.waterPointList = []
} else if (!this.waterPointGeo) { this.waterLineList = []
this.loadLayer('waterPoint')
} else if (!this.waterLineGeo) {
this.loadLayer('waterLine')
}
if (!this.roadWaterLock) { if (!this.roadWaterLock) {
this.loadLayers( this.loadLayers(
@@ -1082,38 +1065,69 @@ export default {
}, },
getRoadWaterIds(rect) { getRoadWaterIds(rect) {
const positions = rect.points.map((d) => [d.lng, d.lat])
let roadPointId = this.getIntersectId( let roadPointId = this.getIntersectId(
rect.points, positions,
this.layers['roadPoint'].layer this.layers['roadPoint'].layer
) )
let roadLineId = this.getIntersectId( let roadLineId = this.getIntersectId(
rect.points, positions,
this.layers['roadLine'].layer this.layers['roadLine'].layer
) )
let waterPointId = this.getIntersectId( let waterPointId = this.getIntersectId(
rect.points, positions,
this.layers['waterPoint'].layer this.layers['waterPoint'].layer
) )
let waterLineId = this.getIntersectId( let waterLineId = this.getIntersectId(
rect.points, positions,
this.layers['waterLine'].layer this.layers['waterLine'].layer
) )
if (roadPointId) {
this.roadPointList.push(
this.layers['roadPoint'].layer.getGraphicById(roadPointId).options
.attr
)
} else if (roadLineId) {
this.roadLineList.push(
this.layers['roadLine'].layer.getGraphicById(roadLineId).options.attr
)
} else if (waterPointId) {
this.waterPointList.push(
this.layers['waterPoint'].layer.getGraphicById(waterPointId).options
.attr
)
} else if (waterLineId) {
this.waterLineList.push(
this.layers['waterLine'].layer.getGraphicById(waterLineId).options
.attr
)
}
console.log( console.log(
'roadPointId===>', 'roadPointId===>',
roadPointId, this.roadPointList,
roadLineId, this.roadLineList,
waterPointId, this.waterPointList,
waterLineId this.waterLineList
) )
}, },
// 清除所有矩形 // 清除所有矩形
clearRectangles() { clearRectangles() {
this.rectangles.forEach((rect) => { if (this.rectangles.length > 0) {
window.graphicLayer.removeGraphic(rect) this.rectangles.forEach((item) => item.remove())
}) window.graphicLayer.removeGraphic(this.rectangles)
this.rectangles = [] this.rectangles = []
this.rectCount = 0
this.validBlocks = []
}
if (this.labels.length > 0) {
this.labels.forEach((item) => item.remove())
window.graphicLayer.removeGraphic(this.labels)
this.labels = []
}
}, },
}, },
} }
@@ -1202,11 +1216,21 @@ export default {
width: 452px; width: 452px;
height: 100%; height: 100%;
background-color: #d4e5db; background-color: #d4e5db;
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>
+47 -15
View File
@@ -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: { devServer: {client: {overlay: false}},
client: {
overlay: false, configureWebpack(config) {
}, // 调试
},
configureWebpack: (config) => {
//调试JS
config.devtool = 'source-map' config.devtool = 'source-map'
config.resolve = { // 别名
alias: { config.resolve.alias['@'] = path.resolve(__dirname, 'src')
'@': 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: {
client: { overlay: false },
proxy: {
'/api': {
target: 'http://192.168.3.35:8381',
changeOrigin: true,
pathRewrite: { '^/api': '' },
}, },
},
},
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,
}), }),