diff --git a/src/data/CMakeLists.txt b/src/data/CMakeLists.txt index 4d6798a..71d062a 100644 --- a/src/data/CMakeLists.txt +++ b/src/data/CMakeLists.txt @@ -1,6 +1,9 @@ find_package(nlohmann_json CONFIG REQUIRED) -add_library(geopro_data STATIC parse/SampleParsers.cpp) +find_package(Qt6 COMPONENTS Core REQUIRED) +add_library(geopro_data STATIC + parse/SampleParsers.cpp + repo/LocalSampleRepository.cpp) target_include_directories(geopro_data PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(geopro_data PUBLIC geopro_core PRIVATE nlohmann_json::nlohmann_json) +target_link_libraries(geopro_data PUBLIC geopro_core Qt6::Core PRIVATE nlohmann_json::nlohmann_json) target_compile_features(geopro_data PUBLIC cxx_std_17) set_target_properties(geopro_data PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF) diff --git a/src/data/repo/IDatasetRepository.hpp b/src/data/repo/IDatasetRepository.hpp new file mode 100644 index 0000000..96e3455 --- /dev/null +++ b/src/data/repo/IDatasetRepository.hpp @@ -0,0 +1,21 @@ +#pragma once +#include +#include +#include "repo/RepoTypes.hpp" +#include "model/Field.hpp" +#include "model/ColorScale.hpp" +#include "model/Anomaly.hpp" +namespace geopro::data { + +// 数据集仓储抽象(同步接口;异步加载留 M1.5)。 +class IDatasetRepository { +public: + virtual ~IDatasetRepository() = default; + virtual std::vector loadStructure() = 0; + virtual geopro::core::Grid loadGrid(const std::string& dsId) = 0; + virtual geopro::core::ScatterField loadScatter(const std::string& dsId) = 0; + virtual geopro::core::ColorScale loadColorScale(const std::string& dsId) = 0; + virtual std::vector loadAnomalies(const std::string& dsId) = 0; +}; + +} // namespace geopro::data diff --git a/src/data/repo/LocalSampleRepository.cpp b/src/data/repo/LocalSampleRepository.cpp new file mode 100644 index 0000000..6dde8ca --- /dev/null +++ b/src/data/repo/LocalSampleRepository.cpp @@ -0,0 +1,92 @@ +#include "repo/LocalSampleRepository.hpp" + +#include +#include + +#include +#include +#include + +#include "parse/SampleParsers.hpp" + +namespace geopro::data { + +using namespace geopro::core; + +namespace { + +// grid1 数据集对应的样本文件名(UTF-8 中文路径)。 +constexpr const char* kDsId = "grid1"; +constexpr const char* kGridFile = u8"剖面网格数据1.txt"; +constexpr const char* kColorScaleFile = u8"剖面网格数据的色阶数据1.txt"; +constexpr const char* kScatterFile = u8"剖面原数据1.txt"; +constexpr const char* kAnomalyFile = u8"剖面网格数据1——对应的异常圈定数据.txt"; + +// 校验 dsId,未知则抛错(输入边界验证)。 +void requireKnownDs(const std::string& dsId) { + if (dsId != kDsId) { + throw std::runtime_error("LocalSampleRepository: unknown dataset id '" + dsId + "'"); + } +} + +} // namespace + +LocalSampleRepository::LocalSampleRepository(std::string sampleDirUtf8) + : dirUtf8_(std::move(sampleDirUtf8)) {} + +std::string LocalSampleRepository::readFile(const std::string& fileNameUtf8) const { + // QString::fromUtf8 保证中文路径正确还原为宽字符,QFile 在 Windows 走宽字符 API。 + const QString path = + QString::fromUtf8(dirUtf8_.c_str()) + QString::fromUtf8(fileNameUtf8.c_str()); + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + throw std::runtime_error("LocalSampleRepository: cannot open file '" + + path.toStdString() + "'"); + } + const QByteArray ba = file.readAll(); + return std::string(ba.constData(), static_cast(ba.size())); +} + +std::vector LocalSampleRepository::loadStructure() { + DsNode ds; + ds.id = kDsId; + ds.name = u8"剖面网格数据1"; + ds.ddType = "grid"; + + TmNode tm; + tm.id = "tm-ert1"; + tm.name = "ERT1"; + tm.confCode = "ERT"; + tm.dss.push_back(std::move(ds)); + + GsNode gs; + gs.id = "gs-1"; + gs.name = u8"测区"; + gs.tms.push_back(std::move(tm)); + + std::vector out; + out.push_back(std::move(gs)); + return out; +} + +Grid LocalSampleRepository::loadGrid(const std::string& dsId) { + requireKnownDs(dsId); + return parseGrid(readFile(kGridFile)); +} + +ScatterField LocalSampleRepository::loadScatter(const std::string& dsId) { + requireKnownDs(dsId); + return parseScatter(readFile(kScatterFile)); +} + +ColorScale LocalSampleRepository::loadColorScale(const std::string& dsId) { + requireKnownDs(dsId); + return parseColorScale(readFile(kColorScaleFile)); +} + +std::vector LocalSampleRepository::loadAnomalies(const std::string& dsId) { + requireKnownDs(dsId); + return parseAnomalies(readFile(kAnomalyFile)); +} + +} // namespace geopro::data diff --git a/src/data/repo/LocalSampleRepository.hpp b/src/data/repo/LocalSampleRepository.hpp new file mode 100644 index 0000000..2cc7b08 --- /dev/null +++ b/src/data/repo/LocalSampleRepository.hpp @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include "repo/IDatasetRepository.hpp" +namespace geopro::data { + +// 本地样本仓储:用 QFile 读真实中文路径样本文件(UTF-8),调对应 parser。 +// 合成最小项目结构树:单 GS「测区」→ 单 TM「ERT1」→ 单 DS「grid1」。 +// 注意:实现使用 Qt(QFile 正确处理中文路径),但本接口不含 Q_OBJECT,AUTOMOC OFF。 +class LocalSampleRepository : public IDatasetRepository { +public: + explicit LocalSampleRepository(std::string sampleDirUtf8); + + std::vector loadStructure() override; + geopro::core::Grid loadGrid(const std::string& dsId) override; + geopro::core::ScatterField loadScatter(const std::string& dsId) override; + geopro::core::ColorScale loadColorScale(const std::string& dsId) override; + std::vector loadAnomalies(const std::string& dsId) override; + +private: + // 用 QFile(路径 = fromUtf8(dir)+fromUtf8(name))读全文件并转 UTF-8 std::string。 + // 读失败抛 std::runtime_error。 + std::string readFile(const std::string& fileNameUtf8) const; + + std::string dirUtf8_; +}; + +} // namespace geopro::data diff --git a/src/data/repo/RepoTypes.hpp b/src/data/repo/RepoTypes.hpp new file mode 100644 index 0000000..b671c54 --- /dev/null +++ b/src/data/repo/RepoTypes.hpp @@ -0,0 +1,9 @@ +#pragma once +#include +#include +namespace geopro::data { +struct DsNode { std::string id, name, ddType; }; +struct TmNode { std::string id, name, confCode; std::vector dss; }; +struct GsNode { std::string id, name; std::vector tms; }; +struct Project { std::string id, name; std::vector gss; }; +} // namespace geopro::data diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index db204e4..30c58b7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,12 +13,16 @@ include(GoogleTest) file(GLOB _proj_data_dirs "${CMAKE_BINARY_DIR}/vcpkg_installed/*/share/proj" ) +# DISCOVERY_MODE PRE_TEST:把用例枚举推迟到 ctest 运行时执行,而非构建后立即跑 +# exe。因 geopro_data 链 Qt6::Core,构建期发现会因 Qt6Core.dll 尚未拷贝到 exe 旁而 +# 失败(0xc0000135);推迟到运行期时 POST_BUILD 已把运行时 DLL 拷到位。 if(_proj_data_dirs) list(GET _proj_data_dirs 0 GEOPRO_PROJ_DATA) gtest_discover_tests(geopro_tests + DISCOVERY_MODE PRE_TEST PROPERTIES ENVIRONMENT "PROJ_DATA=${GEOPRO_PROJ_DATA}") else() - gtest_discover_tests(geopro_tests) + gtest_discover_tests(geopro_tests DISCOVERY_MODE PRE_TEST) endif() target_sources(geopro_tests PRIVATE core/test_local_frame.cpp) @@ -30,6 +34,16 @@ target_sources(geopro_tests PRIVATE core/test_model_data.cpp) target_link_libraries(geopro_tests PRIVATE geopro_core) target_sources(geopro_tests PRIVATE data/test_parsers.cpp) +target_sources(geopro_tests PRIVATE data/test_local_repo.cpp) target_link_libraries(geopro_tests PRIVATE geopro_data) +# geopro_data 链 Qt6::Core,测试 exe 运行(含 gtest 发现)需要 Qt6Core.dll 等运行时 +# DLL 在旁。复用 app 同样的 TARGET_RUNTIME_DLLS POST_BUILD 拷贝。 +if(WIN32) + add_custom_command(TARGET geopro_tests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ $ + COMMAND_EXPAND_LISTS) +endif() + add_subdirectory(spike) # spike S3: banded contour 渲染验证 diff --git a/tests/data/test_local_repo.cpp b/tests/data/test_local_repo.cpp new file mode 100644 index 0000000..502835f --- /dev/null +++ b/tests/data/test_local_repo.cpp @@ -0,0 +1,28 @@ +#include +#include "repo/LocalSampleRepository.hpp" +using namespace geopro::data; +using namespace geopro::core; + +static const std::string kDir = "D:/Git/lanbingtech/geopro/docs/剖面网格数据的色阶数据2等文件/"; + +TEST(LocalRepo, StructureNonEmpty) { + LocalSampleRepository repo(kDir); + auto gs = repo.loadStructure(); + ASSERT_FALSE(gs.empty()); + ASSERT_FALSE(gs[0].tms.empty()); + ASSERT_FALSE(gs[0].tms[0].dss.empty()); + EXPECT_EQ(gs[0].tms[0].dss[0].id, "grid1"); +} + +TEST(LocalRepo, LoadGridScatterAnomaly) { + LocalSampleRepository repo(kDir); + Grid g = repo.loadGrid("grid1"); + EXPECT_EQ(g.nx(), 100); EXPECT_EQ(g.ny(), 22); + ScatterField s = repo.loadScatter("grid1"); + EXPECT_EQ(s.v.size(), 2597u); + auto an = repo.loadAnomalies("grid1"); + EXPECT_EQ(an.size(), 3u); + EXPECT_EQ(an[0].markType, AnomalyMarkType::Polyline); + ColorScale cs = repo.loadColorScale("grid1"); + EXPECT_FALSE(cs.empty()); +}