fix(vtk): 地形不出根因-看小测区LOD选z16-18>DEM上限15致fetchTerrain静默退出;改取祖先DEM瓦片按经纬采样+诊断日志

This commit is contained in:
gaozheng 2026-06-17 10:06:43 +08:00
parent 67f767d787
commit 33e9949623
2 changed files with 49 additions and 20 deletions

View File

@ -114,6 +114,7 @@ void TileBasemap::show(Kind kind) {
inFlight_.clear(); inFlight_.clear();
terrainInFlight_.clear(); terrainInFlight_.clear();
desired_.clear(); desired_.clear();
terrainProbed_ = false;
kind_ = kind; kind_ = kind;
if (kind == Hidden) { if (kind == Hidden) {
if (rw_) rw_->Render(); if (rw_) rw_->Render();
@ -280,18 +281,26 @@ void TileBasemap::placeTile(long long key, int z, int x, int y, const QImage& im
} }
void TileBasemap::fetchTerrain(int z, int x, int y, long long key) { void TileBasemap::fetchTerrain(int z, int x, int y, long long key) {
if (z > kDemMaxZoom) return; // 高层级无 DEM → 维持平面
if (terrainInFlight_.count(key)) return; if (terrainInFlight_.count(key)) return;
// Terrarium 数据约到 z15更高层级取覆盖本块的祖先 DEM 瓦片,按经纬采样其子区域。
const int dz = std::min(z, kDemMaxZoom);
const int shift = z - dz;
const int dx = x >> shift, dy = y >> shift;
if (!terrainProbed_) {
terrainProbed_ = true;
qInfo() << "[basemap] 首次拉DEM 卫星z=" << z << " → DEMz=" << dz << "(" << dx << "," << dy
<< ")";
}
// 必须 https该 S3 桶对纯 http 返回 403实测 // 必须 https该 S3 桶对纯 http 返回 403实测
const QString url = const QString url =
QStringLiteral("https://s3.amazonaws.com/elevation-tiles-prod/terrarium/%1/%2/%3.png") QStringLiteral("https://s3.amazonaws.com/elevation-tiles-prod/terrarium/%1/%2/%3.png")
.arg(z) .arg(dz)
.arg(x) .arg(dx)
.arg(y); .arg(dy);
const int gen = generation_; const int gen = generation_;
terrainInFlight_.insert(key); terrainInFlight_.insert(key);
QNetworkReply* reply = nam_.get(QNetworkRequest(QUrl(url))); QNetworkReply* reply = nam_.get(QNetworkRequest(QUrl(url)));
connect(reply, &QNetworkReply::finished, this, [this, reply, key, z, x, y, gen]() { connect(reply, &QNetworkReply::finished, this, [this, reply, key, z, x, y, dz, dx, dy, gen]() {
reply->deleteLater(); reply->deleteLater();
terrainInFlight_.erase(key); terrainInFlight_.erase(key);
if (gen != generation_ || kind_ != Satellite) return; if (gen != generation_ || kind_ != Satellite) return;
@ -305,11 +314,12 @@ void TileBasemap::fetchTerrain(int z, int x, int y, long long key) {
qWarning() << "[basemap] DEM 解码失败" << reply->url().toString(); qWarning() << "[basemap] DEM 解码失败" << reply->url().toString();
return; return;
} }
applyTerrain(key, z, x, y, dem); applyTerrain(key, z, x, y, dz, dx, dy, dem);
}); });
} }
void TileBasemap::applyTerrain(long long key, int z, int x, int y, const QImage& dem) { void TileBasemap::applyTerrain(long long key, int sz, int sx, int sy, int dz, int dx, int dy,
const QImage& dem) {
auto it = placed_.find(key); auto it = placed_.find(key);
if (it == placed_.end()) return; if (it == placed_.end()) return;
vtkSmartPointer<vtkTexture> tex = it->second->GetTexture(); // 复用已贴卫星纹理 vtkSmartPointer<vtkTexture> tex = it->second->GetTexture(); // 复用已贴卫星纹理
@ -317,24 +327,35 @@ void TileBasemap::applyTerrain(long long key, int z, int x, int y, const QImage&
if (!haveBaseline_) { // 基准高程取首块中心 → 地形整体绕 z=0 起伏 if (!haveBaseline_) { // 基准高程取首块中心 → 地形整体绕 z=0 起伏
baseline_ = demElev(dem, 0.5, 0.5); baseline_ = demElev(dem, 0.5, 0.5);
haveBaseline_ = true; haveBaseline_ = true;
double mn = 1e30, mx = -1e30;
for (int yy = 0; yy < dem.height(); yy += 16)
for (int xx = 0; xx < dem.width(); xx += 16) {
const double e = demElev(dem, xx / double(dem.width() - 1),
yy / double(dem.height() - 1));
mn = std::min(mn, e);
mx = std::max(mx, e);
} }
auto warped = buildWarped(z, x, y, tex, dem); qInfo() << "[basemap] 地形启用 baseline=" << baseline_ << "m 首块高程范围[" << mn << ","
<< mx << "]m 起伏=" << (mx - mn) << "m";
}
auto warped = buildWarped(sz, sx, sy, dz, dx, dy, tex, dem);
scene_.renderer()->RemoveViewProp(it->second); scene_.renderer()->RemoveViewProp(it->second);
scene_.addActor(warped); scene_.addActor(warped);
it->second = warped; it->second = warped;
if (rw_) rw_->Render(); if (rw_) rw_->Render();
} }
vtkSmartPointer<vtkActor> TileBasemap::buildWarped(int z, int x, int y, vtkSmartPointer<vtkActor> TileBasemap::buildWarped(int sz, int sx, int sy, int dz, int dx, int dy,
vtkSmartPointer<vtkTexture> tex, vtkSmartPointer<vtkTexture> tex,
const QImage& dem) { const QImage& dem) {
const geopro::render::LonLatBox b = geopro::render::tileBounds(z, x, y); const geopro::render::LonLatBox sb = geopro::render::tileBounds(sz, sx, sy); // 卫星块(几何)
const auto sw = frame_->toLocal(b.south, b.west); const geopro::render::LonLatBox db = geopro::render::tileBounds(dz, dx, dy); // DEM 块(采样)
const auto se = frame_->toLocal(b.south, b.east); const auto sw = frame_->toLocal(sb.south, sb.west);
const auto nw = frame_->toLocal(b.north, b.west); const auto se = frame_->toLocal(sb.south, sb.east);
const double base = kGroundZ + (z - kMinZoom) * kZEps; const auto nw = frame_->toLocal(sb.north, sb.west);
const double base = kGroundZ + (sz - kMinZoom) * kZEps;
// PlaneSource(等距圆柱下平面插值即正确 x/y) + 自动 tcoord再按高程位移每点 Z。 // PlaneSource(等距圆柱下平面插值即正确 x/y) + 自动 tcoord再按各点真实经纬采 DEM 位移 Z。
vtkNew<vtkPlaneSource> plane; vtkNew<vtkPlaneSource> plane;
plane->SetOrigin(sw.x, sw.y, base); plane->SetOrigin(sw.x, sw.y, base);
plane->SetPoint1(se.x, se.y, base); plane->SetPoint1(se.x, se.y, base);
@ -346,11 +367,16 @@ vtkSmartPointer<vtkActor> TileBasemap::buildWarped(int z, int x, int y,
warped->DeepCopy(plane->GetOutput()); warped->DeepCopy(plane->GetOutput());
vtkDataArray* tc = warped->GetPointData()->GetTCoords(); vtkDataArray* tc = warped->GetPointData()->GetTCoords();
vtkPoints* pts = warped->GetPoints(); vtkPoints* pts = warped->GetPoints();
const double sLonSpan = sb.east - sb.west, sLatSpan = sb.north - sb.south;
const double dLonSpan = db.east - db.west, dLatSpan = db.north - db.south;
const vtkIdType n = pts->GetNumberOfPoints(); const vtkIdType n = pts->GetNumberOfPoints();
for (vtkIdType id = 0; id < n; ++id) { for (vtkIdType id = 0; id < n; ++id) {
double t[2]; double t[2];
tc->GetTuple(id, t); tc->GetTuple(id, t); // u:西0东1, v:南0北1
const double fx = t[0], fy = 1.0 - t[1]; // tcoord v: 南0北1 → fy: 北0南1(对齐 DEM 顶行=北) const double lon = sb.west + t[0] * sLonSpan;
const double lat = sb.south + t[1] * sLatSpan;
const double fx = (lon - db.west) / dLonSpan; // DEM 块内列比例
const double fy = (db.north - lat) / dLatSpan; // DEM 顶行=北 → fy
const double elev = demElev(dem, fx, fy); const double elev = demElev(dem, fx, fy);
double p[3]; double p[3];
pts->GetPoint(id, p); pts->GetPoint(id, p);

View File

@ -42,9 +42,11 @@ private:
bool computeView(double& centerLat, double& centerLon, int& zoom) const; bool computeView(double& centerLat, double& centerLon, int& zoom) const;
void fetchTile(int z, int x, int y, long long key); void fetchTile(int z, int x, int y, long long key);
void placeTile(long long key, int z, int x, int y, const QImage& img); void placeTile(long long key, int z, int x, int y, const QImage& img);
void fetchTerrain(int z, int x, int y, long long key); // 拉对应 DEM 瓦片(卫星模式) void fetchTerrain(int z, int x, int y, long long key); // 拉覆盖该瓦片的 DEM(z>15 取祖先块)
void applyTerrain(long long key, int z, int x, int y, const QImage& dem); // 平面块换起伏块 void applyTerrain(long long key, int sz, int sx, int sy, int dz, int dx, int dy,
vtkSmartPointer<vtkActor> buildWarped(int z, int x, int y, vtkSmartPointer<vtkTexture> tex, const QImage& dem); // 平面块换起伏块
vtkSmartPointer<vtkActor> buildWarped(int sz, int sx, int sy, int dz, int dx, int dy,
vtkSmartPointer<vtkTexture> tex,
const QImage& dem); // DEM 位移网格 + 卫星贴图 const QImage& dem); // DEM 位移网格 + 卫星贴图
static void onInteractionEnd(vtkObject*, unsigned long, void* clientData, void*); static void onInteractionEnd(vtkObject*, unsigned long, void* clientData, void*);
@ -60,6 +62,7 @@ private:
std::set<long long> terrainInFlight_; // DEM 瓦片在途请求 std::set<long long> terrainInFlight_; // DEM 瓦片在途请求
double baseline_ = 0.0; // 基准高程(首块中心),地形绕 z=0 起伏 double baseline_ = 0.0; // 基准高程(首块中心),地形绕 z=0 起伏
bool haveBaseline_ = false; bool haveBaseline_ = false;
bool terrainProbed_ = false; // 首次 fetchTerrain 打一行诊断日志
vtkSmartPointer<vtkInteractorObserver> styleObs_; // 持引用保证回调期有效 vtkSmartPointer<vtkInteractorObserver> styleObs_; // 持引用保证回调期有效
vtkSmartPointer<vtkCallbackCommand> observer_; vtkSmartPointer<vtkCallbackCommand> observer_;
bool refreshing_ = false; bool refreshing_ = false;