From b6138fc7a410698e2359aa5aacf2b35f7b667049 Mon Sep 17 00:00:00 2001 From: yiqiuyang Date: Wed, 10 Sep 2025 06:40:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/residentAnalysis/index.vue | 434 ++++++++++++++++++++------- 1 file changed, 323 insertions(+), 111 deletions(-) diff --git a/src/views/residentAnalysis/index.vue b/src/views/residentAnalysis/index.vue index 87af492..59c63ba 100644 --- a/src/views/residentAnalysis/index.vue +++ b/src/views/residentAnalysis/index.vue @@ -21,10 +21,10 @@
范围
- + - +
导入json文件
@@ -232,12 +232,8 @@ export default { ], form: { - leftTop: '', - rightBottom: '', - DEM: '', - peopleGeoJson: '', - soilGeoJson: '', - plantGeoJson: '', + leftTop: '114.33,27.8', + rightBottom: '114.34, 27.79', slopePer: 0.5, peoplePer: 0.2, plantPer: 0.2, @@ -252,13 +248,6 @@ export default { selectedRect: null, rectangles: [], - // 示例区域:矩形四个角点(经纬度) - positions: [ - [114.33, 27.79], - [114.34, 27.79], - [114.34, 27.8], - [114.33, 27.8], - ], peopleGeo: null, plantGeo: null, soilGeo: null, @@ -273,11 +262,36 @@ export default { _polyCache: null, validBlocks: [], colorList: [], + roadWaterLock: false, } }, created() { 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() { this.getMapJson() this.getColorList() @@ -335,50 +349,45 @@ export default { loadGeoJson() { // 延迟加载,按需显示 this.layers = { - people: this.createLayerConfig('./config/people.geojson', '#ff000033'), - plant: this.createLayerConfig('./config/plant.geojson', '#00ff0033'), - soil: this.createLayerConfig('./config/soil.geojson', '#0000ff33'), + people: this.createLayerConfig('./config/people.geojson', '#ff0000'), + plant: this.createLayerConfig('./config/plant.geojson', '#00ff00'), + soil: this.createLayerConfig('./config/soil.geojson', '#0000ff'), roadPoint: this.createLayerConfig( './config/road_points.geojson', - '#ff0ff022' + '#ff0ff0', + 'point' ), roadLine: this.createLayerConfig( './config/road_lines.geojson', - '#ff0ff022' + '#ff0ff0', + 'polyline' ), waterPoint: this.createLayerConfig( './config/water_points.geojson', - '#ff0ff022' + '#ff0ff0', + 'point' ), waterLine: this.createLayerConfig( './config/water_lines.geojson', - '#ff0ff022' + '#ff0ff0', + 'polyline' ), } // 默认只加载一个,其他按需加载 - this.loadLayer('people') - this.loadLayer('plant') - this.loadLayer('soil') + this.loadLayers(['people', 'plant', 'soil'], (results) => { + console.log('所有图层加载完成', results) + }) + // this.loadLayer('people') }, - createLayerConfig(url, color) { - return { + createLayerConfig(url, color, type = 'polygon') { + const baseConfig = { 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, @@ -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) { @@ -406,6 +462,33 @@ export default { 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 的最小距离(米),判断是否在其内部 calcMinDistance(rectPositions, geoJSONLayer) { if (!geoJSONLayer || !rectPositions?.length) return Infinity @@ -461,7 +544,8 @@ export default { }, // 判断矩形与 mars3d GeoJSON 图层是否相交(不转 GeoJSON) - getIntersectId(position, layer) { + getIntersectId(position, layer, bufferDistance = 0.001) { + console.log('position===>', position, layer) if (!position || !layer || !layer.graphics) return null const rectCoords = position.concat([position[0]]) // 闭合 @@ -469,64 +553,157 @@ export default { const [minX, minY, maxX, maxY] = turf.bbox(rectPoly) // 矩形 bbox let fc = this._polyCache.get(layer) + if (!fc) { const features = [] layer.graphics.forEach((g) => { - if (g.type !== 'polygonP') return // 只关心面 - const coords = g.positions.map((p) => { - const carto = Cesium.Cartographic.fromCartesian(p) - return [ + // 处理面要素 + if (g.type === 'polygon') { + const coords = g.positions.map((p) => { + const carto = Cesium.Cartographic.fromCartesian(p) + return [ + Cesium.Math.toDegrees(carto.longitude), + Cesium.Math.toDegrees(carto.latitude), + ] + }) + coords.push(coords[0]) // 闭合 + features.push({ + type: 'Feature', + geometry: {type: 'Polygon', coordinates: [coords]}, + 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), ] - }) - coords.push(coords[0]) // 闭合 - features.push({ - type: 'Feature', - geometry: {type: 'Polygon', coordinates: [coords]}, - properties: {id: g.attr?.id ?? g.id}, // 挂 id - }) + features.push({ + type: 'Feature', + geometry: {type: 'Point', coordinates: coord}, + properties: {id: g.attr?.id ?? g.id}, + }) + } }) + fc = turf.featureCollection(features) this._polyCache.set(layer, fc) } + // 先进行边界框筛选 const candidates = fc.features.filter((f) => { const [fminX, fminY, fmaxX, fmaxY] = turf.bbox(f) return !(fmaxX < minX || fminX > maxX || fmaxY < minY || fminY > maxY) }) + // 精确相交检测 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 }, - // 绘制矩形区域 - drawInitialArea() { - const polygon = new mars3d.graphic.PolygonEntity({ - positions: this.positions.map((p) => - Cesium.Cartesian3.fromDegrees(p[0], p[1], 100) - ), + // 手动绘制矩形 + async drawRectangle(clampToGround) { + const graphic = await window.graphicLayer.startDraw({ + type: isEntityGraphic ? 'rectangle' : 'rectangleP', style: { - color: '#00ff00', - opacity: 0.3, + color: clampToGround ? '#ffff00' : '#3388ff', + opacity: 0.6, outline: true, outlineColor: '#ffffff', - outlineWidth: 2, - }, - label: { - text: '分析区域', - font: '16px sans-serif', - color: '#ffffff', - outline: true, - outlineColor: '#000000', - outlineWidth: 2, - pixelOffset: new Cesium.Cartesian2(0, -40), + outlineWidth: 2.0, + clampToGround, }, }) - window.graphicLayer.addGraphic(polygon) + console.log('标绘完成', graphic.toJSON()) + }, + + // 绘制矩形区域 + drawInitialArea(val) { + // 如果多边形尚未创建,则创建它 + if (!this.polygon) { + this.polygon = new mars3d.graphic.PolygonEntity({ + positions: this.positions.map((p) => + Cesium.Cartesian3.fromDegrees(p[0], p[1], 0) + ), + style: { + color: '#00ff00', + opacity: 0.3, + outline: true, + outlineColor: '#ffffff', + outlineWidth: 2, + }, + label: { + text: '分析区域', + font: '16px sans-serif', + color: '#ffffff', + outline: true, + outlineColor: '#000000', + outlineWidth: 2, + pixelOffset: new Cesium.Cartesian2(0, -40), + }, + }) + + 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, outlineColor: '#ffffff', 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 */ labelOpts.push({ 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: { text: String(b.totalScore), // 必须字符串 font_size: 24, @@ -717,6 +907,13 @@ export default { verticalOrigin: Cesium.VerticalOrigin.BOTTOM, visibleDepth: false, scaleByDistance: true, + // 禁用标签的拾取,让点击事件穿透到矩形 + disablePicking: true, // 关键设置 + }, + // 添加自定义属性,用于事件识别 + attr: { + isLabel: true, + relatedRectId: b.id, }, }) } @@ -733,18 +930,26 @@ export default { window.graphicLayer.addGraphic(rectGraphics) window.graphicLayer.addGraphic(labelGraphics) - /* 5. 图层级事件委托:只监听一次 */ - window.graphicLayer.off(mars3d.EventType.click, _selectRect, this) // 先清旧监听 - window.graphicLayer.on(mars3d.EventType.click, _selectRect, this) + /* 5. 图层级事件委托:只监听矩形图形的点击事件 */ + window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this) // 先清旧监听 + + // 为每个矩形图形单独绑定点击事件 + rectGraphics.forEach((rectGraphic) => { + rectGraphic.on(mars3d.EventType.click, (event) => { + this._selectRect(event) + }) + }) /* 6. 保存引用 */ this.rectangles = rectGraphics + this.labels = labelGraphics /* 7. 事件处理函数 */ - function _selectRect(e) { - const graphic = e.graphic - if (graphic && graphic.attr) { - this.selectRectangle(graphic) // 你的原逻辑 + this._selectRect = (event) => { + const graphic = event.graphic + // 确保只有矩形图形会触发选择 + if (graphic && graphic.attr && graphic.attr.isRectangle) { + this.selectRectangle(graphic) } } }, @@ -848,52 +1053,59 @@ export default { // 选择矩形 selectRectangle(rect) { console.log('rect===>', rect) - + if (this.selectedRect?.id === rect.id) return this.selectedRect = { id: rect.id, center: rect.center, } - - if ( - !this.roadPointGeo || - !this.roadLineGeo || - !this.waterPointGeo || - !this.waterLineGeo - ) { + if (!this.roadPointGeo) { this.loadLayer('roadPoint') + } else if (!this.roadLineGeo) { this.loadLayer('roadLine') + } else if (!this.waterPointGeo) { this.loadLayer('waterPoint') + } else if (!this.waterLineGeo) { this.loadLayer('waterLine') } - // 关闭之前的Popup(如果存在) - if (this.selectedPopup) { - this.selectedPopup.remove() + if (!this.roadWaterLock) { + this.loadLayers( + ['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, - ] + 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 + ) - // 创建新的Popup - this.selectedPopup = new mars3d.graphic.DivGraphic({ - position: positionArray, - style: { - html: `
id:${rect.id}
平均坡度:${rect.attr.slope}
`, - offsetY: -60, - horizontalOrigin: Cesium.HorizontalOrigin.CENTER, - verticalOrigin: Cesium.VerticalOrigin.BOTTOM, - distanceDisplayCondition: new Cesium.DistanceDisplayCondition( - 0, - 400000 - ), // 按视距距离显示 - }, - }) - - window.graphicLayer.addGraphic(this.selectedPopup) + console.log( + 'roadPointId===>', + roadPointId, + roadLineId, + waterPointId, + waterLineId + ) }, // 清除所有矩形