Bläddra i källkod

添加地形,修复模型加载的位置问题

WQQ 4 dagar sedan
förälder
incheckning
50b5c5b0c4

+ 14 - 0
RuoYi-Vue3/index.html

@@ -211,6 +211,20 @@
   </div>
   <!-- 引入 Cesium -->
   <script src="/Cesium/Cesium.js"></script>
+  <!-- 引入天地图扩展包及其依赖 -->
+  <script src="https://api.tianditu.gov.cn/cdn/plugins/cesium/long.min.js"></script>
+  <script src="https://api.tianditu.gov.cn/cdn/plugins/cesium/bytebuffer.min.js"></script>
+  <script src="https://api.tianditu.gov.cn/cdn/plugins/cesium/protobuf.min.js"></script>
+  <script src="https://api.tianditu.gov.cn/cdn/plugins/cesium/Cesium_ext_min.js"></script>
+  <script>
+    console.log('=== 天地图扩展包加载检查 ===');
+    console.log('Cesium.TdtPlug:', typeof Cesium.TdtPlug);
+    console.log('Cesium.GeoGlobe:', typeof Cesium.GeoGlobe);
+    console.log('Cesium.GeoWTFS:', typeof Cesium.GeoWTFS);
+    if (typeof Cesium.TdtPlug !== 'undefined') {
+      console.log('TdtPlug 对象:', Cesium.TdtPlug);
+    }
+  </script>
   <link rel="stylesheet" href="/Cesium/Widgets/widgets.css">
   <script type="module" src="/src/main.js"></script>
 </body>

+ 118 - 24
RuoYi-Vue3/src/supermap-cesium-module/views/layout/aside.vue

@@ -269,7 +269,7 @@ import TyphoonVisualization from "../../components/typhoon-visualization/typhoon
 import CesiumThreeFusion from "@/components/ThreeCesiumIntegration/CesiumThreeFusion.vue";  //Three.js与Cesium融合组件
 import ThreeModelViewer from "@/components/ThreeModelViewer/ModelViewer.vue";  //模型查看器组件
 import { CircleClose, Plus, CirclePlus, Setting } from '@element-plus/icons-vue';  //删除图标
-import { listModel } from '@/api/watershed/model';  //模型API
+import { listModel, getModel } from '@/api/watershed/model';  //模型API
 import { getDefaultMapConfig, saveMapConfig } from '@/api/cesium/mapConfig';  //地图配置API
 import serviceApi from '@/api/watershed/service';  //自定义服务API
 import { ElMessage } from 'element-plus';
