This commit is contained in:
yiqiuyang
2025-09-10 06:40:30 +08:00
parent 72de8a9b31
commit b6138fc7a4

View File

@ -21,10 +21,10 @@
<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.startPoint"></el-input> <el-input v-model="form.leftTop"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="右下角"> <el-form-item label="右下角">
<el-input v-model="form.endPoint"></el-input> <el-input v-model="form.rightBottom"></el-input>
</el-form-item> </el-form-item>
<div class="importJson flex j-c a-c">导入json文件</div> <div class="importJson flex j-c a-c">导入json文件</div>
</div> </div>
@ -232,12 +232,8 @@ export default {
], ],
form: { form: {
leftTop: '', leftTop: '114.33,27.8',
rightBottom: '', rightBottom: '114.34, 27.79',
DEM: '',
peopleGeoJson: '',
soilGeoJson: '',
plantGeoJson: '',
slopePer: 0.5, slopePer: 0.5,
peoplePer: 0.2, peoplePer: 0.2,
plantPer: 0.2, plantPer: 0.2,
@ -252,13 +248,6 @@ export default {
selectedRect: null, selectedRect: null,
rectangles: [], rectangles: [],
// 示例区域:矩形四个角点(经纬度)
positions: [
[114.33, 27.79],
[114.34, 27.79],
[114.34, 27.8],
[114.33, 27.8],
],
peopleGeo: null, peopleGeo: null,
plantGeo: null, plantGeo: null,
soilGeo: null, soilGeo: null,
@ -273,11 +262,36 @@ export default {
_polyCache: null, _polyCache: null,
validBlocks: [], validBlocks: [],
colorList: [], colorList: [],
roadWaterLock: false,
} }
}, },
created() { created() {
this._polyCache = new WeakMap() this._polyCache = new WeakMap()
}, },
computed: {
// 示例区域:矩形四个角点
positions() {
let a = this.form.leftTop.split(',')
let b = this.form.rightBottom.split(',')
return [
[a[0], b[1]],
[b[0], b[1]],
[b[0], a[1]],
[a[0], a[1]],
]
},
},
watch: {
positions: {
deep: true,
handler(val) {
this.drawInitialArea(val)
},
},
},
mounted() { mounted() {
this.getMapJson() this.getMapJson()
this.getColorList() this.getColorList()
@ -335,50 +349,45 @@ export default {
loadGeoJson() { loadGeoJson() {
// 延迟加载,按需显示 // 延迟加载,按需显示
this.layers = { this.layers = {
people: this.createLayerConfig('./config/people.geojson', '#ff000033'), people: this.createLayerConfig('./config/people.geojson', '#ff0000'),
plant: this.createLayerConfig('./config/plant.geojson', '#00ff0033'), plant: this.createLayerConfig('./config/plant.geojson', '#00ff00'),
soil: this.createLayerConfig('./config/soil.geojson', '#0000ff33'), soil: this.createLayerConfig('./config/soil.geojson', '#0000ff'),
roadPoint: this.createLayerConfig( roadPoint: this.createLayerConfig(
'./config/road_points.geojson', './config/road_points.geojson',
'#ff0ff022' '#ff0ff0',
'point'
), ),
roadLine: this.createLayerConfig( roadLine: this.createLayerConfig(
'./config/road_lines.geojson', './config/road_lines.geojson',
'#ff0ff022' '#ff0ff0',
'polyline'
), ),
waterPoint: this.createLayerConfig( waterPoint: this.createLayerConfig(
'./config/water_points.geojson', './config/water_points.geojson',
'#ff0ff022' '#ff0ff0',
'point'
), ),
waterLine: this.createLayerConfig( waterLine: this.createLayerConfig(
'./config/water_lines.geojson', './config/water_lines.geojson',
'#ff0ff022' '#ff0ff0',
'polyline'
), ),
} }
// 默认只加载一个,其他按需加载 // 默认只加载一个,其他按需加载
this.loadLayer('people') this.loadLayers(['people', 'plant', 'soil'], (results) => {
this.loadLayer('plant') console.log('所有图层加载完成', results)
this.loadLayer('soil') })
// this.loadLayer('people')
}, },
createLayerConfig(url, color) { createLayerConfig(url, color, type = 'polygon') {
return { const baseConfig = {
layer: null, layer: null,
config: { config: {
url: url, url: url,
crs: 'EPSG:4326', crs: 'EPSG:4326',
flyTo: true, // 关闭自动飞行 flyTo: true, // 关闭自动飞行
symbol: {
type: 'polygon',
styleOptions: {
color: Cesium.Color.fromCssColorString(color),
opacity: 0.3,
outline: false,
// outlineColor: color,
// outlineWidth: 1,
},
},
// 视域裁剪,只显示视野内的要素 // 视域裁剪,只显示视野内的要素
cull: { cull: {
enabled: true, enabled: true,
@ -391,6 +400,53 @@ export default {
}, },
}, },
} }
// 根据不同类型设置不同的符号样式
switch (type) {
case 'point':
baseConfig.config.symbol = {
type: 'point',
styleOptions: {
color: Cesium.Color.fromCssColorString(color),
pixelSize: 8,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 1,
scaleByDistance: new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e6, 0.5),
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
}
break
case 'polyline':
baseConfig.config.symbol = {
type: 'polyline',
styleOptions: {
color: Cesium.Color.fromCssColorString(color),
width: 3,
opacity: 0.8,
clampToGround: true,
},
}
break
case 'polygon':
default:
baseConfig.config.symbol = {
type: 'polygon',
styleOptions: {
color: Cesium.Color.fromCssColorString(color),
opacity: 0.3,
outline: false,
// 如果需要轮廓线可以取消注释
// outlineColor: color,
// outlineWidth: 1,
clampToGround: true, // 贴地显示
},
}
break
}
return baseConfig
}, },
async loadLayer(layerName) { async loadLayer(layerName) {
@ -406,6 +462,33 @@ export default {
await window.viewer.addLayer(this.layers[layerName].layer) await window.viewer.addLayer(this.layers[layerName].layer)
}, },
// 新增方法:同时加载多个图层
async loadLayers(names = [], cb) {
const tasks = names.map((name) => {
const cfg = this.layers[name]
if (!cfg) return Promise.resolve(null) // 无配置直接过
// 已经加载过:只切换可见性
if (cfg.layer) {
cfg.layer.show = true
return Promise.resolve(cfg.layer)
}
// 首次加载
return new Promise((resolve) => {
const layer = new mars3d.layer.GeoJsonLayer(cfg.config)
layer.once(mars3d.EventType.load, () => resolve(layer))
window.viewer.addLayer(layer)
cfg.layer = layer // 缓存
})
})
return Promise.all(tasks).then((loadedLayers) => {
if (cb) cb(loadedLayers.filter(Boolean)) // 过滤空值
return loadedLayers.filter(Boolean)
})
},
// 算矩形到 geoJSONLayer 的最小距离(米),判断是否在其内部 // 算矩形到 geoJSONLayer 的最小距离(米),判断是否在其内部
calcMinDistance(rectPositions, geoJSONLayer) { calcMinDistance(rectPositions, geoJSONLayer) {
if (!geoJSONLayer || !rectPositions?.length) return Infinity if (!geoJSONLayer || !rectPositions?.length) return Infinity
@ -461,7 +544,8 @@ export default {
}, },
// 判断矩形与 mars3d GeoJSON 图层是否相交(不转 GeoJSON // 判断矩形与 mars3d GeoJSON 图层是否相交(不转 GeoJSON
getIntersectId(position, layer) { getIntersectId(position, layer, bufferDistance = 0.001) {
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]]) // 闭合
@ -469,10 +553,12 @@ export default {
const [minX, minY, maxX, maxY] = turf.bbox(rectPoly) // 矩形 bbox const [minX, minY, maxX, maxY] = turf.bbox(rectPoly) // 矩形 bbox
let fc = this._polyCache.get(layer) let fc = this._polyCache.get(layer)
if (!fc) { if (!fc) {
const features = [] const features = []
layer.graphics.forEach((g) => { layer.graphics.forEach((g) => {
if (g.type !== 'polygonP') return // 只关心面 // 处理面要素
if (g.type === 'polygon') {
const coords = g.positions.map((p) => { const coords = g.positions.map((p) => {
const carto = Cesium.Cartographic.fromCartesian(p) const carto = Cesium.Cartographic.fromCartesian(p)
return [ return [
@ -484,29 +570,94 @@ export default {
features.push({ features.push({
type: 'Feature', type: 'Feature',
geometry: {type: 'Polygon', coordinates: [coords]}, geometry: {type: 'Polygon', coordinates: [coords]},
properties: {id: g.attr?.id ?? g.id}, // 挂 id properties: {id: g.attr?.id ?? g.id},
}) })
}
// 处理线要素
else if (g.type === 'polyline') {
const coords = g.positions.map((p) => {
const carto = Cesium.Cartographic.fromCartesian(p)
return [
Cesium.Math.toDegrees(carto.longitude),
Cesium.Math.toDegrees(carto.latitude),
]
}) })
features.push({
type: 'Feature',
geometry: {type: 'LineString', coordinates: coords},
properties: {id: g.attr?.id ?? g.id},
})
}
// 处理点要素
else if (g.type === 'point') {
const carto = Cesium.Cartographic.fromCartesian(g.position)
const coord = [
Cesium.Math.toDegrees(carto.longitude),
Cesium.Math.toDegrees(carto.latitude),
]
features.push({
type: 'Feature',
geometry: {type: 'Point', coordinates: coord},
properties: {id: g.attr?.id ?? g.id},
})
}
})
fc = turf.featureCollection(features) fc = turf.featureCollection(features)
this._polyCache.set(layer, fc) this._polyCache.set(layer, fc)
} }
// 先进行边界框筛选
const candidates = fc.features.filter((f) => { const candidates = fc.features.filter((f) => {
const [fminX, fminY, fmaxX, fmaxY] = turf.bbox(f) const [fminX, fminY, fmaxX, fmaxY] = turf.bbox(f)
return !(fmaxX < minX || fminX > maxX || fmaxY < minY || fminY > maxY) return !(fmaxX < minX || fminX > maxX || fmaxY < minY || fminY > maxY)
}) })
// 精确相交检测
for (const f of candidates) { for (const f of candidates) {
if (turf.booleanIntersects(rectPoly, f)) return String(f.properties.id) let isIntersect = false
// 根据几何类型使用不同的相交检测方法
if (f.geometry.type === 'Polygon') {
isIntersect = turf.booleanIntersects(rectPoly, f)
} else if (f.geometry.type === 'LineString') {
// 线与面的相交检测
isIntersect = turf.booleanIntersects(rectPoly, f)
} else if (f.geometry.type === 'Point') {
// 点与面的包含检测
isIntersect = turf.booleanPointInPolygon(f, rectPoly)
} }
if (isIntersect) return String(f.properties.id)
}
return null return null
}, },
// 手动绘制矩形
async drawRectangle(clampToGround) {
const graphic = await window.graphicLayer.startDraw({
type: isEntityGraphic ? 'rectangle' : 'rectangleP',
style: {
color: clampToGround ? '#ffff00' : '#3388ff',
opacity: 0.6,
outline: true,
outlineColor: '#ffffff',
outlineWidth: 2.0,
clampToGround,
},
})
console.log('标绘完成', graphic.toJSON())
},
// 绘制矩形区域 // 绘制矩形区域
drawInitialArea() { drawInitialArea(val) {
const polygon = new mars3d.graphic.PolygonEntity({ // 如果多边形尚未创建,则创建它
if (!this.polygon) {
this.polygon = new mars3d.graphic.PolygonEntity({
positions: this.positions.map((p) => positions: this.positions.map((p) =>
Cesium.Cartesian3.fromDegrees(p[0], p[1], 100) Cesium.Cartesian3.fromDegrees(p[0], p[1], 0)
), ),
style: { style: {
color: '#00ff00', color: '#00ff00',
@ -526,7 +677,33 @@ export default {
}, },
}) })
window.graphicLayer.addGraphic(polygon) console.log(' this.polygon===>', this.polygon)
window.graphicLayer.addGraphic(this.polygon)
this.isInitialized = true
} else {
// 如果多边形已存在,则更新它的位置
this.updateArea(val)
}
},
updateArea(val) {
if (!this.polygon) return
// 将经纬度转换为笛卡尔坐标
const cartesianPositions = val.map((p) =>
Cesium.Cartesian3.fromDegrees(p[0], p[1], 0)
)
// 更新多边形位置
this.polygon.positions = cartesianPositions
// 触发更新根据mars3d的具体API可能需要调用其他方法
if (this.polygon.update) {
this.polygon.update()
}
console.log('区域已更新', val)
}, },
// 分析平均坡度 // 分析平均坡度
@ -699,14 +876,27 @@ export default {
outline: true, outline: true,
outlineColor: '#ffffff', outlineColor: '#ffffff',
outlineWidth: 1, outlineWidth: 1,
// 关键设置:启用填充并设置填充颜色
fill: true,
material: color, // 使用相同的颜色作为填充材质
// 提高填充的优先级,确保可以点击
classificationType: Cesium.ClassificationType.BOTH,
}, },
center: new mars3d.LngLatPoint(b.center[0], b.center[1], 100), center: new mars3d.LngLatPoint(b.center[0], b.center[1], 0),
// 添加自定义属性,用于事件识别
attr: {
isRectangle: true,
originalData: b,
},
// 关键设置:启用深度测试和拾取
depthTestAgainstTerrain: false, // 禁用地形深度测试
allowPicking: true, // 确保允许拾取
}) })
/* 2.2 标签 options */ /* 2.2 标签 options */
labelOpts.push({ labelOpts.push({
id: `label_${b.id}`, id: `label_${b.id}`,
position: new mars3d.LngLatPoint(b.center[0], b.center[1], 100), position: new mars3d.LngLatPoint(b.center[0], b.center[1], 0),
style: { style: {
text: String(b.totalScore), // 必须字符串 text: String(b.totalScore), // 必须字符串
font_size: 24, font_size: 24,
@ -717,6 +907,13 @@ export default {
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
visibleDepth: false, visibleDepth: false,
scaleByDistance: true, scaleByDistance: true,
// 禁用标签的拾取,让点击事件穿透到矩形
disablePicking: true, // 关键设置
},
// 添加自定义属性,用于事件识别
attr: {
isLabel: true,
relatedRectId: b.id,
}, },
}) })
} }
@ -733,18 +930,26 @@ export default {
window.graphicLayer.addGraphic(rectGraphics) window.graphicLayer.addGraphic(rectGraphics)
window.graphicLayer.addGraphic(labelGraphics) window.graphicLayer.addGraphic(labelGraphics)
/* 5. 图层级事件委托:只监听一次 */ /* 5. 图层级事件委托:只监听矩形图形的点击事件 */
window.graphicLayer.off(mars3d.EventType.click, _selectRect, this) // 先清旧监听 window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this) // 先清旧监听
window.graphicLayer.on(mars3d.EventType.click, _selectRect, this)
// 为每个矩形图形单独绑定点击事件
rectGraphics.forEach((rectGraphic) => {
rectGraphic.on(mars3d.EventType.click, (event) => {
this._selectRect(event)
})
})
/* 6. 保存引用 */ /* 6. 保存引用 */
this.rectangles = rectGraphics this.rectangles = rectGraphics
this.labels = labelGraphics
/* 7. 事件处理函数 */ /* 7. 事件处理函数 */
function _selectRect(e) { this._selectRect = (event) => {
const graphic = e.graphic const graphic = event.graphic
if (graphic && graphic.attr) { // 确保只有矩形图形会触发选择
this.selectRectangle(graphic) // 你的原逻辑 if (graphic && graphic.attr && graphic.attr.isRectangle) {
this.selectRectangle(graphic)
} }
} }
}, },
@ -848,52 +1053,59 @@ export default {
// 选择矩形 // 选择矩形
selectRectangle(rect) { selectRectangle(rect) {
console.log('rect===>', rect) console.log('rect===>', rect)
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) {
if (
!this.roadPointGeo ||
!this.roadLineGeo ||
!this.waterPointGeo ||
!this.waterLineGeo
) {
this.loadLayer('roadPoint') this.loadLayer('roadPoint')
} else if (!this.roadLineGeo) {
this.loadLayer('roadLine') this.loadLayer('roadLine')
} else if (!this.waterPointGeo) {
this.loadLayer('waterPoint') this.loadLayer('waterPoint')
} else if (!this.waterLineGeo) {
this.loadLayer('waterLine') this.loadLayer('waterLine')
} }
// 关闭之前的Popup如果存在 if (!this.roadWaterLock) {
if (this.selectedPopup) { this.loadLayers(
this.selectedPopup.remove() ['roadPoint', 'roadLine', 'waterPoint', 'waterLine'],
(results) => {
this.roadWaterLock = true
this.getRoadWaterIds(rect)
}
)
} else {
this.getRoadWaterIds(rect)
} }
const cartographic = Cesium.Cartographic.fromCartesian(rect.center)
const positionArray = [
Cesium.Math.toDegrees(cartographic.longitude),
Cesium.Math.toDegrees(cartographic.latitude),
10,
]
// 创建新的Popup
this.selectedPopup = new mars3d.graphic.DivGraphic({
position: positionArray,
style: {
html: `<div class="marsBlueGradientPnl"><div>id${rect.id}</div><div>平均坡度:${rect.attr.slope}</div></div>`,
offsetY: -60,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(
0,
400000
), // 按视距距离显示
}, },
})
window.graphicLayer.addGraphic(this.selectedPopup) getRoadWaterIds(rect) {
let roadPointId = this.getIntersectId(
rect.points,
this.layers['roadPoint'].layer
)
let roadLineId = this.getIntersectId(
rect.points,
this.layers['roadLine'].layer
)
let waterPointId = this.getIntersectId(
rect.points,
this.layers['waterPoint'].layer
)
let waterLineId = this.getIntersectId(
rect.points,
this.layers['waterLine'].layer
)
console.log(
'roadPointId===>',
roadPointId,
roadLineId,
waterPointId,
waterLineId
)
}, },
// 清除所有矩形 // 清除所有矩形