fix(vtk): A-复刻原版垂直配准.剖面z用真实高程+g.y(原版data.y=高程,非深度);地形改真实高程(去基准减法)x同一VE;剖面与地形同系对齐,剖面顶≈地表露出地面
This commit is contained in:
parent
692ee057ab
commit
c3f72fdc8d
|
|
@ -58,7 +58,6 @@ const char* kMapboxToken =
|
|||
"pk.eyJ1IjoidGJ1c2FuIiwiYSI6ImNtZjY2emZneDBkY24ybXB4cmpvdmwzNWYifQ.h6tcQ380WN5AW6fZr08how";
|
||||
constexpr int kDemMaxZoom = 15;
|
||||
constexpr int kTerrainGrid = 32; // 每瓦片网格分辨率(33x33 顶点)
|
||||
constexpr double kTerrainExag = 1.0; // 地形垂向夸张(1=真实高程)
|
||||
|
||||
// key 打包:z<<44 | x<<22 | y(z≤18, x/y<2^18 < 2^22)。
|
||||
void unpackKey(long long key, int& z, int& x, int& y) {
|
||||
|
|
@ -175,43 +174,19 @@ void TileBasemap::show(Kind kind) {
|
|||
netQueue_.clear(); // 丢弃换源前排队中的请求(在途的按 gen 自然作废)
|
||||
desired_.clear();
|
||||
// demCache_/texCache_ 跨隐藏-重选保留 → 重选地图秒出,不重拉。
|
||||
haveBaseline_ = false; // 数据可能已换位置 → 重算基准高程
|
||||
terrainProbed_ = false;
|
||||
kind_ = kind;
|
||||
if (kind == Hidden) {
|
||||
requestRender();
|
||||
return;
|
||||
}
|
||||
primeBaselineThenRefresh(); // 先定基准高程再铺瓦片:避免先到的粗块用错基准把地形整体抬高
|
||||
refresh(); // 四叉树覆盖:近细远粗一次铺满(地形按真实高程,与剖面同系)
|
||||
}
|
||||
|
||||
void TileBasemap::primeBaselineThenRefresh() {
|
||||
if (haveBaseline_ || kind_ != Satellite) { refresh(); return; }
|
||||
// 先拉"含数据中心"的 DEM 块定基准(z15),确保任何瓦片 warp 前基准已就位、全场一致。
|
||||
const auto c = frame_->toLatLon(0.0, 0.0);
|
||||
const int z = kDemMaxZoom;
|
||||
const geopro::render::TileXY t = geopro::render::lonLatToTile(c.lon, c.lat, z);
|
||||
const long long demKey = tileKey(z, t.x, t.y);
|
||||
auto cached = demCache_.find(demKey);
|
||||
if (cached != demCache_.end()) {
|
||||
ensureBaseline(z, t.x, t.y, cached->second);
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
const int gen = generation_;
|
||||
const QString url =
|
||||
QStringLiteral("https://api.mapbox.com/v4/mapbox.terrain-rgb/%1/%2/%3.pngraw?access_token=%4")
|
||||
.arg(z).arg(t.x).arg(t.y).arg(QString::fromLatin1(kMapboxToken));
|
||||
enqueueGet(url, [this, z, t, demKey, gen](QNetworkReply* r) {
|
||||
r->deleteLater();
|
||||
if (gen != generation_) return;
|
||||
QImage dem;
|
||||
if (r->error() == QNetworkReply::NoError && dem.loadFromData(r->readAll())) {
|
||||
demCache_[demKey] = dem;
|
||||
ensureBaseline(z, t.x, t.y, dem);
|
||||
}
|
||||
refresh(); // 成功→基准就位;失败→baseline=0 兜底,但仍全场一致
|
||||
});
|
||||
void TileBasemap::setVerticalExaggeration(double ve) {
|
||||
if (ve <= 0.0 || ve == ve_) return;
|
||||
ve_ = ve;
|
||||
if (kind_ != Hidden) show(kind_); // 重建地形(高程×新VE),与剖面 VE 保持一致
|
||||
}
|
||||
|
||||
void TileBasemap::refineTile(int z, int x, int y, std::set<long long>& out, int& count) {
|
||||
|
|
@ -392,21 +367,6 @@ void TileBasemap::placeActor(long long key, vtkSmartPointer<vtkActor> actor) {
|
|||
placed_[key] = actor;
|
||||
}
|
||||
|
||||
void TileBasemap::ensureBaseline(int z, int x, int y, const QImage& dem) {
|
||||
if (haveBaseline_) return;
|
||||
// 基准必须锚"数据中心"的地面高程(确定性):只有含数据中心的块来定,按中心点采样。
|
||||
// 否则四叉树里随便哪块先到都定基准 → 地形整体偏移 → 三维剖面相对地面忽上忽下。
|
||||
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;
|
||||
qInfo() << "[basemap] 地形启用 baseline(数据中心)=" << baseline_ << "m z=" << z;
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkActor> TileBasemap::buildFlat(int z, int x, int y,
|
||||
vtkSmartPointer<vtkTexture> tex) {
|
||||
const geopro::render::LonLatBox b = geopro::render::tileBounds(z, x, y);
|
||||
|
|
@ -441,7 +401,6 @@ 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()) {
|
||||
ensureBaseline(dz, dx, dy, *dem); // 用含数据中心的 DEM 块定基准(确定性)
|
||||
placeActor(key, buildWarped(z, x, y, dz, dx, dy, tex, *dem));
|
||||
} else {
|
||||
placeActor(key, buildFlat(z, x, y, tex)); // DEM 拉不到 → 平面兜底
|
||||
|
|
@ -529,7 +488,7 @@ vtkSmartPointer<vtkActor> TileBasemap::buildWarped(int sz, int sx, int sy, int d
|
|||
const double elev = demElev(dem, fx, fy);
|
||||
double p[3];
|
||||
pts->GetPoint(id, p);
|
||||
p[2] = base + (elev - baseline_) * kTerrainExag;
|
||||
p[2] = base + elev * ve_; // 真实高程×垂直夸张:与剖面(同样真实高程×VE)同系对齐
|
||||
pts->SetPoint(id, p);
|
||||
}
|
||||
pts->Modified();
|
||||
|
|
|
|||
|
|
@ -38,12 +38,12 @@ public:
|
|||
void show(Kind kind); // 显示某底图(Hidden 等同 hide);记住类型供 LOD 刷新复用
|
||||
void hide(); // 移除全部瓦片
|
||||
void refresh(); // 按当前相机重算层级+覆盖,增量更新瓦片(交互结束回调)
|
||||
void setVerticalExaggeration(double ve); // 地形垂向夸张(须与剖面 VE 一致才对齐)
|
||||
|
||||
private:
|
||||
static long long tileKey(int z, int x, int y);
|
||||
void ensureObserver(); // 首次显示时挂到交互样式的 EndInteractionEvent
|
||||
void requestRender(); // 合并渲染:同一事件循环轮次多次请求只渲染一帧
|
||||
void primeBaselineThenRefresh(); // 先定数据中心基准高程,再铺四叉树(保证所有瓦片同一基准)
|
||||
void purgeStale(); // 本轮请求全部落地后再删旧层瓦片,避免缩放空白闪烁
|
||||
// 四叉树细分:按瓦片投影屏幕尺寸递归(近细远粗),收集叶瓦片到 out。
|
||||
void refineTile(int z, int x, int y, std::set<long long>& out, int& count);
|
||||
|
|
@ -51,7 +51,6 @@ 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(int z, int x, int y, const QImage& dem); // 用含数据中心的块定基准(确定性)
|
||||
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,
|
||||
|
|
@ -73,6 +72,7 @@ private:
|
|||
std::set<long long> inFlight_; // 在途瓦片(续到起伏/平面最终落地)
|
||||
std::map<long long, QImage> demCache_; // DEM 块缓存(key=DEMz/x/y),跨隐藏/重选复用
|
||||
std::map<long long, vtkSmartPointer<vtkTexture>> texCache_; // 影像纹理缓存,重选/缩放回看免重拉
|
||||
double ve_ = 2.0; // 地形垂向夸张(与剖面 verticalExaggeration 一致才对齐)
|
||||
// 四叉树当前帧相机参数(refresh 写, refineTile 读):相机位置 + 投影系数 + 视锥 6 面。
|
||||
double camX_ = 0, camY_ = 0, camZ_ = 0;
|
||||
double projK_ = 1.0;
|
||||
|
|
@ -81,8 +81,6 @@ private:
|
|||
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 打一行诊断日志
|
||||
vtkSmartPointer<vtkInteractorObserver> styleObs_; // 持引用保证回调期有效
|
||||
vtkSmartPointer<vtkCallbackCommand> observer_;
|
||||
|
|
|
|||
|
|
@ -414,6 +414,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
};
|
||||
// 相机程序化变化(取景/预设/缩放)后,底图按新视锥重算覆盖(治首帧部分瓦片需手动微动才出)。
|
||||
sceneView->onCameraChanged = [basemap]() { basemap->refresh(); };
|
||||
// 垂直夸张:地形须与剖面用同一 VE 才对齐(都按真实高程×VE)。
|
||||
QObject::connect(drawer->col3D(), &geopro::app::Column3DDataset::verticalExaggerationChanged,
|
||||
basemap, [basemap](double ve) { basemap->setVerticalExaggeration(ve); });
|
||||
|
||||
// ── 中央“空状态”引导浮层:未接入真实 sections 时,引导首次使用者从左侧入手。──
|
||||
// 透明背景 + 鼠标穿透(不挡 QVTK 交互);CenterOverlay 随视口尺寸保持居中;
|
||||
|
|
|
|||
|
|
@ -66,8 +66,9 @@ vtkSmartPointer<vtkActor> buildCurtain(const geopro::core::Grid& g,
|
|||
py = 0.0;
|
||||
}
|
||||
const vtkIdType id = static_cast<vtkIdType>(j) * nx + i;
|
||||
// g.y 是深度(越大越深);VTK Z 向上 → 取负,使深部在下、浅部在上(剖面不倒置)。
|
||||
points->SetPoint(id, px, py, -g.y[j]);
|
||||
// g.y 是真实高程(米,越大越高,与原版 web data.y 同义):直接作世界 Z,使剖面落在
|
||||
// 真实海拔上,与同样按真实高程渲染的地形对齐(剖面顶≈地表→露出地面,复刻原版)。
|
||||
points->SetPoint(id, px, py, g.y[j]);
|
||||
const double val = g.valueAt(i, j);
|
||||
if (std::isfinite(val)) {
|
||||
sc->SetValue(id, val);
|
||||
|
|
|
|||
Loading…
Reference in New Issue