@@ -487,7 +487,7 @@ export default {
             format: model.format,
             uploadUnit: model.uploadUnit || '未知单位',
             filePath: model.filePath || model.file_path || model.url || '',
-            coordinates: model.coordinates,
+            coordinates: model.coordinates || model.modelCoordinates || '',
             rotationX: model.rotationX != null ? model.rotationX : (model.rotation_x != null ? model.rotation_x : 0),
             rotationY: model.rotationY != null ? model.rotationY : (model.rotation_y != null ? model.rotation_y : 0),
             rotationZ: model.rotationZ != null ? model.rotationZ : (model.rotation_z != null ? model.rotation_z : 0),
@@ -534,6 +534,31 @@ export default {
       console.log('已加载的模型实体:', this.loadedModelEntities)
       console.log('isModelLoaded结果:', this.isModelLoaded(model.id))
 
+      try {
+        const latestData = await getModel(model.id)
+        if (latestData && latestData.data) {
+          const latest = latestData.data
+          model.coordinates = latest.coordinates || latest.modelCoordinates || model.coordinates
+          model.rotationX = latest.rotationX != null ? latest.rotationX : (model.rotationX || 0)
+          model.rotationY = latest.rotationY != null ? latest.rotationY : (model.rotationY || 0)
+          model.rotationZ = latest.rotationZ != null ? latest.rotationZ : (model.rotationZ || 0)
+          model.scaleX = latest.scaleX != null ? latest.scaleX : (model.scaleX || 1)
+          model.scaleY = latest.scaleY != null ? latest.scaleY : (model.scaleY || 1)
+          model.scaleZ = latest.scaleZ != null ? latest.scaleZ : (model.scaleZ || 1)
+          console.log('已更新模型最新数据:', {
+            coordinates: model.coordinates,
+            rotationX: model.rotationX,
+            rotationY: model.rotationY,
+            rotationZ: model.rotationZ,
+            scaleX: model.scaleX,
+            scaleY: model.scaleY,
+            scaleZ: model.scaleZ
+          })
+        }
+      } catch (fetchError) {
+        console.warn('获取最新模型数据失败,使用缓存数据:', fetchError.message)
+      }
+
       // 清除之前的模型编辑器和高亮模型
       if (this.currentModelEditor) {
         if (this.currentModelEditor.destroy) {
@@ -634,16 +659,28 @@ export default {
             if (coords.length >= 2 && !isNaN(coords[0]) && !isNaN(coords[1])) {
               const lon = coords[0]
               const lat = coords[1]
-              const height = coords[2] || 0
+              const modelHeight = coords[2] !== undefined && !isNaN(coords[2]) ? coords[2] : 0
+              
+              console.log('解析到的模型高度:', modelHeight)
               
-              // 获取地面高度
               const cartographic = Cesium.Cartographic.fromDegrees(lon, lat)
-              const terrainHeight = viewer.scene.globe.getHeight(cartographic)
-              const groundHeight = terrainHeight || 0
               
-              // 模型高度 = 地面高度 + 用户指定的高度
-              position = Cesium.Cartesian3.fromDegrees(lon, lat, groundHeight + height)
-              console.log('使用经纬度定位 - 经度:', lon, '纬度:', lat, '地面高度:', groundHeight, '模型高度:', height, '总高度:', groundHeight + height)
+              if (viewer.scene.globe && viewer.terrainProvider) {
+                try {
+                  const updatedPositions = await Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [cartographic])
+                  const terrainHeight = updatedPositions[0].height || 0
+                  const finalHeight = terrainHeight + modelHeight
+                  position = Cesium.Cartesian3.fromDegrees(lon, lat, finalHeight)
+                  console.log('使用地形高度定位 - 经度:', lon, '纬度:', lat, '地形高度:', terrainHeight, '模型高度:', modelHeight, '总高度:', finalHeight)
+                } catch (terrainError) {
+                  console.warn('获取地形高度失败,使用默认高度:', terrainError.message)
+                  position = Cesium.Cartesian3.fromDegrees(lon, lat, modelHeight)
+                  console.log('使用模型指定高度 - 经度:', lon, '纬度:', lat, '模型高度:', modelHeight)
+                }
+              } else {
+                position = Cesium.Cartesian3.fromDegrees(lon, lat, modelHeight)
+                console.log('无地形服务,使用绝对高度 - 经度:', lon, '纬度:', lat, '高度:', modelHeight)
+              }
             } else {
               console.warn('经纬度格式不正确或为NaN,使用默认位置')
               position = Cesium.Cartesian3.fromDegrees(116.39, 39.9, 0)
@@ -693,6 +730,7 @@ export default {
           const scaleY = model.scaleY || 1;
           const scaleZ = model.scaleZ || 1;
           entity.model.scale = (scaleX + scaleY + scaleZ) / 3;
+          entity._modelScale = { x: scaleX, y: scaleY, z: scaleZ };
         }
 
         console.log('实体已添加:', entity)
@@ -703,17 +741,64 @@ export default {
 
         if (shouldFlyTo) {
           setTimeout(() => {
-            try {
-              viewer.flyTo(entity, {
-                duration: 2,
-                offset: new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0), Cesium.Math.toRadians(-30), 5)
-              })
-              console.log('开始飞行到模型位置')
-            } catch (flyError) {
-              console.error('飞行失败:', flyError)
-              ElMessage.warning('模型已加载,但跳转失败')
-            }
-          }, 100)
+            const calculateFlyDistance = () => {
+              const tryGetModelSize = (attempts = 0) => {
+                if (attempts > 15) {
+                  console.log('多次尝试获取模型尺寸失败,使用默认距离');
+                  viewer.flyTo(entity, {
+                    duration: 2,
+                    offset: new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0), Cesium.Math.toRadians(-30), 100)
+                  });
+                  return;
+                }
+                
+                const entityPosition = entity.position.getValue(viewer.clock.currentTime);
+                if (!entityPosition) {
+                  console.log('无法获取实体位置');
+                  setTimeout(() => tryGetModelSize(attempts + 1), 300);
+                  return;
+                }
+                
+                let bestModel = null;
+                let bestDistance = Infinity;
+                
+                for (let i = 0; i < viewer.scene.primitives.length; i++) {
+                  const primitive = viewer.scene.primitives.get(i);
+                  if (primitive instanceof Cesium.Model) {
+                    const modelPosition = primitive.modelMatrix ? primitive.modelMatrix.clone() : null;
+                    if (modelPosition) {
+                      const modelCartesian = Cesium.Matrix4.getTranslation(modelPosition, new Cesium.Cartesian3());
+                      const distance = Cesium.Cartesian3.distance(entityPosition, modelCartesian);
+                      if (distance < bestDistance) {
+                        bestDistance = distance;
+                        bestModel = primitive;
+                      }
+                    }
+                  }
+                }
+                
+                if (bestModel && bestModel.boundingSphere && bestModel.boundingSphere.radius > 0) {
+                  const modelRadius = bestModel.boundingSphere.radius;
+                  const flyDistance = modelRadius * 3;
+                  console.log('通过位置匹配找到模型,半径:', modelRadius, '飞行距离:', flyDistance);
+                  
+                  viewer.flyTo(entity, {
+                    duration: 2,
+                    offset: new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0), Cesium.Math.toRadians(-30), flyDistance)
+                  });
+                  console.log('开始飞行到模型位置,模型半径:', modelRadius, '飞行距离:', flyDistance);
+                  return;
+                }
+                
+                console.log('等待模型加载完成,尝试次数:', attempts + 1);
+                setTimeout(() => tryGetModelSize(attempts + 1), 300);
+              };
+              
+              tryGetModelSize();
+            };
+
+            calculateFlyDistance();
+          }, 1500)
         }
 
         this.saveLoadedServices()
