diff --git a/src/data/CMakeLists.txt b/src/data/CMakeLists.txt index 803e557..4e0aa60 100644 --- a/src/data/CMakeLists.txt +++ b/src/data/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(geopro_data STATIC parse/SampleParsers.cpp repo/LocalSampleRepository.cpp dto/NavDto.cpp + dto/DatasetChartDto.cpp api/ApiProjectRepository.cpp) target_include_directories(geopro_data PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(geopro_data PUBLIC geopro_core geopro_net Qt6::Core PRIVATE nlohmann_json::nlohmann_json) diff --git a/src/data/dto/DatasetChartDto.cpp b/src/data/dto/DatasetChartDto.cpp new file mode 100644 index 0000000..6d20cdf --- /dev/null +++ b/src/data/dto/DatasetChartDto.cpp @@ -0,0 +1,74 @@ +#include "dto/DatasetChartDto.hpp" +#include +#include +namespace geopro::data::dto { +using namespace geopro::core; + +static double num(const QJsonValue& v, double def = 0.0) { + if (v.isDouble()) return v.toDouble(); + if (v.isString()) { bool ok = false; double d = v.toString().toDouble(&ok); return ok ? d : def; } + return def; +} + +Grid parseInversionGrid(const QJsonObject& data) { + const QJsonArray x = data.value("x").toArray(); + const QJsonArray y = data.value("y").toArray(); + const QJsonArray v = data.value("v").toArray(); // [ny][nx] + const int nx = x.size(), ny = y.size(); + Grid g(nx < 1 ? 1 : nx, ny < 1 ? 1 : ny); + g.x.clear(); for (auto e : x) g.x.push_back(num(e)); + g.y.clear(); for (auto e : y) g.y.push_back(num(e)); + for (int j = 0; j < ny; ++j) { + const QJsonArray row = v.at(j).toArray(); + for (int i = 0; i < nx; ++i) { + const QJsonValue cell = row.at(i); + g.valueAt(i, j) = (cell.isNull() || cell.isUndefined()) ? std::nan("") : num(cell, std::nan("")); + } + } + g.vmin = num(data.value("vmin")); g.vmax = num(data.value("vmax")); + return g; +} + +ScatterField parseScatterGraph(const QJsonObject& data) { + ScatterField s; + auto fill = [&](const char* key, std::vector& dst) { + for (auto e : data.value(key).toArray()) dst.push_back(num(e)); }; + fill("xlist", s.x); fill("ylist", s.y); fill("hlist", s.z); fill("vlist", s.v); + fill("projectXList", s.projX); fill("projectYList", s.projY); + return s; +} + +ColorScale parseColorBar(const QJsonObject& data) { + ColorScale cs; + const QJsonArray bar = data.value("properties").toObject().value("colorBar").toArray(); + for (auto e : bar) { + const QJsonArray pair = e.toArray(); + if (pair.size() < 2) continue; + const double val = num(pair.at(0)); + const std::string rgba = pair.at(1).toString().toStdString(); + cs.addStop(val, parseColor(rgba, AlphaScale::Bit255)); + } + return cs; +} + +std::vector parseDatasetAnomalies(const QJsonArray& arr) { + std::vector out; + for (auto e : arr) { + const QJsonObject o = e.toObject(); + Anomaly a; + a.name = o.value("exceptionName").toString().toStdString(); + a.typeName = o.value("exceptionTypeName").toString().toStdString(); + a.markType = static_cast(o.value("exceptionMarkType").toInt(2)); + const QJsonObject lg = o.value("legend").toObject(); + a.lineColor = lg.value("polylineColor").toString("#000000").toStdString(); + a.lineWidth = lg.value("polylineWidth").toDouble(1.0); + a.dashed = lg.value("polylineShape").toString() == "dash"; + for (auto c : o.value("location").toObject().value("coordinate").toArray()) { + const QJsonObject p = c.toObject(); + a.localPts.push_back(Vec2{p.value("x").toDouble(), p.value("y").toDouble()}); + } + out.push_back(std::move(a)); + } + return out; +} +} // namespace geopro::data::dto diff --git a/src/data/dto/DatasetChartDto.hpp b/src/data/dto/DatasetChartDto.hpp new file mode 100644 index 0000000..c06dca5 --- /dev/null +++ b/src/data/dto/DatasetChartDto.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include +#include "model/Field.hpp" +#include "model/ColorScale.hpp" +#include "model/Anomaly.hpp" +namespace geopro::data::dto { + +// inversion/rows 的 data{ x,y,v[ny][nx],z,elevation,vmin,vmax } → Grid(v 缺省/非数→NaN)。 +geopro::core::Grid parseInversionGrid(const QJsonObject& data); +// getErtRawDataScatterGraph 的 data{ xlist,ylist,hlist,vlist,projectXList,projectYList } → ScatterField。 +geopro::core::ScatterField parseScatterGraph(const QJsonObject& data); +// colorGradation/getDetail 的 data.properties.colorBar [[值,"rgba()"],…] → ColorScale。 +geopro::core::ColorScale parseColorBar(const QJsonObject& data); +// queryException 的 data 数组 → Anomaly[](location.coordinate / legend)。 +std::vector parseDatasetAnomalies(const QJsonArray& arr); + +} // namespace geopro::data::dto diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bbb4d4b..6e60a4e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,6 +37,7 @@ 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_sources(geopro_tests PRIVATE data/test_nav_dto.cpp) +target_sources(geopro_tests PRIVATE data/test_dataset_chart_dto.cpp) target_link_libraries(geopro_tests PRIVATE geopro_data) # net 层:RSA 加密器。测试需直接用 OpenSSL 生成/解密密钥,故显式 find_package diff --git a/tests/data/test_dataset_chart_dto.cpp b/tests/data/test_dataset_chart_dto.cpp new file mode 100644 index 0000000..876da41 --- /dev/null +++ b/tests/data/test_dataset_chart_dto.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include "dto/DatasetChartDto.hpp" +using namespace geopro::data::dto; + +static QJsonObject obj(const char* json) { + return QJsonDocument::fromJson(json).object(); +} + +TEST(DatasetChartDto, ParsesInversionGrid) { + auto d = obj(R"({"x":[0,1],"y":[0,1,2],"v":[[1,2],[3,4],[5,6]],"vmin":1,"vmax":6})"); + auto g = parseInversionGrid(d); + EXPECT_EQ(g.nx(), 2); EXPECT_EQ(g.ny(), 3); + EXPECT_DOUBLE_EQ(g.valueAt(1, 2), 6.0); + EXPECT_DOUBLE_EQ(g.vmax, 6.0); +} +TEST(DatasetChartDto, ParsesColorBar) { + // Use "json" delimiter to avoid raw-string termination by ")" inside rgba() + const char* colorBarJson = "{\"properties\":{\"colorBar\":[[\"10\",\"rgba(0,0,255,255)\"],[\"20\",\"rgba(255,0,0,255)\"]]}}"; + auto d = obj(colorBarJson); + auto cs = parseColorBar(d); + auto stops = cs.stopValues(); + ASSERT_EQ(stops.size(), 2u); + EXPECT_DOUBLE_EQ(stops[0], 10.0); + auto c = cs.colorAt(12.0); // [10,20) -> blue + EXPECT_GT(c.b, c.r); +} +TEST(DatasetChartDto, ParsesAnomalyPolyline) { + auto arr = QJsonDocument::fromJson( + R"([{"exceptionName":"A1","exceptionTypeName":"AnomalyZone","exceptionMarkType":2, + "legend":{"polylineColor":"#0D0101","polylineWidth":4,"polylineShape":"dash"}, + "location":{"coordinate":[{"x":1,"y":2},{"x":3,"y":4}]}}])").array(); + auto v = parseDatasetAnomalies(arr); + ASSERT_EQ(v.size(), 1u); + EXPECT_EQ(v[0].name, "A1"); + EXPECT_EQ(static_cast(v[0].markType), 2); + ASSERT_EQ(v[0].localPts.size(), 2u); + EXPECT_DOUBLE_EQ(v[0].localPts[1].x, 3.0); + EXPECT_TRUE(v[0].dashed); +}