From 63e7874175f91275f3abce90102f6dd5a56345e5 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Wed, 17 Jun 2026 13:47:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(vtk):=20=E5=8A=A0=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E9=99=90=E6=B5=81(=E6=9C=80=E5=A4=9A8=E5=B9=B6=E5=8F=91,?= =?UTF-8?q?=E6=B2=BB=E7=93=A6=E7=89=87=E6=9A=B4=E5=8F=91=E9=A5=B1=E5=92=8C?= =?UTF-8?q?=E5=8D=95=E5=9F=9F=E5=90=8D=E8=87=B4=E5=8A=A0=E8=BD=BD=E4=B8=8D?= =?UTF-8?q?=E8=BF=9B/=E5=8D=A1=E6=AD=BB)+=E7=B2=BE=E7=BB=86=E4=BC=98?= =?UTF-8?q?=E5=85=88=E4=BA=8E=E7=B2=97=E5=BA=95=E6=8E=92=E9=98=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/TileBasemap.cpp | 39 +++++++++++++++++++++++++++++---------- src/app/TileBasemap.hpp | 9 +++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/app/TileBasemap.cpp b/src/app/TileBasemap.cpp index df73150..1bcd6f5 100644 --- a/src/app/TileBasemap.cpp +++ b/src/app/TileBasemap.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ constexpr int kRadius = 4; // 回退用:算不出可视范围时中 constexpr int kMaxTilesPerSide = 13; // 单边瓦片上限(防倾斜时可视范围过大拉爆请求) constexpr int kBaseZoom = 13; // 远处粗底层级(单块~4.9km) constexpr int kBaseRadius = 3; // 粗底半径 ±3 → 7x7 覆盖~34km,填到天边 +constexpr int kMaxConcurrent = 8; // 瓦片请求最大并发(防暴发饱和单域名连接) constexpr int kMinZoom = 3; constexpr int kMaxZoom = 18; constexpr double kGroundZ = 0.0; // 底图置于 z=0 地面参考(剖面深度向下为负,落其下) @@ -132,6 +134,26 @@ void TileBasemap::onInteractionEnd(vtkObject*, unsigned long, void* clientData, if (auto* self = static_cast(clientData)) self->refresh(); } +void TileBasemap::enqueueGet(const QString& url, std::function onDone) { + netQueue_.push_back({url, std::move(onDone)}); + pumpNetQueue(); +} + +void TileBasemap::pumpNetQueue() { + while (netInFlight_ < kMaxConcurrent && !netQueue_.empty()) { + const PendingGet req = std::move(netQueue_.front()); + netQueue_.pop_front(); + ++netInFlight_; + QNetworkReply* reply = nam_.get(QNetworkRequest(QUrl(req.url))); + auto cb = req.cb; + connect(reply, &QNetworkReply::finished, this, [this, reply, cb]() { + cb(reply); // 回调内部 deleteLater + 处理 + --netInFlight_; + pumpNetQueue(); + }); + } +} + void TileBasemap::hide() { show(Hidden); } void TileBasemap::show(Kind kind) { @@ -143,6 +165,7 @@ void TileBasemap::show(Kind kind) { baseTiles_.clear(); baseLoaded_ = false; inFlight_.clear(); + netQueue_.clear(); // 丢弃换源前排队中的请求(在途的按 gen 自然作废) desired_.clear(); // demCache_/texCache_ 跨隐藏-重选保留 → 重选地图秒出,不重拉。 haveBaseline_ = false; // 数据可能已换位置 → 重算基准高程 @@ -152,8 +175,8 @@ void TileBasemap::show(Kind kind) { if (rw_) rw_->Render(); return; } - ensureBaseLayer(); // 先铺远处粗底(填到天边),再叠近处精细 - refresh(); + refresh(); // 先排近处精细(优先加载,用户最先看到) + ensureBaseLayer(); // 再排远处粗底(背景,排队在后) } bool TileBasemap::computeView(double& centerLat, double& centerLon, int& zoom) const { @@ -334,8 +357,7 @@ void TileBasemap::fetchTile(int z, int x, int y, long long key) { const int gen = generation_; inFlight_.insert(key); - QNetworkReply* reply = nam_.get(QNetworkRequest(QUrl(url))); - connect(reply, &QNetworkReply::finished, this, [this, reply, key, z, x, y, gen]() { + enqueueGet(url, [this, key, z, x, y, gen](QNetworkReply* reply) { reply->deleteLater(); // inFlight 保持到瓦片最终落地(起伏/平面),使旧层在新块就位前不被清理 → 无空白闪烁。 QImage img; @@ -423,8 +445,7 @@ void TileBasemap::fetchBaseTile(int z, int x, int y) { 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]() { + enqueueGet(durl, [this, demKey, gen, placeBase](QNetworkReply* dr) { dr->deleteLater(); if (gen != generation_) return; QImage dem; @@ -445,8 +466,7 @@ void TileBasemap::fetchBaseTile(int z, int x, int y) { "&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]() { + enqueueGet(url, [this, key, gen, onTex](QNetworkReply* reply) { reply->deleteLater(); if (gen != generation_ || kind_ != Satellite) return; QImage img; @@ -520,8 +540,7 @@ void TileBasemap::fetchTerrain(int z, int x, int y, long long key, vtkSmartPoint .arg(dy) .arg(QString::fromLatin1(kMapboxToken)); const int gen = generation_; - QNetworkReply* reply = nam_.get(QNetworkRequest(QUrl(url))); - connect(reply, &QNetworkReply::finished, this, [this, reply, key, demKey, gen, place]() { + enqueueGet(url, [this, key, demKey, gen, place](QNetworkReply* reply) { reply->deleteLater(); if (gen != generation_ || kind_ != Satellite || desired_.find(key) == desired_.end() || placed_.count(key)) { diff --git a/src/app/TileBasemap.hpp b/src/app/TileBasemap.hpp index 4e29391..50540dc 100644 --- a/src/app/TileBasemap.hpp +++ b/src/app/TileBasemap.hpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include #include @@ -17,6 +19,7 @@ class vtkTexture; class vtkRenderWindow; class vtkInteractorObserver; class vtkCallbackCommand; +class QNetworkReply; namespace geopro::render { class Scene; } namespace geopro::core { class GeoLocalFrame; } @@ -55,6 +58,9 @@ private: vtkSmartPointer buildWarped(int sz, int sx, int sy, int dz, int dx, int dy, vtkSmartPointer tex, const QImage& dem); // DEM 位移网格 + 卫星贴图 + void enqueueGet(const QString& url, + std::function onDone); // 限并发取瓦片(回调内负责 deleteLater) + void pumpNetQueue(); static void onInteractionEnd(vtkObject*, unsigned long, void* clientData, void*); geopro::render::Scene& scene_; @@ -70,6 +76,9 @@ private: std::map> texCache_; // 影像纹理缓存,重选/缩放回看免重拉 std::vector> baseTiles_; // 远处粗底层(持久,不随相机purge) bool baseLoaded_ = false; + struct PendingGet { QString url; std::function cb; }; + std::deque netQueue_; // 限并发请求队列(防瓦片暴发饱和卡死) + int netInFlight_ = 0; double baseline_ = 0.0; // 基准高程(首块中心),地形绕 z=0 起伏 bool haveBaseline_ = false; bool terrainProbed_ = false; // 首次 fetchTerrain 打一行诊断日志