@@ -1023,10 +1108,19 @@ export default {
           });
           break;
         case "tianDiTuTerrain":
-          var t_Provider = new Cesium.TiandituTerrainProvider({
-            token: "3fb1e9fda20ee995dc815c8243553ce8"
-          });
-          viewer.terrainProvider = t_Provider;
+          console.log('加载 Cesium 官方地形...');
+          const cesiumIonToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiZGYwNWZmMi0wYTJjLTQ3MDgtODk3Mi1mNTg4YjljZTMzYWUiLCJpZCI6MjA0NTU5LCJpYXQiOjE3MTE0NDc4NDh9.xU-eWEQxv-FeADvsH_uM35EKRN6brqmXl1AQm6phnEM";
+          Cesium.Ion.defaultAccessToken = cesiumIonToken;
+          try {
+            viewer.terrainProvider = Cesium.createWorldTerrain({
+              requestWaterMask: true,
+              requestVertexNormals: true
+            });
+            console.log('Cesium 地形加载成功');
+          } catch (e) {
+            console.error('Cesium 地形加载失败:', e);
+            viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider();
+          }
           break;
         case "supermapOnlineTerrain":
           viewer.terrainProvider = new Cesium.SCTTerrainProvider({

+ 0 - 34
RuoYi-Vue3/src/views/front/content/ModelPreview.vue

@@ -17,19 +17,6 @@
         <div v-if="showLightControl" class="light-control-panel">
           <h4>光照控制</h4>
           
-          <!-- 材质透明度调整 -->
-          <div class="control-item">
-            <label>材质不透明度</label>
-            <el-slider 
-              v-model="materialOpacity" 
-              :min="0.1" 
-              :max="1" 
-              :step="0.05" 
-              @change="updateMaterialOpacity"
-            />
-            <span class="value">{{ materialOpacity.toFixed(2) }}</span>
-          </div>
-          
           <!-- 环境光强度 -->
           <div class="control-item">
             <label>环境光强度</label>
@@ -178,8 +165,6 @@ const lightSettings = ref({
   }
 })
 
-const materialOpacity = ref(1.0)
-
 // 获取类型显示名称
 const getTypeDisplayName = (type) => {
   const typeMapping = {
@@ -1452,25 +1437,6 @@ const fixTransparentMaterials = (modelObject) => {
   console.log('修复了', fixedCount, '个半透明材质的透明度')
 }
 
-// 更新模型材质的不透明度
-const updateMaterialOpacity = () => {
-  if (!model) return
-  
-  const opacity = materialOpacity.value
-  
-  model.traverse((child) => {
-    if (child.isMesh && child.material) {
-      const materials = Array.isArray(child.material) ? child.material : [child.material]
-      
-      materials.forEach((material) => {
-        if (material.transparent === true || material.opacity < 1) {
-          material.opacity = opacity
-          material.needsUpdate = true
-        }
-      })
-    }
-  })
-}
 </script>
 
 <style scoped>

+ 4 - 4
RuoYi-Vue3/src/views/front/content/ShuiliGongcheng.vue

@@ -559,7 +559,7 @@ const formData = ref({
   type: '',
   location: '',
   format: '',
-  status: '正常',
+  status: 'NORMAL',
   uploadUnit: '',
   file: null
 })
@@ -786,7 +786,7 @@ const fetchModels = async () => {
           location: model.modelCoordinates || model.coordinates || '未设置',
           uploadUnit: model.uploadUnit || '未知单位',
           format: model.modelFormat || model.format, // 兼容后端原始字段和映射字段
-          status: model.status === '0' ? '正常' : model.status === '1' ? '维护中' : '已删除',
+          status: (!model.status || model.status === '0' || model.status === '正常' || model.status === 'NORMAL') ? '正常' : model.status === '1' || model.status === 'MAINTENANCE' ? '维护中' : '已删除',
           createTime: model.createTime || model.created_at ? (model.createTime || model.created_at).toString().split(' ')[0].replace(/-/g, '/') : '',
           // 保存完整的模型数据,以便查看是否包含文件路径或文件名
           originalData: model
@@ -1054,7 +1054,7 @@ const openUploadDialog = () => {
     location: '',
     uploadUnit: userStore.dept.deptName || '未知单位', // 自动填充当前登录用户的部门名称
     format: '',
-    status: 'NORMAL',  // 改为英文默认值
+    status: 'NORMAL',
     file: null  // 清空文件
   }
 }
@@ -1167,7 +1167,7 @@ const editModel = (row) => {
     location: row.location,
     uploadUnit: row.uploadUnit,
     format: row.format,
-    status: row.status === '正常' ? 'NORMAL' : 'MAINTENANCE'
+    status: row.status === '正常' || row.status === '0' || row.status === 'NORMAL' ? 'NORMAL' : 'MAINTENANCE'
   }
   editDialogVisible.value = true
 }

+ 12 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/watershed/WatershedModelController.java

@@ -101,6 +101,18 @@ public class WatershedModelController extends BaseController
         if (params.get("id") != null) {
             model.setModelId(Long.parseLong(params.get("id").toString()));
         }
+        if (params.get("name") != null) {
+            model.setModelName(params.get("name").toString());
+        }
+        if (params.get("type") != null) {
+            model.setModelType(params.get("type").toString());
+        }
+        if (params.get("format") != null) {
+            model.setModelFormat(params.get("format").toString());
+        }
+        if (params.get("status") != null) {
+            model.setStatus(params.get("status").toString());
+        }
         if (params.get("coordinates") != null) {
             model.setModelCoordinates(params.get("coordinates").toString());
         }

BIN
ruoyi-admin/target/classes/com/ruoyi/web/controller/watershed/WatershedModelController.class