feat/dataset-detail-chart #5

Merged
gaozheng merged 74 commits from feat/dataset-detail-chart into main 2026-06-13 17:30:37 +08:00
5 changed files with 137 additions and 0 deletions
Showing only changes of commit e1bbf39d6d - Show all commits

View File

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

View File

@ -0,0 +1,74 @@
#include "dto/DatasetChartDto.hpp"
#include <cmath>
#include <QString>
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<double>& 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<Anomaly> parseDatasetAnomalies(const QJsonArray& arr) {
std::vector<Anomaly> 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<AnomalyMarkType>(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

View File

@ -0,0 +1,19 @@
#pragma once
#include <vector>
#include <QJsonObject>
#include <QJsonArray>
#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 } → Gridv 缺省/非数→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<geopro::core::Anomaly> parseDatasetAnomalies(const QJsonArray& arr);
} // namespace geopro::data::dto

View File

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

View File

@ -0,0 +1,42 @@
#include <gtest/gtest.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#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<int>(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);
}