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 打一行诊断日志