feat(vtk): 天地图底图瓦片层 TileBasemap(③:复用WMTS token,经同一frame配准,col2D地图开关)
This commit is contained in:
parent
c06f9ea0f8
commit
a588b651a6
|
|
@ -65,14 +65,15 @@ add_executable(geopro_desktop WIN32
|
||||||
ExportDatasetDialog.cpp
|
ExportDatasetDialog.cpp
|
||||||
SettingsDialog.cpp
|
SettingsDialog.cpp
|
||||||
Logging.cpp
|
Logging.cpp
|
||||||
DatasetDimension.cpp)
|
DatasetDimension.cpp
|
||||||
|
TileBasemap.cpp)
|
||||||
|
|
||||||
target_include_directories(geopro_desktop PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(geopro_desktop PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
# QtKeychain 经 FetchContent 接入,头文件不随 target 传播,显式加源/构建目录(含生成的 export 头)。
|
# QtKeychain 经 FetchContent 接入,头文件不随 target 传播,显式加源/构建目录(含生成的 export 头)。
|
||||||
target_include_directories(geopro_desktop PRIVATE ${qtkeychain_SOURCE_DIR} ${qtkeychain_BINARY_DIR})
|
target_include_directories(geopro_desktop PRIVATE ${qtkeychain_SOURCE_DIR} ${qtkeychain_BINARY_DIR})
|
||||||
|
|
||||||
target_link_libraries(geopro_desktop PRIVATE
|
target_link_libraries(geopro_desktop PRIVATE
|
||||||
Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Svg
|
Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Svg Qt6::Network
|
||||||
Qt6::WebEngineWidgets Qt6::WebEngineQuick
|
Qt6::WebEngineWidgets Qt6::WebEngineQuick
|
||||||
${VTK_LIBRARIES}
|
${VTK_LIBRARIES}
|
||||||
ads::qt6advanceddocking
|
ads::qt6advanceddocking
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
#include "TileBasemap.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <vtkActor.h>
|
||||||
|
#include <vtkImageData.h>
|
||||||
|
#include <vtkNew.h>
|
||||||
|
#include <vtkPlaneSource.h>
|
||||||
|
#include <vtkPolyDataMapper.h>
|
||||||
|
#include <vtkProperty.h>
|
||||||
|
#include <vtkRenderWindow.h>
|
||||||
|
#include <vtkRenderer.h>
|
||||||
|
#include <vtkTexture.h>
|
||||||
|
|
||||||
|
#include "Scene.hpp"
|
||||||
|
#include "geo/GeoLocalFrame.hpp"
|
||||||
|
#include "ground/TileMath.hpp"
|
||||||
|
|
||||||
|
namespace geopro::app {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// 天地图 WMTS 令牌(与轨迹图 trajectory_map.html 同源)。
|
||||||
|
const char* kTk = "aca91d8c9f59a4f779f39061b8a07737";
|
||||||
|
constexpr int kZoom = 16; // 固定层级(MVP):单瓦片 ~数百米,半径覆盖典型测线
|
||||||
|
constexpr int kRadius = 3; // 中心瓦片 ±3 → 7x7=49 块,约数公里
|
||||||
|
constexpr double kGroundZ = 0.0; // 底图置于 z=0 地面参考(剖面深度向下为负,落其下)
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TileBasemap::TileBasemap(geopro::render::Scene& scene, vtkRenderWindow* rw,
|
||||||
|
std::shared_ptr<geopro::core::GeoLocalFrame> frame, QObject* parent)
|
||||||
|
: QObject(parent), scene_(scene), rw_(rw), frame_(std::move(frame)) {}
|
||||||
|
|
||||||
|
void TileBasemap::clearTiles() {
|
||||||
|
for (auto& a : tiles_) scene_.renderer()->RemoveViewProp(a);
|
||||||
|
tiles_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileBasemap::hide() {
|
||||||
|
++generation_;
|
||||||
|
clearTiles();
|
||||||
|
if (rw_) rw_->Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileBasemap::show(Kind kind) {
|
||||||
|
clearTiles();
|
||||||
|
if (kind == Hidden) {
|
||||||
|
++generation_;
|
||||||
|
if (rw_) rw_->Render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int myGen = ++generation_;
|
||||||
|
const auto c = frame_->toLatLon(0.0, 0.0); // 数据原点经纬(已重锚到真实剖面中心)
|
||||||
|
const geopro::render::TileXY center = geopro::render::lonLatToTile(c.lon, c.lat, kZoom);
|
||||||
|
const QString layerDir = (kind == Satellite) ? QStringLiteral("img_w") : QStringLiteral("vec_w");
|
||||||
|
const QString layer = (kind == Satellite) ? QStringLiteral("img") : QStringLiteral("vec");
|
||||||
|
const int n = 1 << kZoom;
|
||||||
|
for (int dy = -kRadius; dy <= kRadius; ++dy) {
|
||||||
|
for (int dx = -kRadius; dx <= kRadius; ++dx) {
|
||||||
|
const int tx = center.x + dx, ty = center.y + dy;
|
||||||
|
if (tx < 0 || ty < 0 || tx >= n || ty >= n) continue;
|
||||||
|
const int sub = (tx + ty) % 8; // 子域负载分担 t0-t7
|
||||||
|
const QString url =
|
||||||
|
QStringLiteral("http://t%1.tianditu.gov.cn/%2/wmts?service=wmts&request=GetTile"
|
||||||
|
"&version=1.0.0&LAYER=%3&tileMatrixSet=w&TileMatrix=%4&TileRow=%5"
|
||||||
|
"&TileCol=%6&style=default&format=tiles&tk=%7")
|
||||||
|
.arg(sub)
|
||||||
|
.arg(layerDir, layer)
|
||||||
|
.arg(kZoom)
|
||||||
|
.arg(ty)
|
||||||
|
.arg(tx)
|
||||||
|
.arg(QString::fromLatin1(kTk));
|
||||||
|
QNetworkReply* reply = nam_.get(QNetworkRequest(QUrl(url)));
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply, tx, ty, myGen]() {
|
||||||
|
reply->deleteLater();
|
||||||
|
if (myGen != generation_) return; // 已被新的 show/hide 取代 → 丢弃
|
||||||
|
if (reply->error() != QNetworkReply::NoError) return;
|
||||||
|
QImage img;
|
||||||
|
if (!img.loadFromData(reply->readAll())) return;
|
||||||
|
placeTile(kZoom, tx, ty, img);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileBasemap::placeTile(int z, int x, int y, const QImage& img) {
|
||||||
|
const geopro::render::LonLatBox b = geopro::render::tileBounds(z, x, y);
|
||||||
|
const auto sw = frame_->toLocal(b.south, b.west);
|
||||||
|
const auto se = frame_->toLocal(b.south, b.east);
|
||||||
|
const auto nw = frame_->toLocal(b.north, b.west);
|
||||||
|
|
||||||
|
// QImage → vtkImageData(RGBA),垂直翻转:使纹理 v=0 对应瓦片南边(与下方 PlaneSource tcoord 一致)。
|
||||||
|
const QImage rgba = img.convertToFormat(QImage::Format_RGBA8888);
|
||||||
|
const int w = rgba.width(), h = rgba.height();
|
||||||
|
if (w <= 0 || h <= 0) return;
|
||||||
|
vtkNew<vtkImageData> vimg;
|
||||||
|
vimg->SetDimensions(w, h, 1);
|
||||||
|
vimg->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
|
||||||
|
for (int row = 0; row < h; ++row) {
|
||||||
|
const uchar* src = rgba.scanLine(h - 1 - row);
|
||||||
|
auto* dst = static_cast<uchar*>(vimg->GetScalarPointer(0, row, 0));
|
||||||
|
std::memcpy(dst, src, static_cast<size_t>(w) * 4);
|
||||||
|
}
|
||||||
|
vtkNew<vtkTexture> tex;
|
||||||
|
tex->SetInputData(vimg);
|
||||||
|
tex->InterpolateOn();
|
||||||
|
|
||||||
|
// PlaneSource 自动 tcoord:origin→(0,0)、point1→(1,0)、point2→(0,1)。
|
||||||
|
// origin=SW(西南)、point1=SE(东) → u 西→东;point2=NW(北) → v 南→北。与翻转后纹理对齐。
|
||||||
|
vtkNew<vtkPlaneSource> plane;
|
||||||
|
plane->SetOrigin(sw.x, sw.y, kGroundZ);
|
||||||
|
plane->SetPoint1(se.x, se.y, kGroundZ);
|
||||||
|
plane->SetPoint2(nw.x, nw.y, kGroundZ);
|
||||||
|
|
||||||
|
vtkNew<vtkPolyDataMapper> mapper;
|
||||||
|
mapper->SetInputConnection(plane->GetOutputPort());
|
||||||
|
auto actor = vtkSmartPointer<vtkActor>::New();
|
||||||
|
actor->SetMapper(mapper);
|
||||||
|
actor->SetTexture(tex);
|
||||||
|
actor->GetProperty()->LightingOff(); // 底图不受场景光照
|
||||||
|
|
||||||
|
scene_.addActor(actor);
|
||||||
|
tiles_.push_back(actor);
|
||||||
|
if (rw_) rw_->Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace geopro::app
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <vtkSmartPointer.h>
|
||||||
|
|
||||||
|
class vtkActor;
|
||||||
|
class vtkRenderWindow;
|
||||||
|
class QImage;
|
||||||
|
namespace geopro::render { class Scene; }
|
||||||
|
namespace geopro::core { class GeoLocalFrame; }
|
||||||
|
|
||||||
|
namespace geopro::app {
|
||||||
|
|
||||||
|
// 天地图 WMTS 底图层:以共享 GeoLocalFrame 原点为中心,异步拉取覆盖瓦片贴成地面纹理面(z=0)。
|
||||||
|
// 复用轨迹图同款 token/WMTS。瓦片经同一 frame 配准 → 自动与帘面/轨迹对齐。
|
||||||
|
// 注:本期固定 zoom + 固定覆盖半径(MVP),相机驱动 LOD/数据范围自适应后续再加。
|
||||||
|
class TileBasemap : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Kind { Street = 0, Satellite = 1, Hidden = 2 };
|
||||||
|
TileBasemap(geopro::render::Scene& scene, vtkRenderWindow* rw,
|
||||||
|
std::shared_ptr<geopro::core::GeoLocalFrame> frame, QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void show(Kind kind); // 拉取并显示(Hidden 等同 hide)
|
||||||
|
void hide(); // 移除全部瓦片面
|
||||||
|
|
||||||
|
private:
|
||||||
|
void clearTiles();
|
||||||
|
void placeTile(int z, int x, int y, const QImage& img);
|
||||||
|
|
||||||
|
geopro::render::Scene& scene_;
|
||||||
|
vtkRenderWindow* rw_;
|
||||||
|
std::shared_ptr<geopro::core::GeoLocalFrame> frame_;
|
||||||
|
QNetworkAccessManager nam_;
|
||||||
|
std::vector<vtkSmartPointer<vtkActor>> tiles_;
|
||||||
|
int generation_ = 0; // 每次 show/hide 自增;迟到的瓦片回包比对丢弃
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace geopro::app
|
||||||
|
|
@ -118,6 +118,7 @@
|
||||||
#include "panels/ObjectAttrPanel.hpp"
|
#include "panels/ObjectAttrPanel.hpp"
|
||||||
#include "panels/DatasetAttrPanel.hpp"
|
#include "panels/DatasetAttrPanel.hpp"
|
||||||
#include "panels/ObjectExceptionPanel.hpp"
|
#include "panels/ObjectExceptionPanel.hpp"
|
||||||
|
#include "TileBasemap.hpp"
|
||||||
#include "panels/columns/ColumnDrawer.hpp"
|
#include "panels/columns/ColumnDrawer.hpp"
|
||||||
#include "panels/columns/Column3DDataset.hpp"
|
#include "panels/columns/Column3DDataset.hpp"
|
||||||
#include "panels/columns/Column2DDataset.hpp"
|
#include "panels/columns/Column2DDataset.hpp"
|
||||||
|
|
@ -394,6 +395,15 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
detailCtrl.openDataset(dsId, ddCode, name);
|
detailCtrl.openDataset(dsId, ddCode, name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── 二维数据集栏:天地图底图开关(③,复用轨迹图 token,经同一共享 GeoLocalFrame 配准)──
|
||||||
|
auto* basemap = new geopro::app::TileBasemap(*scene, renderWindowPtr, frame, &window);
|
||||||
|
QObject::connect(drawer->col2D(), &geopro::app::Column2DDataset::basemapChanged, basemap,
|
||||||
|
[basemap](int idx) {
|
||||||
|
// 地图下拉:0 天地图(街道) / 1 Google(暂未实现→隐藏) / 2 隐藏。
|
||||||
|
basemap->show(idx == 0 ? geopro::app::TileBasemap::Street
|
||||||
|
: geopro::app::TileBasemap::Hidden);
|
||||||
|
});
|
||||||
|
|
||||||
// ── 中央“空状态”引导浮层:未接入真实 sections 时,引导首次使用者从左侧入手。──
|
// ── 中央“空状态”引导浮层:未接入真实 sections 时,引导首次使用者从左侧入手。──
|
||||||
// 透明背景 + 鼠标穿透(不挡 QVTK 交互);CenterOverlay 随视口尺寸保持居中;
|
// 透明背景 + 鼠标穿透(不挡 QVTK 交互);CenterOverlay 随视口尺寸保持居中;
|
||||||
// 接入真实中央数据后改成依 sections 是否为空调 setVisible 即可。
|
// 接入真实中央数据后改成依 sections 是否为空调 setVisible 即可。
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue