From f57291a127841266af0aa365453206ea4bb910b3 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Mon, 8 Jun 2026 11:38:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(render):=20=E5=9C=B0=E5=BD=A2=E9=AB=98?= =?UTF-8?q?=E7=A8=8B=E6=8C=89=E6=B5=8B=E7=BA=BF=E5=9C=B0=E8=A1=A8=E5=9F=BA?= =?UTF-8?q?=E5=87=86=20rebase=20+=20=E5=88=87=E7=89=87=E6=94=B9=E5=B7=A6?= =?UTF-8?q?=E9=94=AE=E6=8B=96=E5=8A=A8=E7=A7=BB=E5=8A=A8=E5=88=87=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 反馈1 地形浮空/偏位: 诊断确认 DEM 是 WGS84 经纬度(26x10, 覆盖~700x330m), 测线仅~70m 在其南缘 (横向"偏"实为地形覆盖远大于测线, 地理正确); 纵向浮空因地形用绝对高程(16-95m) vs 帘面深度。 → buildTerrain 加 zOffset(从高程减基准), app 传测线地表高程中位数 refElev, 使地形落在测线附近。 完整 Z 基准统一(与帘面/体素夸张一致)仍属 spec M-3 待办。 - 反馈2 切片交互: vtkImagePlaneWidget 默认左键=取值光标(十字), 不直观; 改 左键=移动切面 (VTK_SLICE_MOTION_ACTION)、中键=取值。现在左键拖动直接滑动切面。 - 全 40 测试绿; app 构建干净。 --- src/app/main.cpp | 11 +++++++++-- src/render/actors/TerrainActor.cpp | 5 +++-- src/render/actors/TerrainActor.hpp | 5 ++++- tests/render/test_terrain.cpp | 2 +- tests/spike/render_verify.cpp | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) 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);