最新代码

This commit is contained in:
yiqiuyang
2025-09-10 04:16:41 +08:00
parent ab310c6b48
commit 72de8a9b31
10 changed files with 2355 additions and 218 deletions

View File

@ -50,6 +50,6 @@ export default {
#router {
width: 100%;
height: calc(100% - 74px);
border: 1px solid red;
overflow-y: hidden;
}
</style>

View File

@ -1,18 +1,27 @@
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false;
Vue.use(ElementUI);
import VxeUIAll from 'vxe-pc-ui'
import 'vxe-pc-ui/es/style.css'
const bus = new Vue();
Vue.prototype.$bus = bus;
import VxeUITable from 'vxe-table'
import 'vxe-table/es/style.css'
Vue.config.productionTip = false
Vue.use(VxeUIAll)
Vue.use(VxeUITable)
Vue.use(ElementUI)
const bus = new Vue()
Vue.prototype.$bus = bus
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
}).$mount('#app')

View File

@ -126,7 +126,7 @@ export default {
},
async mounted() {
await this.getMapOption()
this.initMap()
// this.initMap()
},
methods: {
async getMapOption() {
@ -616,15 +616,10 @@ export default {
}
.control-panel .title {
/* text-align: center; */
margin-bottom: 10px;
color: #1c1c1c;
}
.importJson {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background: #2f705f;
border-radius: 4px;
color: #fff;
@ -660,4 +655,4 @@ export default {
height: 100%;
border: 1px solid #333;
}
</style>
</style>

View File

@ -1,120 +1,116 @@
<template>
<div id="page">
<div class="header">
<div class="images" 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" />
<el-button
class="form-btn"
type="primary"
size="mini"
@click="analyzeAverageSlope"
>确定</el-button
>
</div>
<el-button class="btn" type="primary" size="mini">确定</el-button>
</div>
<div class="content flex j-s a-c">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
<div class="left">
<el-form label-width="80px" label-position="left" size="small">
<!-- 范围 -->
<div class="control-panel">
<div class="title">范围</div>
<el-form-item label="左上角">
<el-input v-model="form.startPoint"></el-input>
</el-form-item>
<el-form-item label="右下角">
<el-input v-model="form.endPoint"></el-input>
</el-form-item>
<div class="importJson flex j-c a-c">导入json文件</div>
</div>
<!-- <div id="cesiumContainer" class="mars-content" ref="marsMap"></div> -->
<!-- 权重 -->
<div class="control-panel">
<div class="title">权重</div>
<el-form-item label="坡度">
<el-input v-model="form.slopePer"></el-input>
</el-form-item>
<el-form-item label="居民地">
<el-input v-model="form.peoplePer"></el-input>
</el-form-item>
<el-form-item label="植被">
<el-input v-model="form.plantPer"></el-input>
</el-form-item>
<el-form-item label="土壤">
<el-input v-model="form.soilPer"></el-input>
</el-form-item>
</div>
<div class="panel">
<h2 class="panel-title">坡度分析控制面板</h2>
<!-- 条件 -->
<div class="control-panel">
<div class="title">条件</div>
<el-form-item label="长">
<el-input v-model="form.xLength"></el-input>
</el-form-item>
<el-form-item label="宽">
<el-input v-model="form.yLength"></el-input>
</el-form-item>
<el-form-item label="最大坡度">
<el-input v-model="form.maxSlope"></el-input>
</el-form-item>
<el-form-item label="居民地最小距离">
<el-input v-model="form.minPeopleDis"></el-input>
</el-form-item>
</div>
</el-form>
</div>
<div class="center" id="cesiumContainer"></div>
<div class="input-group">
<label for="length">矩形长度经度方向分割数</label>
<input
type="number"
id="length"
v-model="length"
min="2"
max="20"
placeholder="请输入长度分割数"
/>
</div>
<div class="input-group">
<label for="width">矩形宽度纬度方向分割数</label>
<input
type="number"
id="width"
v-model="width"
min="2"
max="20"
placeholder="请输入宽度分割数"
/>
</div>
<div class="input-group">
<label for="width">最大坡度</label>
<input
type="number"
id="width"
v-model="maxSlope"
min="2"
max="20"
placeholder="请输入宽度分割数"
/>
</div>
<div class="input-group">
<label for="width">最小居民区</label>
<input
type="number"
id="width"
v-model="minPeopleDis"
min="2"
max="20"
placeholder="请输入宽度分割数"
/>
</div>
<!-- 权重 -->
<div class="input-group">
<label for="width">坡度</label>
<input
type="number"
id="width"
v-model="slotPer"
min="2"
max="20"
placeholder="请输入宽度分割数"
/>
</div>
<div class="input-group">
<label for="width">居民区</label>
<input
type="number"
id="width"
v-model="peoplePer"
min="2"
max="20"
placeholder="请输入宽度分割数"
/>
</div>
<button @click="analyzeAverageSlope">计算平均坡度</button>
<div v-if="analyzing" class="loading">
<div class="loading-spinner"></div>
<span>分析中请稍候...</span>
</div>
<div v-if="selectedRect?.id" class="result-container">
<h3 class="result-title">选中矩形信息</h3>
<div class="result-item">
<span class="result-label">矩形ID:</span>
<span class="result-value">{{ selectedRect.id }}</span>
</div>
<div class="result-item">
<span class="result-label">平均坡度:</span>
<span class="result-value">{{ selectedRect.slope }}°</span>
</div>
</div>
<div class="instructions">
<h3>使用说明</h3>
<ul>
<li>输入长度和宽度分割数2-20之间</li>
<li>点击"计算平均坡度"按钮开始分析</li>
<li>地图上将显示分割后的矩形区域</li>
<li>点击任意矩形查看其详细坡度信息</li>
<li>坡度值越大表示地形越陡峭</li>
</ul>
<div class="right flex column j-s">
<!-- 路点 -->
<vxe-table
class="item"
:data="roadPointList"
v-if="roadPointList.length > 0"
>
<vxe-column type="seq" width="70"></vxe-column>
<vxe-column field="name" title="Name"></vxe-column>
<vxe-column field="sex" title="Sex"></vxe-column>
<vxe-column field="age" title="Age"></vxe-column>
</vxe-table>
<!-- 路线 -->
<vxe-table
class="item"
:data="roadLineList"
v-if="roadLineList.length > 0"
>
<vxe-column type="seq" width="70"></vxe-column>
<vxe-column field="name" title="Name"></vxe-column>
<vxe-column field="sex" title="Sex"></vxe-column>
<vxe-column field="age" title="Age"></vxe-column>
</vxe-table>
<!-- 水点 -->
<vxe-table
class="item"
:data="waterPointList"
v-if="waterPointList.length > 0"
>
<vxe-column type="seq" width="70"></vxe-column>
<vxe-column field="name" title="Name"></vxe-column>
<vxe-column field="sex" title="Sex"></vxe-column>
<vxe-column field="age" title="Age"></vxe-column>
</vxe-table>
<!-- 水线 -->
<vxe-table
class="item"
:data="waterLineList"
v-if="waterLineList.length > 0"
>
<vxe-column type="seq" width="70"></vxe-column>
<vxe-column field="name" title="Name"></vxe-column>
<vxe-column field="sex" title="Sex"></vxe-column>
<vxe-column field="age" title="Age"></vxe-column>
</vxe-table>
</div>
</div>
</div>
@ -122,23 +118,138 @@
<script>
import {getStorage} from '@/utils/localStorage'
import mapJson from '/public/config/map.json'
import plantJson from '/public/config/plant.json'
import soilJson from '/public/config/soil.json'
export default {
name: '',
data() {
return {
imagesList: [{id: 1, src: require('@/assets/image/crop.png')}],
length: 3,
width: 3,
roadPointList: [
{
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',
},
],
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: {
leftTop: '',
rightBottom: '',
DEM: '',
peopleGeoJson: '',
soilGeoJson: '',
plantGeoJson: '',
slopePer: 0.5,
peoplePer: 0.2,
plantPer: 0.2,
soilPer: 0.1,
xLength: 3,
yLength: 3,
maxSlope: 6,
minPeopleDis: 1,
},
analyzing: false,
rectCount: 0,
maxSlope: 6,
minPeopleDis: 1,
slotPer: 0.5,
peoplePer: 0.2,
plantPer: 0.2,
soilPer: 0.1,
selectedRect: null,
rectangles: [],
// 示例区域:矩形四个角点(经纬度)
@ -153,6 +264,11 @@ export default {
soilGeo: null,
plantJson: null,
soilJson: null,
roadPointGeo: null,
roadLineGeo: null,
waterPointGeo: null,
waterLineGeo: null,
minDistance: null,
_polyCache: null,
validBlocks: [],
@ -163,7 +279,7 @@ export default {
this._polyCache = new WeakMap()
},
mounted() {
// this.getMapJson()
this.getMapJson()
this.getColorList()
},
@ -174,32 +290,21 @@ export default {
methods: {
// 获取地图配置
getMapJson() {
window.viewerOptions = mapJson
this.initMarsMap()
this.getJson()
},
// 获取 json 文件
getJson() {
fetch('./config/plant.json')
.then((res) => {
// 先确认成功
if (!res.ok) throw new Error(res.status)
return res.json()
})
.then((data) => {
this.plantJson = data
})
fetch('./config/soil.json')
.then((res) => {
// 先确认成功
if (!res.ok) throw new Error(res.status)
return res.json()
})
.then((data) => {
this.soilJson = data
})
window.mars3d.Util.fetchJson({
url: `./config/map.json`,
}).then((res) => {
let mapJSonStr = getStorage('localmap')
let mapJson
if (mapJSonStr) {
mapJson = mars3d.Util.merge(res.map3d, mapJSonStr)
} else {
mapJson = res.map3d
}
this.viewerOptions = mapJson
this.plantJson = plantJson
this.soilJson = soilJson
this.initMarsMap()
})
},
getColorList() {
@ -215,7 +320,7 @@ export default {
initMarsMap() {
window.viewer = new window.mars3d.Map(
'cesiumContainer',
window.viewerOptions
this.viewerOptions
)
window.graphicLayer = new window.mars3d.layer.GraphicLayer()
@ -227,29 +332,78 @@ export default {
this.loadGeoJson()
},
// 加载 geojson 数据
loadGeoJson() {
this.peopleGeo = new mars3d.layer.GeoJsonLayer({
url: './config/people.geojson',
crs: 'EPSG:4326',
flyTo: true,
})
// 延迟加载,按需显示
this.layers = {
people: this.createLayerConfig('./config/people.geojson', '#ff000033'),
plant: this.createLayerConfig('./config/plant.geojson', '#00ff0033'),
soil: this.createLayerConfig('./config/soil.geojson', '#0000ff33'),
roadPoint: this.createLayerConfig(
'./config/road_points.geojson',
'#ff0ff022'
),
roadLine: this.createLayerConfig(
'./config/road_lines.geojson',
'#ff0ff022'
),
waterPoint: this.createLayerConfig(
'./config/water_points.geojson',
'#ff0ff022'
),
waterLine: this.createLayerConfig(
'./config/water_lines.geojson',
'#ff0ff022'
),
}
this.plantGeo = new mars3d.layer.GeoJsonLayer({
url: './config/plant.geojson',
crs: 'EPSG:4326',
flyTo: true,
})
// 默认只加载一个,其他按需加载
this.loadLayer('people')
this.loadLayer('plant')
this.loadLayer('soil')
},
this.soilGeo = new mars3d.layer.GeoJsonLayer({
url: './config/soil.geojson',
crs: 'EPSG:4326',
flyTo: true,
})
createLayerConfig(url, color) {
return {
layer: null,
config: {
url: url,
crs: 'EPSG:4326',
flyTo: true, // 关闭自动飞行
symbol: {
type: 'polygon',
styleOptions: {
color: Cesium.Color.fromCssColorString(color),
opacity: 0.3,
outline: false,
// outlineColor: color,
// outlineWidth: 1,
},
},
// 视域裁剪,只显示视野内的要素
cull: {
enabled: true,
cullFace: 'back',
},
// 简化参数
simplify: {
enabled: true,
tolerance: 0.0001,
},
},
}
},
window.viewer.addLayer(this.peopleGeo)
window.viewer.addLayer(this.plantGeo)
window.viewer.addLayer(this.soilGeo)
async loadLayer(layerName) {
if (this.layers[layerName].layer) {
this.layers[layerName].layer.show = !this.layers[layerName].layer.show
return
}
this.layers[layerName].layer = new mars3d.layer.GeoJsonLayer(
this.layers[layerName].config
)
await window.viewer.addLayer(this.layers[layerName].layer)
},
// 算矩形到 geoJSONLayer 的最小距离(米),判断是否在其内部
@ -265,7 +419,7 @@ export default {
const polygons = geoJSONLayer
.getGraphics()
.filter((g) => g.type === 'polygonP')
.filter((g) => g.type === 'polygon')
polygons.forEach((g) => {
const lnglats = g.positions.map((p) => {
@ -352,7 +506,7 @@ export default {
drawInitialArea() {
const polygon = new mars3d.graphic.PolygonEntity({
positions: this.positions.map((p) =>
Cesium.Cartesian3.fromDegrees(p[0], p[1])
Cesium.Cartesian3.fromDegrees(p[0], p[1], 100)
),
style: {
color: '#00ff00',
@ -385,8 +539,8 @@ export default {
this.clearRectangles()
// 获取分割参数
const xSplit = parseInt(this.length) || 5
const ySplit = parseInt(this.width) || 5
const xSplit = parseInt(this.form.xLength) || 3
const ySplit = parseInt(this.form.yLength) || 3
// 计算区域边界
const lats = this.positions.map((p) => p[1])
@ -460,25 +614,36 @@ export default {
]
const slope = slopes[index]
const distance = this.calcMinDistance(
rectPos,
this.layers['people'].layer
)
const distance = this.calcMinDistance(rectPos, this.peopleGeo)
if (slope < this.maxSlope && distance > this.minPeopleDis) {
if (
slope < this.form.maxSlope &&
distance > this.form.minPeopleDis
) {
console.log('111===>', slope, distance)
const soilGeoId = this.getIntersectId(rectPos, this.soilGeo)
const plantGeoId = this.getIntersectId(rectPos, this.plantGeo)
const soilGeoId = this.getIntersectId(
rectPos,
this.layers['soil'].layer
)
const plantGeoId = this.getIntersectId(
rectPos,
this.layers['plant'].layer
)
const plantScore = plantGeoId
? this.getScore(
this.plantGeo.getGraphicById(plantGeoId).options.attr[
'编码'
],
this.layers['plant'].layer.getGraphicById(plantGeoId)
.options.attr['编码'],
this.plantJson
)
: 0
const soilScore = soilGeoId
? this.getScore(
this.soilGeo.getGraphicById(soilGeoId).options.attr['编码'],
this.layers['soil'].layer.getGraphicById(soilGeoId).options
.attr['编码'],
this.soilJson
)
: 1
@ -535,7 +700,6 @@ export default {
outlineColor: '#ffffff',
outlineWidth: 1,
},
attr: b, // 整个对象挂进去
center: new mars3d.LngLatPoint(b.center[0], b.center[1], 100),
})
@ -570,8 +734,8 @@ export default {
window.graphicLayer.addGraphic(labelGraphics)
/* 5. 图层级事件委托:只监听一次 */
window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this) // 先清旧监听
window.graphicLayer.on(mars3d.EventType.click, this._selectRect, this)
window.graphicLayer.off(mars3d.EventType.click, _selectRect, this) // 先清旧监听
window.graphicLayer.on(mars3d.EventType.click, _selectRect, this)
/* 6. 保存引用 */
this.rectangles = rectGraphics
@ -612,12 +776,12 @@ export default {
const isDistanceRangeSmall = distanceRange < 0.0001
// 设置权重
const slopePer = 0.3
const distancePer = 0.3
const peoplePer = 0.2
const soilPer = 0.2
const slopePer = this.form.slopePer
const peoplePer = this.form.peoplePer
const plantPer = this.form.plantPer
const soilPer = this.form.soilPer
const defaultDistanceScore = 0.5 * distancePer
const defaultPeoplePer = 0.5 * peoplePer
// 预分配结果数组
const results = new Array(data.length)
@ -633,26 +797,26 @@ export default {
: 0.5 * slopePer // 处理slope范围很小的情况
// 计算distance分数
const distanceScore = isDistanceRangeSmall
? defaultDistanceScore
: ((item.distance - minDistance) / distanceRange) * distancePer
const peopleScore = isDistanceRangeSmall
? defaultPeoplePer
: ((item.distance - minDistance) / distanceRange) * peoplePer
// 计算其他分数
const peopleScore = (item.plantScore || 0) * peoplePer
const plantScore = (item.plantScore || 0) * plantPer
const soilScore = item.soilScore * soilPer
const totalScore = (
slopeScore +
distanceScore +
peopleScore +
plantScore +
soilScore
).toFixed(2)
results[i] = {
...item,
slopeScore,
distanceScore,
peopleScore,
plantScore,
soilScore,
totalScore,
}
@ -683,13 +847,24 @@ export default {
// 选择矩形
selectRectangle(rect) {
console.log('rect===>', rect)
this.selectedRect = {
id: rect.id,
slope: rect.attr.slope,
center: rect.center,
}
console.log('rect.attr===>', rect.attr)
if (
!this.roadPointGeo ||
!this.roadLineGeo ||
!this.waterPointGeo ||
!this.waterLineGeo
) {
this.loadLayer('roadPoint')
this.loadLayer('roadLine')
this.loadLayer('waterPoint')
this.loadLayer('waterLine')
}
// 关闭之前的Popup如果存在
if (this.selectedPopup) {
@ -738,6 +913,11 @@ export default {
border-radius: 4px;
}
::v-deep .el-form-item--mini.el-form-item,
.el-form-item--small.el-form-item {
margin-bottom: 8px;
}
#page {
width: 100%;
height: 100%;
@ -757,6 +937,9 @@ export default {
cursor: pointer;
margin-left: 32px;
}
.form-btn {
margin-left: 30px;
}
.btn {
position: absolute;
top: 50%;
@ -768,10 +951,34 @@ export default {
.content {
width: 100%;
height: calc(100% - 60px);
.left {
width: 348px;
height: 100%;
background-color: #d4e5db;
.control-panel {
width: 340px;
padding: 5px 20px;
box-sizing: border-box;
background-size: cover;
background: #d4e5db;
.title {
font-size: 16px;
margin-bottom: 5px;
color: #1c1c1c;
}
}
.importJson {
background: #2f705f;
border-radius: 4px;
color: #fff;
width: 260px;
height: 24px;
font-size: 14px;
text-align: center;
padding: 3px 0;
}
}
.center {
@ -783,6 +990,11 @@ export default {
width: 452px;
height: 100%;
background-color: #d4e5db;
overflow: auto;
.item {
margin-bottom: 10px;
}
}
}
</style>