fix(vtk): 加远处粗底图层(z13~34km,随地形起伏,持久不purge)填到天边治倾斜露黑边;基准高程base/detail共用保连续
This commit is contained in:
parent
52f7a7d5e8
commit
b5bab42825
|
|
@ -41,6 +41,8 @@ namespace {
|
|||
const char* kTk = "aca91d8c9f59a4f779f39061b8a07737";
|
||||
constexpr int kRadius = 4; // 回退用:算不出可视范围时中心 ±4
|
||||
constexpr int kMaxTilesPerSide = 13; // 单边瓦片上限(防倾斜时可视范围过大拉爆请求)
|
||||
constexpr int kBaseZoom = 13; // 远处粗底层级(单块~4.9km)
|
||||
constexpr int kBaseRadius = 3; // 粗底半径 ±3 → 7x7 覆盖~34km,填到天边
|
||||
constexpr int kMinZoom = 3;
|
||||
constexpr int kMaxZoom = 18;
|
||||
constexpr double kGroundZ = 0.0; // 底图置于 z=0 地面参考(剖面深度向下为负,落其下)
|
||||
|
|
@ -137,15 +139,20 @@ void TileBasemap::show(Kind kind) {
|
|||
++generation_; // 旧回包(含换源前的层)按 generation 丢弃
|
||||
for (auto& kv : placed_) scene_.renderer()->RemoveViewProp(kv.second);
|
||||
placed_.clear();
|
||||
for (auto& a : baseTiles_) scene_.renderer()->RemoveViewProp(a);
|
||||
baseTiles_.clear();
|
||||
baseLoaded_ = false;
|
||||
inFlight_.clear();
|
||||
desired_.clear();
|
||||
// demCache_/texCache_ 跨隐藏-重选保留 → 重选地图秒出,不重拉。
|
||||
haveBaseline_ = false; // 数据可能已换位置 → 重算基准高程
|
||||
terrainProbed_ = false;
|
||||
kind_ = kind;
|
||||
if (kind == Hidden) {
|
||||
if (rw_) rw_->Render();
|
||||
return;
|
||||
}
|
||||
ensureBaseLayer(); // 先铺远处粗底(填到天边),再叠近处精细
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
|
@ -362,6 +369,94 @@ void TileBasemap::placeActor(long long key, vtkSmartPointer<vtkActor> actor) {
|
|||
placed_[key] = actor;
|
||||
}
|
||||
|
||||
void TileBasemap::ensureBaseline(const QImage& dem) {
|
||||
if (haveBaseline_) return; // 仅首块定基准 → base/detail 共用同一基准,地形连续无断层
|
||||
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 起伏=" << (mx - mn) << "m";
|
||||
}
|
||||
|
||||
void TileBasemap::ensureBaseLayer() {
|
||||
if (baseLoaded_ || kind_ != Satellite) return;
|
||||
baseLoaded_ = true;
|
||||
const auto c = frame_->toLatLon(0.0, 0.0); // 数据中心(show 在重锚后调 → 已对准数据)
|
||||
const geopro::render::TileXY ct = geopro::render::lonLatToTile(c.lon, c.lat, kBaseZoom);
|
||||
const int n = 1 << kBaseZoom;
|
||||
for (int dy = -kBaseRadius; dy <= kBaseRadius; ++dy)
|
||||
for (int dx = -kBaseRadius; dx <= kBaseRadius; ++dx) {
|
||||
const int tx = ct.x + dx, ty = ct.y + dy;
|
||||
if (tx < 0 || ty < 0 || tx >= n || ty >= n) continue;
|
||||
fetchBaseTile(kBaseZoom, tx, ty);
|
||||
}
|
||||
}
|
||||
|
||||
void TileBasemap::fetchBaseTile(int z, int x, int y) {
|
||||
const int gen = generation_;
|
||||
const long long key = tileKey(z, x, y);
|
||||
|
||||
// 有了卫星纹理后 → 取 DEM(base zoom ≤ kDemMaxZoom, 同级) → warp 成持久粗底块。
|
||||
auto onTex = [this, z, x, y, gen](vtkSmartPointer<vtkTexture> tex) {
|
||||
const long long demKey = tileKey(z, x, y);
|
||||
auto placeBase = [this, z, x, y, gen, tex](const QImage* dem) {
|
||||
if (gen != generation_ || kind_ != Satellite) return; // 已隐藏/换源 → 丢弃
|
||||
vtkSmartPointer<vtkActor> a;
|
||||
if (dem && !dem->isNull()) {
|
||||
ensureBaseline(*dem);
|
||||
a = buildWarped(z, x, y, z, x, y, tex, *dem);
|
||||
} else {
|
||||
a = buildFlat(z, x, y, tex);
|
||||
}
|
||||
a->SetUseBounds(false); // 粗底不参与包围盒/取景
|
||||
scene_.addActor(a);
|
||||
baseTiles_.push_back(a);
|
||||
if (rw_) rw_->Render();
|
||||
};
|
||||
auto dc = demCache_.find(demKey);
|
||||
if (dc != demCache_.end()) { placeBase(&dc->second); return; }
|
||||
const QString durl =
|
||||
QStringLiteral("https://api.mapbox.com/v4/mapbox.terrain-rgb/%1/%2/%3.pngraw?access_token=%4")
|
||||
.arg(z).arg(x).arg(y).arg(QString::fromLatin1(kMapboxToken));
|
||||
QNetworkReply* dr = nam_.get(QNetworkRequest(QUrl(durl)));
|
||||
connect(dr, &QNetworkReply::finished, this, [this, dr, demKey, gen, placeBase]() {
|
||||
dr->deleteLater();
|
||||
if (gen != generation_) return;
|
||||
QImage dem;
|
||||
if (dr->error() == QNetworkReply::NoError && dem.loadFromData(dr->readAll())) {
|
||||
demCache_[demKey] = dem;
|
||||
placeBase(&dem);
|
||||
} else {
|
||||
placeBase(nullptr);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
auto tc = texCache_.find(key);
|
||||
if (tc != texCache_.end()) { onTex(tc->second); return; }
|
||||
const int sub = (x + y) % 8;
|
||||
const QString url =
|
||||
QStringLiteral("http://t%1.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile"
|
||||
"&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix=%2&TileRow=%3"
|
||||
"&TileCol=%4&style=default&format=tiles&tk=%5")
|
||||
.arg(sub).arg(z).arg(y).arg(x).arg(QString::fromLatin1(kTk));
|
||||
QNetworkReply* reply = nam_.get(QNetworkRequest(QUrl(url)));
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply, key, gen, onTex]() {
|
||||
reply->deleteLater();
|
||||
if (gen != generation_ || kind_ != Satellite) return;
|
||||
QImage img;
|
||||
if (reply->error() != QNetworkReply::NoError || !img.loadFromData(reply->readAll())) return;
|
||||
auto tex = makeTexture(img);
|
||||
texCache_[key] = tex;
|
||||
onTex(tex);
|
||||
});
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkActor> TileBasemap::buildFlat(int z, int x, int y,
|
||||
vtkSmartPointer<vtkTexture> tex) {
|
||||
const geopro::render::LonLatBox b = geopro::render::tileBounds(z, x, y);
|
||||
|
|
@ -395,20 +490,7 @@ void TileBasemap::fetchTerrain(int z, int x, int y, long long key, vtkSmartPoint
|
|||
// 落地一块瓦片:DEM 有效→起伏,否则→平面兜底;并推进 inFlight/清理。
|
||||
auto place = [this, key, z, x, y, dz, dx, dy, tex](const QImage* dem) {
|
||||
if (dem && !dem->isNull()) {
|
||||
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 起伏=" << (mx - mn)
|
||||
<< "m";
|
||||
}
|
||||
ensureBaseline(*dem); // 首块定基准(base/detail 共用)→ 地形连续
|
||||
placeActor(key, buildWarped(z, x, y, dz, dx, dy, tex, *dem));
|
||||
} else {
|
||||
placeActor(key, buildFlat(z, x, y, tex)); // DEM 拉不到 → 平面兜底
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <vtkSmartPointer.h>
|
||||
|
||||
|
|
@ -46,6 +47,9 @@ private:
|
|||
void fetchTerrain(int z, int x, int y, long long key,
|
||||
vtkSmartPointer<vtkTexture> tex); // 拉覆盖该瓦片的 DEM(z>15 取祖先块)后落地
|
||||
void placeActor(long long key, vtkSmartPointer<vtkActor> actor);
|
||||
void ensureBaseline(const QImage& dem); // 首块 DEM 定基准高程(base/detail 共用→地形连续)
|
||||
void ensureBaseLayer(); // 远处粗底图层(填到天边,治倾斜露黑边)
|
||||
void fetchBaseTile(int z, int x, int y); // 单块粗底(sat+DEM→warp,持久不purge)
|
||||
vtkSmartPointer<vtkActor> buildFlat(int z, int x, int y,
|
||||
vtkSmartPointer<vtkTexture> tex); // 平面瓦片(DEM 兜底)
|
||||
vtkSmartPointer<vtkActor> buildWarped(int sz, int sx, int sy, int dz, int dx, int dy,
|
||||
|
|
@ -64,6 +68,8 @@ private:
|
|||
std::set<long long> inFlight_; // 在途瓦片(续到起伏/平面最终落地)
|
||||
std::map<long long, QImage> demCache_; // DEM 块缓存(key=DEMz/x/y),跨隐藏/重选复用
|
||||
std::map<long long, vtkSmartPointer<vtkTexture>> texCache_; // 影像纹理缓存,重选/缩放回看免重拉
|
||||
std::vector<vtkSmartPointer<vtkActor>> baseTiles_; // 远处粗底层(持久,不随相机purge)
|
||||
bool baseLoaded_ = false;
|
||||
double baseline_ = 0.0; // 基准高程(首块中心),地形绕 z=0 起伏
|
||||
bool haveBaseline_ = false;
|
||||
bool terrainProbed_ = false; // 首次 fetchTerrain 打一行诊断日志
|
||||
|
|
|
|||
Loading…
Reference in New Issue