feat(data): Repository 接口 + LocalSampleRepository(QFile 读中文路径)

- 新增 repo/RepoTypes.hpp(POD 项目结构树)、IDatasetRepository.hpp(同步接口)
- LocalSampleRepository 用 QFile + QString::fromUtf8 读真实中文路径样本(UTF-8),
  合成单 GS/TM/DS 树(DsNode.id=grid1),映射 grid/colorscale/scatter/anomaly 文件
- geopro_data 链 Qt6::Core(AUTOMOC OFF、无 Q_OBJECT);core 仍无 Qt 依赖
- 测试 geopro_tests 因链 Qt 需运行时 DLL 在旁:POST_BUILD 拷贝 TARGET_RUNTIME_DLLS
  + gtest DISCOVERY_MODE PRE_TEST(推迟枚举到运行期)
- 新增 tests/data/test_local_repo.cpp:实测中文路径读取 + scatter 2597/anomaly 3 全链路
This commit is contained in:
gaozheng 2026-06-07 20:35:20 +08:00
parent fe5936a3a6
commit f48b9ebb8f
7 changed files with 198 additions and 3 deletions

View File

@ -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)

View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <vector>
#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<GsNode> 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<geopro::core::Anomaly> loadAnomalies(const std::string& dsId) = 0;
};
} // namespace geopro::data

View File

@ -0,0 +1,92 @@
#include "repo/LocalSampleRepository.hpp"
#include <stdexcept>
#include <utility>
#include <QByteArray>
#include <QFile>
#include <QString>
#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<size_t>(ba.size()));
}
std::vector<GsNode> 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<GsNode> 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<Anomaly> LocalSampleRepository::loadAnomalies(const std::string& dsId) {
requireKnownDs(dsId);
return parseAnomalies(readFile(kAnomalyFile));
}
} // namespace geopro::data

View File

@ -0,0 +1,28 @@
#pragma once
#include <string>
#include <vector>
#include "repo/IDatasetRepository.hpp"
namespace geopro::data {
// 本地样本仓储:用 QFile 读真实中文路径样本文件UTF-8调对应 parser。
// 合成最小项目结构树:单 GS「测区」→ 单 TM「ERT1」→ 单 DS「grid1」。
// 注意:实现使用 QtQFile 正确处理中文路径),但本接口不含 Q_OBJECTAUTOMOC OFF。
class LocalSampleRepository : public IDatasetRepository {
public:
explicit LocalSampleRepository(std::string sampleDirUtf8);
std::vector<GsNode> 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<geopro::core::Anomaly> 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

View File

@ -0,0 +1,9 @@
#pragma once
#include <string>
#include <vector>
namespace geopro::data {
struct DsNode { std::string id, name, ddType; };
struct TmNode { std::string id, name, confCode; std::vector<DsNode> dss; };
struct GsNode { std::string id, name; std::vector<TmNode> tms; };
struct Project { std::string id, name; std::vector<GsNode> gss; };
} // namespace geopro::data

View File

@ -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
$<TARGET_RUNTIME_DLLS:geopro_tests> $<TARGET_FILE_DIR:geopro_tests>
COMMAND_EXPAND_LISTS)
endif()
add_subdirectory(spike) # spike S3: banded contour

View File

@ -0,0 +1,28 @@
#include <gtest/gtest.h>
#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());
}