Forráskód Böngészése

拾取坐标增加范围拾取,可以导出txt和geojson数据

WQQ 1 hónapja
szülő
commit
516d218ce3

+ 3 - 1
RuoYi-Vue3/src/supermap-cesium-module/components/model-transform/model-transform-panel.vue

@@ -463,8 +463,10 @@ export default {
           return;
         }
         
-        if (entity.position) {
+        if (entity.model) {
           selectEntity(entity);
+        } else {
+          clearSelection();
         }
       } else {
         clearSelection();

+ 340 - 59
RuoYi-Vue3/src/supermap-cesium-module/components/scene/pick-coordinate/pick-coordinate.vue

@@ -2,58 +2,123 @@
   <div id="pick-coordinate-panel" class="sm-panel" v-drag>
     <div class="sm-function-module-sub-section" style="margin:0" v-stopdrag>
       <div class="sm-half-L">
-        <label>状态:</label>
-        <span :class="{ active: isPicking }">{{ isPicking ? '拾取中...' : '已停止' }}</span>
+        <label>拾取模式:</label>
+        <el-select v-model="pickMode" size="small" style="width: 100px;">
+          <el-option label="点拾取" value="point" />
+          <el-option label="区域" value="polygon" />
+        </el-select>
       </div>
-      <div class="sm-half-L" v-if="lastPosition">
-        <label>经度:</label>
-        <span class="coord-value">{{ lastPosition.longitude }}</span>
-      </div>
-      <div class="sm-half-L" v-if="lastPosition">
-        <label>纬度:</label>
-        <span class="coord-value">{{ lastPosition.latitude }}</span>
-      </div>
-      <div class="sm-half-L" v-if="lastPosition">
-        <label>高度:</label>
-        <span class="coord-value">{{ lastPosition.height }} m</span>
-      </div>
-      <div class="sm-half-L flex-between">
-        <el-button 
-          :type="isPicking ? 'danger' : 'primary'" 
-          size="small" 
-          @click="togglePicking"
-          class="pick-btn"
-        >
-          {{ isPicking ? '停止拾取' : '开始拾取' }}
-        </el-button>
-        <el-button 
-          size="small" 
-          @click="copyCoordinate" 
-          :disabled="!lastPosition"
-        >
-          复制
-        </el-button>
-        <el-button 
-          size="small" 
-          @click="clearPosition"
-        >
-          清空
-        </el-button>
-      </div>
-      <div class="history-section" v-if="positionHistory.length > 0">
-        <label>历史记录</label>
-        <div class="history-list">
-          <div 
-            class="history-item" 
-            v-for="(pos, index) in positionHistory" 
-            :key="index"
-            @click="copyHistoryCoordinate(pos)"
+
+      <template v-if="pickMode === 'point'">
+        <div class="sm-half-L">
+          <label>状态:</label>
+          <span :class="{ active: isPicking }">{{ isPicking ? '拾取中...' : '已停止' }}</span>
+        </div>
+        <div class="sm-half-L" v-if="lastPosition">
+          <label>经度:</label>
+          <span class="coord-value">{{ lastPosition.longitude }}</span>
+        </div>
+        <div class="sm-half-L" v-if="lastPosition">
+          <label>纬度:</label>
+          <span class="coord-value">{{ lastPosition.latitude }}</span>
+        </div>
+        <div class="sm-half-L" v-if="lastPosition">
+          <label>高度:</label>
+          <span class="coord-value">{{ lastPosition.height }} m</span>
+        </div>
+        <div class="sm-half-L flex-between">
+          <el-button
+            :type="isPicking ? 'danger' : 'primary'"
+            size="small"
+            @click="togglePicking"
+            class="pick-btn"
+          >
+            {{ isPicking ? '停止拾取' : '开始拾取' }}
+          </el-button>
+          <el-button
+            size="small"
+            @click="copyCoordinate"
+            :disabled="!lastPosition"
           >
-            <span class="history-index">{{ index + 1 }}.</span>
-            <span class="history-coord">{{ pos.longitude }}, {{ pos.latitude }}</span>
+            复制
+          </el-button>
+          <el-button
+            size="small"
+            @click="clearPosition"
+          >
+            清空
+          </el-button>
+        </div>
+        <div class="history-section" v-if="positionHistory.length > 0">
+          <label>历史记录</label>
+          <div class="history-list">
+            <div
+              class="history-item"
+              v-for="(pos, index) in positionHistory"
+              :key="index"
+              @click="copyHistoryCoordinate(pos)"
+            >
+              <span class="history-index">{{ index + 1 }}.</span>
+              <span class="history-coord">{{ pos.longitude }}, {{ pos.latitude }}</span>
+            </div>
           </div>
         </div>
-      </div>
+      </template>
+
+      <template v-else>
+        <div class="sm-half-L">
+          <label>状态:</label>
+          <span :class="{ active: isDrawing }">{{ isDrawing ? '绘制中...' : (polygonPositions.length > 0 ? '已绘制' : '未开始') }}</span>
+        </div>
+        <div class="sm-half-L" v-if="polygonPositions.length > 0">
+          <label>顶点数:</label>
+          <span class="coord-value">{{ polygonPositions.length }}</span>
+        </div>
+        <div class="sm-half-L" v-if="polygonPositions.length >= 3">
+          <label>面积:</label>
+          <span class="coord-value">{{ areaText }}</span>
+        </div>
+        <div class="sm-half-L flex-between">
+          <el-button
+            :type="isDrawing ? 'danger' : 'primary'"
+            size="small"
+            @click="toggleDrawing"
+          >
+            {{ isDrawing ? '完成绘制' : '开始绘制' }}
+          </el-button>
+          <el-button
+            size="small"
+            @click="clearPolygon"
+            :disabled="polygonPositions.length === 0"
+          >
+            清空
+          </el-button>
+        </div>
+        <div class="polygon-info" v-if="polygonPositions.length > 0">
+          <label>顶点坐标</label>
+          <div class="vertex-list">
+            <div
+              class="vertex-item"
+              v-for="(pt, index) in polygonPositions"
+              :key="index"
+            >
+              <span class="vertex-index">{{ index + 1 }}</span>
+              <span class="vertex-coord">{{ pt.longitude }}, {{ pt.latitude }}</span>
+            </div>
+          </div>
+        </div>
+        <div class="export-section" v-if="polygonPositions.length >= 3">
+          <label>导出</label>
+          <div class="flex-between">
+            <el-button size="small" @click="exportToTxt" class="export-btn">
+              导出TXT
+            </el-button>
+            <el-button size="small" @click="exportToGeoJson" class="export-btn">
+              导出GeoJSON
+            </el-button>
+          </div>
+        </div>
+      </template>
     </div>
   </div>
 </template>
@@ -66,18 +131,36 @@ export default {
   data() {
     return {
       isPicking: false,
+      isDrawing: false,
+      pickMode: 'point',
       handler: null,
+      handlerArea: null,
       lastPosition: null,
-      positionHistory: []
+      positionHistory: [],
+      polygonPositions: [],
+      areaText: '',
+      doubleClickHandler: null
     }
   },
   mounted() {
     this.initHandler()
-    this.startPicking()
+    if (this.pickMode === 'point') {
+      this.startPicking()
+    }
   },
   beforeUnmount() {
     this.destroyPick()
   },
+  watch: {
+    pickMode(newMode) {
+      if (newMode === 'point') {
+        this.stopDrawing()
+        this.startPicking()
+      } else {
+        this.stopPicking()
+      }
+    }
+  },
   methods: {
     initHandler() {
       if (!this.handler) {
@@ -86,19 +169,19 @@ export default {
     },
     startPicking() {
       this.initHandler()
-      
+
       this.handler.setInputAction((movement) => {
         if (typeof viewer === 'undefined' || !viewer || !viewer.scene) {
           return
         }
-        
+
         let cartesian = viewer.scene.pickPosition(movement.position)
-        
+
         if (!cartesian) {
           const ray = viewer.camera.getPickRay(movement.position)
           cartesian = viewer.scene.globe.pick(ray, viewer.scene)
         }
-        
+
         if (cartesian) {
           const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
           if (cartographic) {
@@ -127,7 +210,7 @@ export default {
           }
         }
       }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
-      
+
       this.isPicking = true
     },
     updatePosition(longitude, latitude, height, cartesian) {
@@ -136,12 +219,12 @@ export default {
         latitude,
         height
       }
-      
+
       if (this.positionHistory.length >= 10) {
         this.positionHistory.shift()
       }
       this.positionHistory.push({ ...this.lastPosition })
-      
+
       if (typeof viewer !== 'undefined' && viewer && viewer.entities) {
         viewer.entities.removeById('pick-point-marker')
         viewer.entities.add({
@@ -202,12 +285,161 @@ export default {
         viewer.entities.removeById('pick-point-marker')
       }
     },
+    initAreaHandler() {
+      if (!this.handlerArea) {
+        this.handlerArea = new Cesium.MeasureHandler(
+          viewer,
+          Cesium.MeasureMode.Area,
+          Cesium.ClampMode.Space
+        )
+      }
+
+      const handlerArea = this.handlerArea
+
+      handlerArea.activeEvt.addEventListener(isActive => {
+        if (isActive == true) {
+          viewer.enableCursorStyle = false
+          viewer._element.style.cursor = ''
+        } else {
+          viewer.enableCursorStyle = true
+        }
+      })
+
+      handlerArea.measureEvt.addEventListener(result => {
+        const area = Number(result.area)
+        if (area > 1000000) {
+          this.areaText = (area / 1000000).toFixed(2) + ' km²'
+        } else {
+          this.areaText = area.toFixed(2) + ' m²'
+        }
+
+        this.polygonPositions = result.positions.map(pos => {
+          const cartographic = Cesium.Cartographic.fromCartesian(pos)
+          return {
+            longitude: Cesium.Math.toDegrees(cartographic.longitude).toFixed(6),
+            latitude: Cesium.Math.toDegrees(cartographic.latitude).toFixed(6),
+            height: cartographic.height.toFixed(2)
+          }
+        })
+      })
+    },
+    startDrawing() {
+      this.clearPolygon()
+      this.initAreaHandler()
+
+      if (this.handlerArea) {
+        this.handlerArea.activate()
+      }
+
+      this.isDrawing = true
+    },
+    stopDrawing() {
+      if (this.handlerArea) {
+        this.handlerArea.deactivate()
+      }
+      this.isDrawing = false
+    },
+    toggleDrawing() {
+      if (this.isDrawing) {
+        this.finishDrawing()
+      } else {
+        this.startDrawing()
+      }
+    },
+    finishDrawing() {
+      this.stopDrawing()
+      if (this.polygonPositions.length >= 3) {
+        ElMessage.success('区域绘制完成')
+      }
+    },
+    clearPolygon() {
+      this.stopDrawing()
+      this.polygonPositions = []
+      this.areaText = ''
+
+      if (this.handlerArea) {
+        this.handlerArea.clear()
+      }
+    },
+    exportToTxt() {
+      if (this.polygonPositions.length < 3) {
+        ElMessage.warning('请先绘制至少3个顶点')
+        return
+      }
+
+      let content = 'Polygon Coordinates Export\n'
+      content += '==========================\n\n'
+      content += `Total Vertices: ${this.polygonPositions.length}\n`
+      content += `Area: ${this.areaText}\n\n`
+      content += 'Vertex Data:\n'
+      content += '------------------------\n'
+
+      this.polygonPositions.forEach((pt, index) => {
+        content += `Point ${index + 1}: ${pt.longitude}, ${pt.latitude}, ${pt.height}\n`
+      })
+
+      content += '\nPolygon WKT:\n'
+      content += `POLYGON ((${this.polygonPositions.map(p => `${p.longitude} ${p.latitude}`).join(', ')}, ${this.polygonPositions[0].longitude} ${this.polygonPositions[0].latitude}))\n`
+
+      const blob = new Blob(['\ufeff' + content], { type: 'text/plain;charset=utf-8' })
+      const url = URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = `polygon_coordinates_${Date.now()}.txt`
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link)
+      URL.revokeObjectURL(url)
+
+      ElMessage.success('TXT文件导出成功')
+    },
+    exportToGeoJson() {
+      if (this.polygonPositions.length < 3) {
+        ElMessage.warning('请先绘制至少3个顶点')
+        return
+      }
+
+      const coordinates = this.polygonPositions.map(p => [parseFloat(p.longitude), parseFloat(p.latitude)])
+      coordinates.push([...coordinates[0]])
+
+      const geoJson = {
+        type: 'FeatureCollection',
+        features: [{
+          type: 'Feature',
+          properties: {
+            name: `polygon_${Date.now()}`
+          },
+          geometry: {
+            type: 'Polygon',
+            coordinates: [coordinates]
+          }
+        }]
+      }
+
+      const blob = new Blob([JSON.stringify(geoJson, null, 2)], { type: 'application/geo+json;charset=utf-8' })
+      const url = URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = `polygon_${Date.now()}.geojson`
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link)
+      URL.revokeObjectURL(url)
+
+      ElMessage.success('GeoJSON文件导出成功')
+    },
     destroyPick() {
       this.stopPicking()
+      this.stopDrawing()
+      this.clearPolygon()
       if (this.handler) {
         this.handler.destroy()
         this.handler = null
       }
+      if (this.handlerArea) {
+        this.handlerArea.deactivate()
+        this.handlerArea = null
+      }
       if (typeof viewer !== 'undefined' && viewer && viewer.entities) {
         viewer.entities.removeById('pick-point-marker')
       }
@@ -273,11 +505,14 @@ export default {
   margin-top: 5px;
 }
 
-.history-section > label {
+.history-section > label,
+.polygon-info > label,
+.export-section > label {
   width: 100%;
   font-weight: 500;
   color: #606266;
   margin-bottom: 8px;
+  display: block;
 }
 
 .history-list {
@@ -311,4 +546,50 @@ export default {
   color: #303133;
   font-size: 12px;
 }
-</style>
+
+.polygon-info {
+  width: 100%;
+  border-top: 1px solid #ebeef5;
+  padding-top: 10px;
+  margin-top: 5px;
+}
+
+.vertex-list {
+  max-height: 180px;
+  overflow-y: auto;
+}
+
+.vertex-item {
+  display: flex;
+  align-items: center;
+  padding: 4px 8px;
+  margin-bottom: 4px;
+  background: #f5f7fa;
+  border-radius: 4px;
+}
+
+.vertex-index {
+  color: #409eff;
+  margin-right: 8px;
+  font-size: 12px;
+  font-weight: 500;
+  min-width: 20px;
+}
+
+.vertex-coord {
+  font-family: monospace;
+  color: #303133;
+  font-size: 12px;
+}
+
+.export-section {
+  width: 100%;
+  border-top: 1px solid #ebeef5;
+  padding-top: 10px;
+  margin-top: 10px;
+}
+
+.export-btn {
+  flex: 1;
+}
+</style>

+ 2 - 2
RuoYi-Vue3/src/supermap-cesium-module/components/scene/scene-attribute/scene-attribute.js

@@ -120,9 +120,9 @@ function s3mlayerAttribute(props) {
     function initCorrectBeijingTime() {
         if (!viewer) return;
 
-        viewer.scene.sun.show = true;
+        viewer.scene.sun.show = state.sunShow;
         viewer.scene.moon.show = true;
-        viewer.scene.globe.enableLighting = true;
+        viewer.scene.globe.enableLighting = state.sunShow;
 
         // 设置时间轴为今天的00:00到24:00
         const today = new Date();

+ 9 - 9
RuoYi-Vue3/src/supermap-cesium-module/config/views_config.js

@@ -41,10 +41,15 @@ export default [
             },
             {
                 component: "Sm3dSpatialQuery3d",
-                imgSrc: "/img/componentsImg/spatialQuery3D.png", 
+                imgSrc: "/img/componentsImg/spatialQuery3D.png",
                 name: "三维空间查询"
+            },
+            {
+                component: "Sm3dPickCoordinate",
+                imgSrc: "/img/componentsImg/pick-coordinate.svg",
+                name: "拾取坐标"
             }
-          
+
         ]
     },
     {
@@ -201,13 +206,8 @@ export default [
             {
                 component: "Sm3dVolumeRender",
                 imgSrc: "/img/componentsImg/volume.png",
-                name: "带时序的体元栅格的可视化表达",
-            },
-            {
-                component: "Sm3dPickCoordinate",
-                imgSrc: "/img/componentsImg/pick-coordinate.svg",
-                name: "拾取坐标"
-            },
+                name: "带时序的体元栅格",
+            }
         ]
     },
     {

+ 4 - 5
RuoYi-Vue3/src/supermap-cesium-module/views/layout/aside.vue

@@ -1197,11 +1197,10 @@ export default {
     // 添加模型图标到 Cesium 地图
     addModelIcon() {
       if (!window.viewer) return
-      
-      // 定义图标位置(经纬度)
-      const longitude = 118.853990
-      const latitude = 25.230104
-      const height = 0.03
+
+      const longitude = 119.143770
+      const latitude = 25.867679
+      const height = 43.80
       
       // 创建图标实体
       const modelIcon = window.viewer.entities.add({