diff --git a/src/app/main.cpp b/src/app/main.cpp index 26b6805..19f0271 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -167,6 +167,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re const double lat0 = median(baseGrid.lat); const double lon0 = median(baseGrid.lon); auto frame = std::make_shared(lat0, lon0); + // 测线地表高程基准(地形 z rebase 用,使地形落在测线附近而非按绝对高程浮空)。 + const double refElev = baseGrid.elevation.empty() ? 0.0 : median(baseGrid.elevation); // ── 中央 QVTK + Scene(竖直帘面场景)───────────────────────────────── // Scene 非 QObject:堆分配,用 widget 销毁信号清理(widget 随 window 销毁)。 @@ -348,7 +350,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // frame/structure 全局共享;切视图/勾选变化都调用此函数重建当前视图。 auto rebuildCentral = [scene, rendererPtr, renderWindowPtr, viewMode, &repo, frame, tree, structure, showCurtain, showVoxel, showTerrain, showSlice, slicePlane, - crs]() { + crs, refElev]() { // 先拆除上次的切片 widget(独立于 scene actor,须显式关闭),再按条件重建。 if (*slicePlane) { (*slicePlane)->Off(); *slicePlane = nullptr; } scene->clear(); @@ -416,6 +418,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re plane->SetSliceIndex(dims[0] / 2); plane->SetLookupTable(lut); plane->DisplayTextOn(); + // 左键拖动=移动切面(默认左键是取值光标十字,不直观);中键仍可取值。 + plane->SetLeftButtonAction(vtkImagePlaneWidget::VTK_SLICE_MOTION_ACTION); + plane->SetMiddleButtonAction(vtkImagePlaneWidget::VTK_CURSOR_ACTION); plane->On(); *slicePlane = plane; } @@ -424,7 +429,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // 三维「地形」图层:GDAL 读 DEM(高程)+影像(EPSG:3857),重投影到世界系,warp 面 + 纹理。 if (!is2D && *showTerrain && crs) { - auto terr = geopro::render::buildTerrain(repo.demPath(), repo.imagePath(), *frame, 1.0); + // zOffset=refElev 使地形落在测线地表高程附近(不按绝对高程浮空);zScale=1 真实起伏。 + auto terr = geopro::render::buildTerrain(repo.demPath(), repo.imagePath(), *frame, + refElev, 1.0); if (terr) scene->addActor(terr); } diff --git a/src/render/actors/TerrainActor.cpp b/src/render/actors/TerrainActor.cpp index 0b41e37..f793dad 100644 --- a/src/render/actors/TerrainActor.cpp +++ b/src/render/actors/TerrainActor.cpp @@ -92,7 +92,8 @@ Raster readGeo(GDALDataset* ds) { } // namespace vtkSmartPointer buildTerrain(const std::string& demPath, const std::string& imagePath, - const geopro::core::GeoLocalFrame& frame, double zScale) + const geopro::core::GeoLocalFrame& frame, double zOffset, + double zScale) { GDALAllRegister(); @@ -152,7 +153,7 @@ vtkSmartPointer buildTerrain(const std::string& demPath, const std::st const vtkIdType id = static_cast(j) * dg.w + i; float z = elev[static_cast(j) * dg.w + i]; if (hasNoData && z == static_cast(noData)) z = vmin; - points->SetPoint(id, local.x, local.y, z * zScale); + points->SetPoint(id, local.x, local.y, (z - zOffset) * zScale); // rebase 到测线高程 sc->SetValue(id, z); if (hasImage) { const auto m = llTo3857.forward(ll.x, ll.y); // (mercX, mercY) diff --git a/src/render/actors/TerrainActor.hpp b/src/render/actors/TerrainActor.hpp index 87f5497..4c29ff6 100644 --- a/src/render/actors/TerrainActor.hpp +++ b/src/render/actors/TerrainActor.hpp @@ -13,10 +13,13 @@ namespace geopro::render { // 高程作 z 起伏(vtkStructuredGrid 面),影像按经纬→3857→像素 算纹理坐标贴面。 // 影像加载失败则退化为按高程上色(无纹理)。读不到 DEM 返回空 actor。 // -// 依赖 GDAL/PROJ;调用方运行时须有 PROJ_DATA。zScale 为高程纵向夸张(地形通常 1.0~数倍)。 +// 依赖 GDAL/PROJ;调用方运行时须有 PROJ_DATA。 +// zOffset:从高程减去的基准(米)——传测线地表高程使地形落在测线附近(否则按绝对高程浮空, +// spec M-3 Z 基准)。世界 z = (elev - zOffset) * zScale。zScale 为纵向夸张。 vtkSmartPointer buildTerrain(const std::string& demPath, const std::string& imagePath, const geopro::core::GeoLocalFrame& frame, + double zOffset = 0.0, double zScale = 1.0); } // namespace geopro::render diff --git a/tests/render/test_terrain.cpp b/tests/render/test_terrain.cpp index cb5f3f0..b8c937c 100644 --- a/tests/render/test_terrain.cpp +++ b/tests/render/test_terrain.cpp @@ -18,7 +18,7 @@ static const std::string kDir = // (需 PROJ_DATA + GDAL 运行时; tests CMake 注入 PROJ_DATA。) TEST(Terrain, BuildsTexturedSurfaceFromSampleDemImage) { GeoLocalFrame frame(22.546, 114.164); // 测区附近(香港) - auto actor = geopro::render::buildTerrain(kDir + "dem.tif", kDir + "image.tif", frame, 1.0); + auto actor = geopro::render::buildTerrain(kDir + "dem.tif", kDir + "image.tif", frame); ASSERT_NE(actor.GetPointer(), nullptr); ASSERT_NE(actor->GetMapper(), nullptr); // 成功读 DEM → 有 mapper(空 actor 无 mapper) } diff --git a/tests/spike/render_verify.cpp b/tests/spike/render_verify.cpp index d4cfa93..8fde7b2 100644 --- a/tests/spike/render_verify.cpp +++ b/tests/spike/render_verify.cpp @@ -152,7 +152,7 @@ int main() { // 7) DEM 地形 + 影像贴图 — GDAL 读 + 重投影到世界系 + warp 面 + 纹理 { - auto terr = render::buildTerrain(dir + "dem.tif", dir + "image.tif", frame, 1.0); + auto terr = render::buildTerrain(dir + "dem.tif", dir + "image.tif", frame, 0.0, 1.0); vtkNew ren; ren->SetBackground(0.50, 0.60, 0.72); if (terr) ren->AddActor(terr); render::applyFree3D(ren);