255 lines
10 KiB
C++
255 lines
10 KiB
C++
#include <gtest/gtest.h>
|
||
#include <algorithm>
|
||
#include <QJsonDocument>
|
||
#include <QJsonObject>
|
||
#include "dto/MeasurementDto.hpp"
|
||
using namespace geopro::data::dto;
|
||
|
||
static QJsonObject obj(const char* json) {
|
||
return QJsonDocument::fromJson(json).object();
|
||
}
|
||
|
||
// scatter/graph 的 data:electrodeList + scatterGraphData.rows(17 列定位数组,取自真实夹具前 3 行)。
|
||
static const char* kScatterData = R"({
|
||
"electrodeList": [
|
||
{"electrodeNo":1,"horizontalDistance":0,"slopeDistance":0,"pseudoDepth":0},
|
||
{"electrodeNo":2,"horizontalDistance":1.08,"slopeDistance":1.2459494894830485,"pseudoDepth":0},
|
||
{"electrodeNo":3,"horizontalDistance":3.15,"slopeDistance":3.3194843662393243,"pseudoDepth":0}
|
||
],
|
||
"scatterGraphData": {
|
||
"hasCoordinate": true,
|
||
"rows": [
|
||
[2.115013167852418,2.2827169278611863,null,-1.2,242.952988,2.385,4.116,0,"1453611521843200",24.288,1,4,2,3,12.5664,1,242.952988],
|
||
[5.124875148343908,5.300065674086864,null,-2.4,-1066.592407,1.873,13.377,0,"1453611521843201",23.12,1,7,3,5,25.1327,2,-1066.592407],
|
||
[7.994418521491841,8.175900836015376,null,-3.6,100.239388,2.001,9.976,0,"1453611521843202",21.987,1,10,4,7,37.6991,3,100.239388]
|
||
],
|
||
"__rowsTotal": 576
|
||
}
|
||
})";
|
||
|
||
// colorGradation/getDetail{type:3} 的 data:与反演同构混合格式 colorBar(hex + rgba alpha 0–1)。
|
||
static const char* kColorBarData = R"json({
|
||
"properties": {
|
||
"lvlSchemeType": "normal",
|
||
"colorBar": [
|
||
["0.00", "#00008B"],
|
||
["1.51", "rgba(0, 0, 170, 1)"],
|
||
["208.40", "#FF0000"],
|
||
["1323.20", "rgba(48, 0, 48, 1)"]
|
||
]
|
||
}
|
||
})json";
|
||
|
||
// measurement/rows 的 data:filedList(列定义)+ rowList(含 vmap 摊平),取自真实夹具前 2 行。
|
||
static const char* kRowsData = R"({
|
||
"filedList": [
|
||
{"fieldCode":"a","name":"A"},
|
||
{"fieldCode":"b","name":"B"},
|
||
{"fieldCode":"k","name":"K"},
|
||
{"fieldCode":"R","name":"电阻"},
|
||
{"fieldCode":"R0","name":"视电阻率"}
|
||
],
|
||
"rowList": [
|
||
{"id":"1453611521843200","rowNo":1,"displayStatus":0,"a":1,"b":4,"k":12.5664,
|
||
"vmap":{"R":19.333542,"V":10223.44531,"I":528.793213,"R0_RD":0,"SP":127.640175,"R0":242.952988}},
|
||
{"id":"1453611521843201","rowNo":2,"displayStatus":0,"a":1,"b":7,"k":25.1327,
|
||
"vmap":{"R":2.974368,"V":1822.526123,"I":612.743958,"R0_RD":0,"SP":92.380089,"R0":74.753899}}
|
||
],
|
||
"__rowListTotal": 576
|
||
})";
|
||
|
||
TEST(MeasurementDto, ParsesScatterPositionalRows) {
|
||
auto p = parseMeasurementScatter(obj(kScatterData), obj(kColorBarData));
|
||
const auto& s = p.scatter;
|
||
|
||
// 3 个散点;连续上色(cauto,与反演原数据同路径,已无 discreteColor 字段)
|
||
// + 图例与反演统一为底部横条。
|
||
EXPECT_FALSE(p.verticalLegend); // measurement 图例改用底部横条(与反演一致)
|
||
ASSERT_EQ(s.x.size(), 3u);
|
||
ASSERT_EQ(s.y.size(), 3u);
|
||
ASSERT_EQ(s.v.size(), 3u);
|
||
|
||
// x = col1 斜距;y = col3 伪深度(负);色值 = col16。
|
||
EXPECT_DOUBLE_EQ(s.x[0], 2.2827169278611863);
|
||
EXPECT_DOUBLE_EQ(s.y[0], -1.2);
|
||
EXPECT_LT(s.y[0], 0.0); // 伪深度向下为负
|
||
EXPECT_DOUBLE_EQ(s.v[0], 242.952988);
|
||
EXPECT_DOUBLE_EQ(s.x[2], 8.175900836015376);
|
||
EXPECT_DOUBLE_EQ(s.y[2], -3.6);
|
||
|
||
// cauto 关键事实:负异常值(col16)原样保留——视图 setDataRange 据此得 cmin<0,
|
||
// 使中段视电阻率(≈25–250)归一化到色阶中部(深品红/紫),与原版 Plotly 一致。
|
||
EXPECT_DOUBLE_EQ(s.v[1], -1066.592407);
|
||
EXPECT_LT(s.v[1], 0.0); // 负异常值未被过滤
|
||
const double vMin = std::min({s.v[0], s.v[1], s.v[2]});
|
||
EXPECT_DOUBLE_EQ(vMin, -1066.592407); // 数据范围下界为负 → cmin<0
|
||
|
||
// A/B/M/N = col10-13。
|
||
ASSERT_EQ(s.a.size(), 3u);
|
||
EXPECT_DOUBLE_EQ(s.a[0], 1.0);
|
||
EXPECT_DOUBLE_EQ(s.b[0], 4.0);
|
||
EXPECT_DOUBLE_EQ(s.m[0], 2.0);
|
||
EXPECT_DOUBLE_EQ(s.n[0], 3.0);
|
||
|
||
// 电极 X = electrodeList.slopeDistance。
|
||
ASSERT_EQ(s.electrodeX.size(), 3u);
|
||
EXPECT_DOUBLE_EQ(s.electrodeX[0], 0.0);
|
||
EXPECT_DOUBLE_EQ(s.electrodeX[1], 1.2459494894830485);
|
||
|
||
// 电极编号 = electrodeList.electrodeNo(1-based),与 electrodeX 一一对应(供 hover num)。
|
||
ASSERT_EQ(s.electrodeNo.size(), 3u);
|
||
EXPECT_DOUBLE_EQ(s.electrodeNo[0], 1.0);
|
||
EXPECT_DOUBLE_EQ(s.electrodeNo[1], 2.0);
|
||
EXPECT_DOUBLE_EQ(s.electrodeNo[2], 3.0);
|
||
}
|
||
|
||
// scatter/graph 含 scatterGraphConf 时:工具条下拉项 + 默认选中 + x/y 本地重绘备选列。
|
||
static const char* kScatterDataWithConf = R"({
|
||
"electrodeList": [
|
||
{"electrodeNo":1,"horizontalDistance":0,"slopeDistance":0,"pseudoDepth":0}
|
||
],
|
||
"scatterGraphConf": {
|
||
"x": [
|
||
{"fieldCode":"horizontalDistance","name":"平距"},
|
||
{"fieldCode":"slopeDistance","name":"斜距"}
|
||
],
|
||
"y": [
|
||
{"fieldCode":"Layer No","name":"层数"},
|
||
{"fieldCode":"pseudoDepth","name":"伪深度"},
|
||
{"fieldCode":"elevationPseudoDepth","name":"伪深度+高程"}
|
||
],
|
||
"v": [
|
||
{"fieldCode":"I","name":"电流"},
|
||
{"fieldCode":"R0","name":"视电阻率"},
|
||
{"fieldCode":"SP","name":"自然电位"}
|
||
],
|
||
"vcalculationMethod": [
|
||
{"fieldCode":null,"name":"线性"},
|
||
{"fieldCode":null,"name":"对数"},
|
||
{"fieldCode":null,"name":"导数"}
|
||
]
|
||
},
|
||
"scatterGraphData": {
|
||
"rows": [
|
||
[2.115013167852418,2.2827169278611863,null,-1.2,242.952988,2.385,4.116,0,"id0",24.288,1,4,2,3,12.5664,1,242.952988]
|
||
]
|
||
}
|
||
})";
|
||
|
||
TEST(MeasurementDto, ParsesScatterToolbarConfAndDefaults) {
|
||
auto p = parseMeasurementScatter(obj(kScatterDataWithConf), obj(kColorBarData));
|
||
const auto& tb = p.toolbar;
|
||
|
||
EXPECT_FALSE(tb.empty());
|
||
ASSERT_EQ(tb.x.size(), 2u);
|
||
ASSERT_EQ(tb.y.size(), 3u);
|
||
ASSERT_EQ(tb.v.size(), 3u);
|
||
ASSERT_EQ(tb.method.size(), 3u);
|
||
|
||
// 选项名/码(顺序保留)。
|
||
EXPECT_EQ(tb.x[0].code.toStdString(), "horizontalDistance");
|
||
EXPECT_EQ(tb.x[0].name.toStdString(), std::string("平距"));
|
||
EXPECT_EQ(tb.x[1].code.toStdString(), "slopeDistance");
|
||
EXPECT_EQ(tb.method[0].name.toStdString(), std::string("线性"));
|
||
EXPECT_TRUE(tb.method[0].code.isEmpty()); // method 的 fieldCode 为 null
|
||
|
||
// 默认选中:斜距 / 伪深度 / 视电阻率 / 线性。
|
||
EXPECT_EQ(tb.defaultX.toStdString(), "slopeDistance");
|
||
EXPECT_EQ(tb.defaultY.toStdString(), "pseudoDepth");
|
||
EXPECT_EQ(tb.defaultV.toStdString(), "R0");
|
||
EXPECT_EQ(tb.defaultMethod.toStdString(), std::string("线性"));
|
||
}
|
||
|
||
TEST(MeasurementDto, CarriesAltColumnsForLocalReplot) {
|
||
auto p = parseMeasurementScatter(obj(kScatterDataWithConf), obj(kColorBarData));
|
||
|
||
// x 备选:平距(col0) / 斜距(col1);y 备选:伪深度(col3) / 伪深度+高程(col9)。
|
||
ASSERT_EQ(p.altXHorizontal.size(), 1u);
|
||
ASSERT_EQ(p.altXSlope.size(), 1u);
|
||
ASSERT_EQ(p.altYPseudo.size(), 1u);
|
||
ASSERT_EQ(p.altYElevationPseudo.size(), 1u);
|
||
EXPECT_DOUBLE_EQ(p.altXHorizontal[0], 2.115013167852418);
|
||
EXPECT_DOUBLE_EQ(p.altXSlope[0], 2.2827169278611863);
|
||
EXPECT_DOUBLE_EQ(p.altYPseudo[0], -1.2);
|
||
EXPECT_DOUBLE_EQ(p.altYElevationPseudo[0], 24.288);
|
||
}
|
||
|
||
TEST(MeasurementDto, ToolbarEmptyWhenNoConf) {
|
||
// 无 scatterGraphConf(反演原数据形状):工具条空 → 视图渲染反演工具条。
|
||
auto p = parseMeasurementScatter(obj(kScatterData), obj(kColorBarData));
|
||
EXPECT_TRUE(p.toolbar.empty());
|
||
}
|
||
|
||
TEST(MeasurementDto, ParsesScatterColorBarOpaque) {
|
||
auto p = parseMeasurementScatter(obj(kScatterData), obj(kColorBarData));
|
||
auto stops = p.scale.stops();
|
||
ASSERT_EQ(stops.size(), 4u);
|
||
// 首/末值。
|
||
EXPECT_DOUBLE_EQ(stops.front().first, 0.0);
|
||
EXPECT_DOUBLE_EQ(stops.back().first, 1323.20);
|
||
// 首色 #00008B(深蓝),末色 rgba(48,0,48,1)(alpha=1 → 255,不透明)。
|
||
EXPECT_EQ(stops.front().second.r, 0);
|
||
EXPECT_EQ(stops.front().second.g, 0);
|
||
EXPECT_EQ(stops.front().second.b, 0x8B);
|
||
EXPECT_EQ(stops.front().second.a, 255);
|
||
EXPECT_EQ(stops.back().second.r, 48);
|
||
EXPECT_EQ(stops.back().second.g, 0);
|
||
EXPECT_EQ(stops.back().second.b, 48);
|
||
EXPECT_EQ(stops.back().second.a, 255); // 回归:alpha=1 须映射为 255 不透明
|
||
}
|
||
|
||
TEST(MeasurementDto, ParsesTableColumnsAndVmapFlattened) {
|
||
auto t = parseMeasurementTable(obj(kRowsData));
|
||
|
||
// 列来自 filedList(5 列)+ 末尾追加“隐藏/显示”开关列 = 6 列。
|
||
ASSERT_EQ(t.columns.size(), 6u);
|
||
EXPECT_EQ(t.columns[0].code.toStdString(), "a");
|
||
EXPECT_EQ(t.columns[0].title.toStdString(), "A");
|
||
EXPECT_EQ(t.columns[4].code.toStdString(), "R0");
|
||
EXPECT_EQ(t.columns[4].title.toStdString(), std::string("视电阻率"));
|
||
|
||
// 2 行;total 来自 __rowListTotal。
|
||
ASSERT_EQ(t.rows.size(), 2u);
|
||
EXPECT_EQ(t.total, 576);
|
||
|
||
// 顶层列(a/b/k)从行对象取;vmap 列(R/R0)从 vmap 摊平。
|
||
ASSERT_EQ(t.rows[0].size(), 6u);
|
||
EXPECT_EQ(t.rows[0][0].toStdString(), "1"); // a(整数无小数点)
|
||
EXPECT_EQ(t.rows[0][1].toStdString(), "4"); // b
|
||
EXPECT_EQ(t.rows[0][3].toStdString(), "19.333542"); // R from vmap
|
||
EXPECT_EQ(t.rows[0][4].toStdString(), "242.952988"); // R0 from vmap
|
||
EXPECT_EQ(t.rows[1][4].toStdString(), "74.753899");
|
||
}
|
||
|
||
TEST(MeasurementDto, AppendsHideShowToggleColumn) {
|
||
auto t = parseMeasurementTable(obj(kRowsData));
|
||
|
||
// 末列为“隐藏/显示”,kind=Toggle,code=displayStatus。
|
||
ASSERT_EQ(t.columns.size(), 6u);
|
||
const auto& toggle = t.columns.back();
|
||
EXPECT_EQ(toggle.title.toStdString(), std::string("隐藏/显示"));
|
||
EXPECT_EQ(toggle.code.toStdString(), "displayStatus");
|
||
EXPECT_EQ(toggle.kind, geopro::core::TableColumnKind::Toggle);
|
||
|
||
// displayStatus==0 ⇒ ON/可见("1"),全部 ON。
|
||
ASSERT_EQ(t.rows.size(), 2u);
|
||
EXPECT_EQ(t.rows[0].back().toStdString(), "1");
|
||
EXPECT_EQ(t.rows[1].back().toStdString(), "1");
|
||
}
|
||
|
||
TEST(MeasurementDto, ToggleOffWhenDisplayStatusNonZero) {
|
||
// displayStatus!=0 ⇒ OFF("0")。
|
||
auto t = parseMeasurementTable(obj(R"({
|
||
"filedList": [{"fieldCode":"a","name":"A"}],
|
||
"rowList": [
|
||
{"id":"x","a":1,"displayStatus":0},
|
||
{"id":"y","a":2,"displayStatus":1}
|
||
]
|
||
})"));
|
||
ASSERT_EQ(t.columns.size(), 2u);
|
||
EXPECT_EQ(t.columns.back().kind, geopro::core::TableColumnKind::Toggle);
|
||
ASSERT_EQ(t.rows.size(), 2u);
|
||
EXPECT_EQ(t.rows[0].back().toStdString(), "1"); // displayStatus 0 → ON
|
||
EXPECT_EQ(t.rows[1].back().toStdString(), "0"); // displayStatus 1 → OFF
|
||
}
|