From 07cf75d967e9fed2a475d3035516825b2ae4b7b1 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Wed, 24 Jun 2026 17:48:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(app):=20CategoryConfig=20=E6=98=A0?= =?UTF-8?q?=E5=B0=84=E8=A1=A8=20+=20splitByCategory=20=E6=8C=89=20dsTypeCo?= =?UTF-8?q?de=20=E5=88=86=E5=A4=A7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/CMakeLists.txt | 1 + src/app/DatasetCategory.cpp | 22 ++++++++++++++ src/app/DatasetCategory.hpp | 16 ++++++++++ src/data/repo/CategoryConfig.hpp | 29 ++++++++++++++++++ tests/CMakeLists.txt | 5 +++ tests/app/test_dataset_category.cpp | 47 +++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+) create mode 100644 src/app/DatasetCategory.cpp create mode 100644 src/app/DatasetCategory.hpp create mode 100644 src/data/repo/CategoryConfig.hpp create mode 100644 tests/app/test_dataset_category.cpp diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 32fa716..03304cb 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -107,6 +107,7 @@ add_executable(geopro_desktop WIN32 VolumePropertiesDialog.cpp Logging.cpp DatasetDimension.cpp + DatasetCategory.cpp TileBasemap.cpp) target_include_directories(geopro_desktop PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/app/DatasetCategory.cpp b/src/app/DatasetCategory.cpp new file mode 100644 index 0000000..bca33cc --- /dev/null +++ b/src/app/DatasetCategory.cpp @@ -0,0 +1,22 @@ +#include "DatasetCategory.hpp" + +namespace geopro::app { + +CategoryBuckets splitByCategory(const std::vector& rows) { + const auto& cfg = categoryConfigs(); + CategoryBuckets b; + b.segments.resize(cfg.size()); + for (const auto& r : rows) { + int hit = -1; + // 先按 ddCode(三维体/切片)——它们无 dsTypeCode(来自 Api3dRepository mock 行)。 + for (std::size_t i = 0; i < cfg.size() && hit < 0; ++i) + if (!cfg[i].ddCode.empty() && r.ddCode == cfg[i].ddCode) hit = static_cast(i); + // 再按 dsTypeCode。 + for (std::size_t i = 0; i < cfg.size() && hit < 0; ++i) + if (!cfg[i].dsTypeCode.empty() && r.dsTypeCode == cfg[i].dsTypeCode) hit = static_cast(i); + if (hit >= 0) b.segments[static_cast(hit)].push_back(r); + } + return b; +} + +} // namespace geopro::app diff --git a/src/app/DatasetCategory.hpp b/src/app/DatasetCategory.hpp new file mode 100644 index 0000000..519db3f --- /dev/null +++ b/src/app/DatasetCategory.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include "repo/CategoryConfig.hpp" +#include "repo/RepoTypes.hpp" + +namespace geopro::app { + +struct CategoryBuckets { + std::vector> segments; // 与 categoryConfigs() 同序同长 +}; + +// 按 CategoryConfig 把 ds 分入大类段:先判 ddCode 白名单(三维体/切片),否则按 dsTypeCode 匹配; +// 不在表内的丢弃(接地电阻/原始数据/白化/坐标等)。保留原顺序。 +CategoryBuckets splitByCategory(const std::vector& rows); + +} // namespace geopro::app diff --git a/src/data/repo/CategoryConfig.hpp b/src/data/repo/CategoryConfig.hpp new file mode 100644 index 0000000..dea58df --- /dev/null +++ b/src/data/repo/CategoryConfig.hpp @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +namespace geopro::app { + +// 一个数据类型大类段的配置(spec §5)。识别键二选一:dsTypeCode 优先;ddCode 用于三维体/切片。 +struct CategorySpec { + std::string id; // 段稳定 id + std::string title; // 段标题(UI 显示) + std::string dsTypeCode; // 主识别键(空=不按 dsTypeCode) + std::string ddCode; // 次识别键(dd_voxel/dd_slice;空=不按 ddCode) + bool canGenerateVolume; // 段内是否提供「生成三维体」入口(仅反演类) + bool hasArrayTypeFilter; // 段头是否显示装置类型筛选(仅 ERT 类) +}; + +// 5 段固定有序(spec §5 表)。 +inline const std::vector& categoryConfigs() { + static const std::vector kCfg = { + {"resistivity", "电阻率数据", "ERT platform inversion data", "", true, true}, + {"apparent", "视电阻率数据", "visual resistivity data", "", true, true}, + {"transient", "瞬变电磁数据", "DD TRANSIENT ELECTROMAGNETIC INVERSION", "", true, false}, + {"voxel", "三维体", "", "dd_voxel", false, false}, + {"slice", "切片", "", "dd_slice", false, false}, + }; + return kCfg; +} + +} // namespace geopro::app diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 66a2a60..95bddc6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -168,6 +168,11 @@ target_sources(geopro_tests PRIVATE app/test_dataset_dimension.cpp ${CMAKE_SOURCE_DIR}/src/app/DatasetDimension.cpp ) +# 大类分类纯函数(splitByCategory: dsTypeCode/ddCode -> 5 个数据类型大类段,无 Qt/VTK 依赖)。 +target_sources(geopro_tests PRIVATE + app/test_dataset_category.cpp + ${CMAKE_SOURCE_DIR}/src/app/DatasetCategory.cpp +) # measurement 散点纯逻辑(值类型变换 / 显隐 id 收集 / 过滤体 / 另存体,Qt6::Core JSON + core model)。 target_sources(geopro_tests PRIVATE app/test_scatter_data_ops.cpp diff --git a/tests/app/test_dataset_category.cpp b/tests/app/test_dataset_category.cpp new file mode 100644 index 0000000..8e4e84f --- /dev/null +++ b/tests/app/test_dataset_category.cpp @@ -0,0 +1,47 @@ +#include +#include "DatasetCategory.hpp" +using geopro::data::DsRow; +using namespace geopro::app; + +namespace { +DsRow row(const std::string& id, const std::string& ddCode, const std::string& dsTypeCode) { + DsRow r; + r.id = id; + r.ddCode = ddCode; + r.dsTypeCode = dsTypeCode; + return r; +} +} // namespace + +TEST(SplitByCategory, RoutesByDsTypeCodeAndDdCode) { + std::vector rows = { + row("a", "dd_inversion_data", "ERT platform inversion data"), // 电阻率 + row("b", "dd_inversion_data", "visual resistivity data"), // 视电阻率 + row("c", "dd_inversion_data", "DD TRANSIENT ELECTROMAGNETIC INVERSION"), // 瞬变 + row("v", "dd_voxel", ""), // 三维体(按 ddCode) + row("s", "dd_slice", ""), // 切片(按 ddCode) + row("x", "dd_ert_measurement_gr_data", "earth resistance"), // 接地电阻 → 丢弃 + }; + const CategoryBuckets b = splitByCategory(rows); + ASSERT_EQ(b.segments.size(), categoryConfigs().size()); + EXPECT_EQ(b.segments[0].size(), 1u); EXPECT_EQ(b.segments[0][0].id, "a"); + EXPECT_EQ(b.segments[1].size(), 1u); EXPECT_EQ(b.segments[1][0].id, "b"); + EXPECT_EQ(b.segments[2].size(), 1u); EXPECT_EQ(b.segments[2][0].id, "c"); + EXPECT_EQ(b.segments[3].size(), 1u); EXPECT_EQ(b.segments[3][0].id, "v"); + EXPECT_EQ(b.segments[4].size(), 1u); EXPECT_EQ(b.segments[4][0].id, "s"); + // 接地电阻不进任何段。 + std::size_t total = 0; + for (auto& s : b.segments) total += s.size(); + EXPECT_EQ(total, 5u); +} + +TEST(SplitByCategory, PreservesOrderWithinSegment) { + std::vector rows = { + row("a1", "dd_inversion_data", "ERT platform inversion data"), + row("a2", "dd_inversion_data", "ERT platform inversion data"), + }; + const CategoryBuckets b = splitByCategory(rows); + ASSERT_EQ(b.segments[0].size(), 2u); + EXPECT_EQ(b.segments[0][0].id, "a1"); + EXPECT_EQ(b.segments[0][1].id, "a2"); +}