221 lines
9.6 KiB
C++
221 lines
9.6 KiB
C++
#include "repo/LocalSample3dRepository.hpp"
|
||
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
#include <cstddef>
|
||
#include <exception>
|
||
#include <limits>
|
||
#include <utility>
|
||
#include <vector>
|
||
|
||
#include "algo/VolumeBuilder.hpp"
|
||
#include "geo/CrsTransform.hpp"
|
||
#include "geo/GeoLocalFrame.hpp"
|
||
#include "model/ColorScale.hpp"
|
||
#include "model/Field.hpp"
|
||
#include "repo/LocalSampleRepository.hpp"
|
||
|
||
namespace geopro::data {
|
||
|
||
using geopro::core::ColorScale;
|
||
using geopro::core::CrsTransform;
|
||
using geopro::core::GeoLocalFrame;
|
||
using geopro::core::PointSet;
|
||
using geopro::core::ScatterField;
|
||
|
||
namespace {
|
||
|
||
// 与 render::VoxelFromScatters 的默认参数同口径(保持渲染/切片纵向一致)。
|
||
// 「散点→GridSpec→IDW→ScalarVolume」已提到 core::buildVolume 共享(与 Api3dRepository 同源,
|
||
// 消除调参漂移);此处仅保留默认参数 + 本地样本配准。
|
||
constexpr double kCellXY = 1.0;
|
||
constexpr double kCellZ = 0.5;
|
||
constexpr double kPower = 2.0;
|
||
constexpr double kMaxDist = 4.0;
|
||
constexpr const char* kWgs84 = "EPSG:4326";
|
||
|
||
} // namespace
|
||
|
||
LocalSample3dRepository::LocalSample3dRepository(LocalSampleRepository& base, std::string projectCrs,
|
||
double baseLat, double baseLon)
|
||
: base_(base), projectCrs_(std::move(projectCrs)), baseLat_(baseLat), baseLon_(baseLon) {}
|
||
|
||
DsDimension LocalSample3dRepository::dimensionOf(const DsRow& ds) const {
|
||
const std::string& c = ds.ddCode;
|
||
// 真三维体 / 体素 / 帘面(dd_section/反演剖面摆成竖直帘面)入三维数据集。
|
||
if (c == "dd_voxel" || c == "dd_Structual3D" || c == "dd_Property3D" || c == "dd_section" ||
|
||
c == "dd_inversion_data") {
|
||
return DsDimension::Dim3D;
|
||
}
|
||
// 切片:三维分析栏。
|
||
if (c == "dd_slice") return DsDimension::Analysis3D;
|
||
// 足迹型 → 二维数据集。dd_trajectory_data = 统一通用轨迹(数据字典 DD0623「保留」,已并入
|
||
// dd_radar_rtk_trajectory);瞬变电磁/雷达通道/RTK 等轨迹型字典均标「删除」,不再单列。
|
||
if (c == "dd_trajectory_data") return DsDimension::Dim2D;
|
||
return DsDimension::Other;
|
||
}
|
||
|
||
void LocalSample3dRepository::loadVolume(
|
||
const std::string& /*dsId*/,
|
||
std::function<void(VolumeGrid, geopro::core::ColorScale)> onOk, OnError onErr) {
|
||
// P1 样本:dsId 暂未使用,固定读同一组交叉剖面散点→体素(真实 Api 实现按 dsId 取)。
|
||
try {
|
||
// 1) 读两条交叉剖面散点 + 色阶;配准到世界局部米 + 深度,组装 IDW 输入点集。
|
||
const std::vector<ScatterField> profiles = base_.loadVoxelScatters();
|
||
const CrsTransform crs(projectCrs_, kWgs84);
|
||
const GeoLocalFrame frame(baseLat_, baseLon_);
|
||
|
||
PointSet pts;
|
||
for (const auto& s : profiles) {
|
||
const std::size_t n = s.v.size();
|
||
if (s.projX.size() < n || s.projY.size() < n || s.y.size() < n) continue;
|
||
for (std::size_t i = 0; i < n; ++i) {
|
||
const auto ll = crs.forward(s.projX[i], s.projY[i]); // (lon, lat)
|
||
const auto local = frame.toLocal(ll.y, ll.x); // (x East, y North) 米
|
||
pts.x.push_back(local.x);
|
||
pts.y.push_back(local.y);
|
||
pts.z.push_back(-s.y[i]); // 深度向下:z 取负
|
||
pts.v.push_back(s.v[i]);
|
||
}
|
||
}
|
||
if (pts.v.empty()) {
|
||
onErr("LocalSample3dRepository: no voxel points after registration");
|
||
return;
|
||
}
|
||
|
||
// 2) 点集 → GridSpec → IDW → 数据实测值域(共享 core::buildVolume)。
|
||
geopro::core::BuiltVolume bv =
|
||
geopro::core::buildVolume(pts, kCellXY, kCellZ, kPower, kMaxDist);
|
||
|
||
// 3) 值域:优先 colorBar 真实分段值,否则 buildVolume 的数据实测范围。
|
||
double vmin = bv.vmin, vmax = bv.vmax;
|
||
ColorScale cs;
|
||
try {
|
||
cs = base_.loadScatterColorScale("grid1");
|
||
} catch (const std::exception&) {
|
||
// 色阶缺失 → 沿用数据实测范围。
|
||
}
|
||
const std::vector<double> stops = cs.stopValues();
|
||
if (stops.size() >= 2) {
|
||
vmin = stops.front(); vmax = stops.back();
|
||
}
|
||
|
||
VolumeGrid out{std::move(bv.vol),
|
||
{{bv.spec.ox, bv.spec.oy, bv.spec.oz}},
|
||
{{bv.spec.dx, bv.spec.dy, bv.spec.dz}},
|
||
vmin, vmax};
|
||
onOk(std::move(out), std::move(cs));
|
||
} catch (const std::exception& e) {
|
||
onErr(std::string("LocalSample3dRepository::loadVolume: ") + e.what());
|
||
}
|
||
}
|
||
|
||
void LocalSample3dRepository::loadSection(const std::string& /*dsId*/,
|
||
std::function<void(SectionData)> onOk, OnError onErr) {
|
||
// P1 样本:忽略入参 dsId(本地仅一份样本 grid1);真实 Api 实现走 ERT 反演端点按 dsId 取。
|
||
try {
|
||
SectionData s;
|
||
s.grid = base_.loadGrid("grid1"); // 样本 dd_section 网格
|
||
s.scale = base_.loadColorScale("grid1"); // 对应色阶
|
||
onOk(std::move(s)); // 本地同步回调
|
||
} catch (const std::exception& e) {
|
||
onErr(std::string("LocalSample3dRepository::loadSection: ") + e.what());
|
||
}
|
||
}
|
||
|
||
void LocalSample3dRepository::loadMapLine(const std::string& /*dsId*/,
|
||
std::function<void(MapLine)> onOk, OnError onErr) {
|
||
// P1 样本:取样本 grid1 的 lat/lon 作为足迹折线(测试/离线用,不依赖网络)。
|
||
// 真实 Api 实现走 dd/ert/trajectory/line 端点按 dsId 取经纬。
|
||
try {
|
||
const core::Grid g = base_.loadGrid("grid1");
|
||
MapLine line;
|
||
line.lat = g.lat; // 样本 grid 的测线经纬(与帘面同源 → 同系配准)
|
||
line.lon = g.lon;
|
||
if (!line.valid()) {
|
||
onErr("LocalSample3dRepository::loadMapLine: 样本无有效经纬折线");
|
||
return;
|
||
}
|
||
onOk(std::move(line)); // 本地同步回调
|
||
} catch (const std::exception& e) {
|
||
onErr(std::string("LocalSample3dRepository::loadMapLine: ") + e.what());
|
||
}
|
||
}
|
||
|
||
void LocalSample3dRepository::loadTerrainPaths(std::function<void(TerrainPaths)> onOk,
|
||
OnError onErr) {
|
||
try {
|
||
TerrainPaths p{base_.demPath(), base_.imagePath()};
|
||
onOk(std::move(p));
|
||
} catch (const std::exception& e) {
|
||
onErr(std::string("LocalSample3dRepository::loadTerrainPaths: ") + e.what());
|
||
}
|
||
}
|
||
|
||
// ── 切片 CRUD(spec §6.3 内存态 stub)───────────────────────────────────────
|
||
|
||
void LocalSample3dRepository::createSlice(const SliceSpec& spec, const std::string& /*name*/,
|
||
std::function<void(std::string)> onOk,
|
||
OnError /*onErr*/) {
|
||
std::string newId = "slice-" + std::to_string(++sliceCounter_);
|
||
slices_[newId] = spec;
|
||
onOk(std::move(newId));
|
||
}
|
||
|
||
void LocalSample3dRepository::saveSlice(const std::string& dsId, const SliceSpec& spec,
|
||
std::function<void()> onOk, OnError /*onErr*/) {
|
||
slices_[dsId] = spec;
|
||
onOk();
|
||
}
|
||
|
||
void LocalSample3dRepository::deleteSlice(const std::string& dsId,
|
||
std::function<void()> onOk, OnError /*onErr*/) {
|
||
slices_.erase(dsId);
|
||
onOk();
|
||
}
|
||
|
||
// ── 异常 / 异常体(spec §6.4 内存态 stub)──────────────────────────────────
|
||
|
||
void LocalSample3dRepository::loadAnomalyTree(const std::string& /*objectId*/,
|
||
std::function<void(AnomalyTree)> onOk,
|
||
OnError /*onErr*/) {
|
||
onOk(AnomalyTree{}); // stub: 空树(无样本异常)
|
||
}
|
||
|
||
void LocalSample3dRepository::saveAnomaly(const geopro::core::Anomaly& a,
|
||
const std::string& /*screenshotPngPath*/,
|
||
std::function<void(std::string)> onOk,
|
||
OnError /*onErr*/) {
|
||
std::string newId = "anomaly-" + std::to_string(++anomalyCounter_);
|
||
anomalies_[newId] = a;
|
||
onOk(std::move(newId));
|
||
}
|
||
|
||
void LocalSample3dRepository::deleteAnomaly(const std::string& anomalyId,
|
||
std::function<void()> onOk, OnError /*onErr*/) {
|
||
anomalies_.erase(anomalyId);
|
||
onOk();
|
||
}
|
||
|
||
void LocalSample3dRepository::deleteAnomalyGroup(const std::string& /*bodyId*/,
|
||
std::function<void()> onOk, OnError /*onErr*/) {
|
||
// stub: 内存态无 AnomalyBody 存储(bodyId 仅逻辑分组,真实 backend 实现时才有)
|
||
onOk();
|
||
}
|
||
|
||
// ── 任务管理(spec §6.5 内存态 stub)────────────────────────────────────────
|
||
|
||
void LocalSample3dRepository::loadTaskRecords(const std::string& /*dsId*/,
|
||
std::function<void(std::vector<TaskRecord>)> onOk,
|
||
OnError /*onErr*/) {
|
||
onOk({}); // stub: 无样本任务记录
|
||
}
|
||
|
||
void LocalSample3dRepository::loadUsableTasks(const std::string& /*ddCode*/,
|
||
std::function<void(std::vector<UsableTask>)> onOk,
|
||
OnError /*onErr*/) {
|
||
onOk({}); // stub: 空列表(真实实现按 ddCode 过滤 model/list 返回)
|
||
}
|
||
|
||
} // namespace geopro::data
|