fix(vtk): 统一相机取景用数据包围盒-预设按钮(上下左右)不再被底图推远; 基准高程锚数据中心(确定性)修垂直偏移; 滚轮交互防抖140ms治缩放卡顿
This commit is contained in:
parent
67eaade7bd
commit
69a81b2eac
|
|
@ -8,6 +8,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QTimer>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
@ -107,7 +108,11 @@ long long TileBasemap::tileKey(int z, int x, int y) {
|
||||||
|
|
||||||
TileBasemap::TileBasemap(geopro::render::Scene& scene, vtkRenderWindow* rw,
|
TileBasemap::TileBasemap(geopro::render::Scene& scene, vtkRenderWindow* rw,
|
||||||
std::shared_ptr<geopro::core::GeoLocalFrame> frame, QObject* parent)
|
std::shared_ptr<geopro::core::GeoLocalFrame> frame, QObject* parent)
|
||||||
: QObject(parent), scene_(scene), rw_(rw), frame_(std::move(frame)) {}
|
: QObject(parent), scene_(scene), rw_(rw), frame_(std::move(frame)) {
|
||||||
|
refreshTimer_ = new QTimer(this);
|
||||||
|
refreshTimer_->setSingleShot(true);
|
||||||
|
connect(refreshTimer_, &QTimer::timeout, this, [this]() { refresh(); });
|
||||||
|
}
|
||||||
|
|
||||||
TileBasemap::~TileBasemap() {
|
TileBasemap::~TileBasemap() {
|
||||||
if (styleObs_ && observer_) styleObs_->RemoveObserver(observer_);
|
if (styleObs_ && observer_) styleObs_->RemoveObserver(observer_);
|
||||||
|
|
@ -128,7 +133,8 @@ void TileBasemap::ensureObserver() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileBasemap::onInteractionEnd(vtkObject*, unsigned long, void* clientData, void*) {
|
void TileBasemap::onInteractionEnd(vtkObject*, unsigned long, void* clientData, void*) {
|
||||||
if (auto* self = static_cast<TileBasemap*>(clientData)) self->refresh();
|
// 防抖:交互(滚轮/旋转/平移)停止 ~140ms 后才刷新四叉树,避免每格事件都重算卡顿。
|
||||||
|
if (auto* self = static_cast<TileBasemap*>(clientData)) self->refreshTimer_->start(140);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileBasemap::enqueueGet(const QString& url, std::function<void(QNetworkReply*)> onDone) {
|
void TileBasemap::enqueueGet(const QString& url, std::function<void(QNetworkReply*)> onDone) {
|
||||||
|
|
@ -348,18 +354,19 @@ void TileBasemap::placeActor(long long key, vtkSmartPointer<vtkActor> actor) {
|
||||||
placed_[key] = actor;
|
placed_[key] = actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileBasemap::ensureBaseline(const QImage& dem) {
|
void TileBasemap::ensureBaseline(int z, int x, int y, const QImage& dem) {
|
||||||
if (haveBaseline_) return; // 仅首块定基准 → base/detail 共用同一基准,地形连续无断层
|
if (haveBaseline_) return;
|
||||||
baseline_ = demElev(dem, 0.5, 0.5);
|
// 基准必须锚"数据中心"的地面高程(确定性):只有含数据中心的块来定,按中心点采样。
|
||||||
|
// 否则四叉树里随便哪块先到都定基准 → 地形整体偏移 → 三维剖面相对地面忽上忽下。
|
||||||
|
const auto c = frame_->toLatLon(0.0, 0.0); // 数据中心经纬
|
||||||
|
const geopro::render::TileXY ct = geopro::render::lonLatToTile(c.lon, c.lat, z);
|
||||||
|
if (ct.x != x || ct.y != y) return; // 此块不含数据中心 → 不用它定基准
|
||||||
|
const geopro::render::LonLatBox b = geopro::render::tileBounds(z, x, y);
|
||||||
|
const double fx = (c.lon - b.west) / (b.east - b.west); // 数据中心在块内的列比例
|
||||||
|
const double fy = (b.north - c.lat) / (b.north - b.south); // 行比例(顶=北)
|
||||||
|
baseline_ = demElev(dem, fx, fy);
|
||||||
haveBaseline_ = true;
|
haveBaseline_ = true;
|
||||||
double mn = 1e30, mx = -1e30;
|
qInfo() << "[basemap] 地形启用 baseline(数据中心)=" << baseline_ << "m z=" << z;
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vtkSmartPointer<vtkActor> TileBasemap::buildFlat(int z, int x, int y,
|
vtkSmartPointer<vtkActor> TileBasemap::buildFlat(int z, int x, int y,
|
||||||
|
|
@ -396,7 +403,7 @@ void TileBasemap::fetchTerrain(int z, int x, int y, long long key, vtkSmartPoint
|
||||||
// 落地一块瓦片:DEM 有效→起伏,否则→平面兜底;并推进 inFlight/清理。
|
// 落地一块瓦片:DEM 有效→起伏,否则→平面兜底;并推进 inFlight/清理。
|
||||||
auto place = [this, key, z, x, y, dz, dx, dy, tex](const QImage* dem) {
|
auto place = [this, key, z, x, y, dz, dx, dy, tex](const QImage* dem) {
|
||||||
if (dem && !dem->isNull()) {
|
if (dem && !dem->isNull()) {
|
||||||
ensureBaseline(*dem); // 首块定基准(base/detail 共用)→ 地形连续
|
ensureBaseline(dz, dx, dy, *dem); // 用含数据中心的 DEM 块定基准(确定性)
|
||||||
placeActor(key, buildWarped(z, x, y, dz, dx, dy, tex, *dem));
|
placeActor(key, buildWarped(z, x, y, dz, dx, dy, tex, *dem));
|
||||||
} else {
|
} else {
|
||||||
placeActor(key, buildFlat(z, x, y, tex)); // DEM 拉不到 → 平面兜底
|
placeActor(key, buildFlat(z, x, y, tex)); // DEM 拉不到 → 平面兜底
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ class vtkRenderWindow;
|
||||||
class vtkInteractorObserver;
|
class vtkInteractorObserver;
|
||||||
class vtkCallbackCommand;
|
class vtkCallbackCommand;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
|
class QTimer;
|
||||||
namespace geopro::render { class Scene; }
|
namespace geopro::render { class Scene; }
|
||||||
namespace geopro::core { class GeoLocalFrame; }
|
namespace geopro::core { class GeoLocalFrame; }
|
||||||
|
|
||||||
|
|
@ -49,7 +50,7 @@ private:
|
||||||
void fetchTerrain(int z, int x, int y, long long key,
|
void fetchTerrain(int z, int x, int y, long long key,
|
||||||
vtkSmartPointer<vtkTexture> tex); // 拉覆盖该瓦片的 DEM(z>15 取祖先块)后落地
|
vtkSmartPointer<vtkTexture> tex); // 拉覆盖该瓦片的 DEM(z>15 取祖先块)后落地
|
||||||
void placeActor(long long key, vtkSmartPointer<vtkActor> actor);
|
void placeActor(long long key, vtkSmartPointer<vtkActor> actor);
|
||||||
void ensureBaseline(const QImage& dem); // 首块 DEM 定基准高程(各层级共用→地形连续)
|
void ensureBaseline(int z, int x, int y, const QImage& dem); // 用含数据中心的块定基准(确定性)
|
||||||
vtkSmartPointer<vtkActor> buildFlat(int z, int x, int y,
|
vtkSmartPointer<vtkActor> buildFlat(int z, int x, int y,
|
||||||
vtkSmartPointer<vtkTexture> tex); // 平面瓦片(DEM 兜底)
|
vtkSmartPointer<vtkTexture> tex); // 平面瓦片(DEM 兜底)
|
||||||
vtkSmartPointer<vtkActor> buildWarped(int sz, int sx, int sy, int dz, int dx, int dy,
|
vtkSmartPointer<vtkActor> buildWarped(int sz, int sx, int sy, int dz, int dx, int dy,
|
||||||
|
|
@ -84,6 +85,7 @@ private:
|
||||||
bool terrainProbed_ = false; // 首次 fetchTerrain 打一行诊断日志
|
bool terrainProbed_ = false; // 首次 fetchTerrain 打一行诊断日志
|
||||||
vtkSmartPointer<vtkInteractorObserver> styleObs_; // 持引用保证回调期有效
|
vtkSmartPointer<vtkInteractorObserver> styleObs_; // 持引用保证回调期有效
|
||||||
vtkSmartPointer<vtkCallbackCommand> observer_;
|
vtkSmartPointer<vtkCallbackCommand> observer_;
|
||||||
|
QTimer* refreshTimer_ = nullptr; // 交互防抖:滚轮/旋转停止后才刷新四叉树(避免每格卡)
|
||||||
bool refreshing_ = false;
|
bool refreshing_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,11 @@ void VtkSceneView::setAxes(geopro::controller::AxesMode mode, geopro::controller
|
||||||
}
|
}
|
||||||
|
|
||||||
void VtkSceneView::applyCameraView(geopro::controller::ViewDir dir) {
|
void VtkSceneView::applyCameraView(geopro::controller::ViewDir dir) {
|
||||||
geopro::render::applyView(scene_.renderer(), toRenderViewDir(dir));
|
geopro::render::applyView(scene_.renderer(), toRenderViewDir(dir)); // 设朝向(内部 ResetCamera 含底图)
|
||||||
|
double bounds[6];
|
||||||
|
if (computeDataBounds(bounds))
|
||||||
|
scene_.renderer()->ResetCamera(bounds); // 重新取景到数据(否则被~公里级底图推到超远)
|
||||||
|
scene_.renderer()->ResetCameraClippingRange(); // 裁剪面含底图
|
||||||
if (renderWindow_) renderWindow_->Render();
|
if (renderWindow_) renderWindow_->Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue