feat/vtk-3d-view #7

Merged
gaozheng merged 301 commits from feat/vtk-3d-view into main 2026-06-27 18:43:52 +08:00
2 changed files with 38 additions and 10 deletions
Showing only changes of commit 63e7874175 - Show all commits

View File

@ -3,6 +3,7 @@
#include <algorithm>
#include <cmath>
#include <cstring>
#include <utility>
#include <QDebug>
#include <QImage>
@ -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<TileBasemap*>(clientData)) self->refresh();
}
void TileBasemap::enqueueGet(const QString& url, std::function<void(QNetworkReply*)> 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)) {

View File

@ -4,6 +4,8 @@
#include <QNetworkAccessManager>
#include <QObject>
#include <deque>
#include <functional>
#include <map>
#include <memory>
#include <set>
@ -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<vtkActor> buildWarped(int sz, int sx, int sy, int dz, int dx, int dy,
vtkSmartPointer<vtkTexture> tex,
const QImage& dem); // DEM 位移网格 + 卫星贴图
void enqueueGet(const QString& url,
std::function<void(QNetworkReply*)> onDone); // 限并发取瓦片(回调内负责 deleteLater)
void pumpNetQueue();
static void onInteractionEnd(vtkObject*, unsigned long, void* clientData, void*);
geopro::render::Scene& scene_;
@ -70,6 +76,9 @@ private:
std::map<long long, vtkSmartPointer<vtkTexture>> texCache_; // 影像纹理缓存,重选/缩放回看免重拉
std::vector<vtkSmartPointer<vtkActor>> baseTiles_; // 远处粗底层(持久,不随相机purge)
bool baseLoaded_ = false;
struct PendingGet { QString url; std::function<void(QNetworkReply*)> cb; };
std::deque<PendingGet> netQueue_; // 限并发请求队列(防瓦片暴发饱和卡死)
int netInFlight_ = 0;
double baseline_ = 0.0; // 基准高程(首块中心),地形绕 z=0 起伏
bool haveBaseline_ = false;
bool terrainProbed_ = false; // 首次 fetchTerrain 打一行诊断日志