diff --git a/src/app/TileBasemap.cpp b/src/app/TileBasemap.cpp index 671b115..f095692 100644 --- a/src/app/TileBasemap.cpp +++ b/src/app/TileBasemap.cpp @@ -114,6 +114,7 @@ void TileBasemap::show(Kind kind) { inFlight_.clear(); terrainInFlight_.clear(); desired_.clear(); + terrainProbed_ = false; kind_ = kind; if (kind == Hidden) { 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) { - if (z > kDemMaxZoom) return; // 高层级无 DEM → 维持平面 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(实测)。 const QString url = QStringLiteral("https://s3.amazonaws.com/elevation-tiles-prod/terrarium/%1/%2/%3.png") - .arg(z) - .arg(x) - .arg(y); + .arg(dz) + .arg(dx) + .arg(dy); const int gen = generation_; terrainInFlight_.insert(key); 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(); terrainInFlight_.erase(key); 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(); 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); if (it == placed_.end()) return; vtkSmartPointer 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 起伏 baseline_ = demElev(dem, 0.5, 0.5); 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); + } + qInfo() << "[basemap] 地形启用 baseline=" << baseline_ << "m 首块高程范围[" << mn << "," + << mx << "]m 起伏=" << (mx - mn) << "m"; } - auto warped = buildWarped(z, x, y, tex, dem); + auto warped = buildWarped(sz, sx, sy, dz, dx, dy, tex, dem); scene_.renderer()->RemoveViewProp(it->second); scene_.addActor(warped); it->second = warped; if (rw_) rw_->Render(); } -vtkSmartPointer TileBasemap::buildWarped(int z, int x, int y, +vtkSmartPointer TileBasemap::buildWarped(int sz, int sx, int sy, int dz, int dx, int dy, vtkSmartPointer tex, const QImage& dem) { - const geopro::render::LonLatBox b = geopro::render::tileBounds(z, x, y); - const auto sw = frame_->toLocal(b.south, b.west); - const auto se = frame_->toLocal(b.south, b.east); - const auto nw = frame_->toLocal(b.north, b.west); - const double base = kGroundZ + (z - kMinZoom) * kZEps; + const geopro::render::LonLatBox sb = geopro::render::tileBounds(sz, sx, sy); // 卫星块(几何) + const geopro::render::LonLatBox db = geopro::render::tileBounds(dz, dx, dy); // DEM 块(采样) + const auto sw = frame_->toLocal(sb.south, sb.west); + const auto se = frame_->toLocal(sb.south, sb.east); + 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 plane; plane->SetOrigin(sw.x, sw.y, base); plane->SetPoint1(se.x, se.y, base); @@ -346,11 +367,16 @@ vtkSmartPointer TileBasemap::buildWarped(int z, int x, int y, warped->DeepCopy(plane->GetOutput()); vtkDataArray* tc = warped->GetPointData()->GetTCoords(); 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(); for (vtkIdType id = 0; id < n; ++id) { double t[2]; - tc->GetTuple(id, t); - const double fx = t[0], fy = 1.0 - t[1]; // tcoord v: 南0北1 → fy: 北0南1(对齐 DEM 顶行=北) + tc->GetTuple(id, t); // u:西0东1, v:南0北1 + 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); double p[3]; pts->GetPoint(id, p); diff --git a/src/app/TileBasemap.hpp b/src/app/TileBasemap.hpp index d5873e1..3020d4e 100644 --- a/src/app/TileBasemap.hpp +++ b/src/app/TileBasemap.hpp @@ -42,9 +42,11 @@ private: bool computeView(double& centerLat, double& centerLon, int& zoom) const; 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 fetchTerrain(int z, int x, int y, long long key); // 拉对应 DEM 瓦片(卫星模式) - void applyTerrain(long long key, int z, int x, int y, const QImage& dem); // 平面块换起伏块 - vtkSmartPointer buildWarped(int z, int x, int y, vtkSmartPointer tex, + void fetchTerrain(int z, int x, int y, long long key); // 拉覆盖该瓦片的 DEM(z>15 取祖先块) + void applyTerrain(long long key, int sz, int sx, int sy, int dz, int dx, int dy, + const QImage& dem); // 平面块换起伏块 + vtkSmartPointer buildWarped(int sz, int sx, int sy, int dz, int dx, int dy, + vtkSmartPointer tex, const QImage& dem); // DEM 位移网格 + 卫星贴图 static void onInteractionEnd(vtkObject*, unsigned long, void* clientData, void*); @@ -60,6 +62,7 @@ private: std::set terrainInFlight_; // DEM 瓦片在途请求 double baseline_ = 0.0; // 基准高程(首块中心),地形绕 z=0 起伏 bool haveBaseline_ = false; + bool terrainProbed_ = false; // 首次 fetchTerrain 打一行诊断日志 vtkSmartPointer styleObs_; // 持引用保证回调期有效 vtkSmartPointer observer_; bool refreshing_ = false;