feat(data): 样本 JSON 解析器(grid/scatter/colorscale/anomaly)
This commit is contained in:
parent
a35ababdd4
commit
fe5936a3a6
|
|
@ -9,4 +9,5 @@
|
||||||
# add_subdirectory(controller) # 联动编排
|
# add_subdirectory(controller) # 联动编排
|
||||||
#
|
#
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
add_subdirectory(data)
|
||||||
add_subdirectory(app)
|
add_subdirectory(app)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
find_package(nlohmann_json CONFIG REQUIRED)
|
||||||
|
add_library(geopro_data STATIC parse/SampleParsers.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_compile_features(geopro_data PUBLIC cxx_std_17)
|
||||||
|
set_target_properties(geopro_data PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF)
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
#include "parse/SampleParsers.hpp"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace geopro::data {
|
||||||
|
|
||||||
|
using nlohmann::json;
|
||||||
|
using namespace geopro::core;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// 读取 number 数组到 vector<double>,缺字段返回空。
|
||||||
|
std::vector<double> readDoubleArray(const json& obj, const char* key) {
|
||||||
|
std::vector<double> out;
|
||||||
|
auto it = obj.find(key);
|
||||||
|
if (it == obj.end() || !it->is_array()) return out;
|
||||||
|
out.reserve(it->size());
|
||||||
|
for (const auto& e : *it) out.push_back(e.get<double>());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Grid parseGrid(const std::string& jsonText) {
|
||||||
|
const json root = json::parse(jsonText);
|
||||||
|
const json& d = root.at("data");
|
||||||
|
|
||||||
|
const std::vector<double> xs = readDoubleArray(d, "x");
|
||||||
|
const std::vector<double> ys = readDoubleArray(d, "y");
|
||||||
|
const int nx = static_cast<int>(xs.size());
|
||||||
|
const int ny = static_cast<int>(ys.size());
|
||||||
|
|
||||||
|
Grid g(nx, ny);
|
||||||
|
g.x = xs;
|
||||||
|
g.y = ys;
|
||||||
|
|
||||||
|
// v: 外层 j=y、内层 i=x ⇒ v[j][i],写入 valueAt(i,j)(i 最快,与 values 一致)。
|
||||||
|
auto vIt = d.find("v");
|
||||||
|
if (vIt != d.end() && vIt->is_array()) {
|
||||||
|
const json& v = *vIt;
|
||||||
|
for (int j = 0; j < ny && j < static_cast<int>(v.size()); ++j) {
|
||||||
|
const json& row = v[j];
|
||||||
|
if (!row.is_array()) continue;
|
||||||
|
for (int i = 0; i < nx && i < static_cast<int>(row.size()); ++i) {
|
||||||
|
g.valueAt(i, j) = row[i].get<double>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// z: [ny][nx] 扁平成 g.z,顺序 j*nx+i(与 values 一致,i 最快)。
|
||||||
|
auto zIt = d.find("z");
|
||||||
|
if (zIt != d.end() && zIt->is_array()) {
|
||||||
|
const json& z = *zIt;
|
||||||
|
g.z.assign(static_cast<size_t>(nx) * ny, 0.0);
|
||||||
|
for (int j = 0; j < ny && j < static_cast<int>(z.size()); ++j) {
|
||||||
|
const json& row = z[j];
|
||||||
|
if (!row.is_array()) continue;
|
||||||
|
for (int i = 0; i < nx && i < static_cast<int>(row.size()); ++i) {
|
||||||
|
g.z[static_cast<size_t>(j) * nx + i] = row[i].get<double>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.elevation = readDoubleArray(d, "elevation");
|
||||||
|
g.lat = readDoubleArray(d, "lat");
|
||||||
|
g.lon = readDoubleArray(d, "lon");
|
||||||
|
g.vmin = d.value("vmin", 0.0);
|
||||||
|
g.vmax = d.value("vmax", 0.0);
|
||||||
|
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScatterField parseScatter(const std::string& jsonText) {
|
||||||
|
const json root = json::parse(jsonText);
|
||||||
|
const json& d = root.at("data");
|
||||||
|
|
||||||
|
ScatterField s;
|
||||||
|
s.x = readDoubleArray(d, "xlist");
|
||||||
|
s.y = readDoubleArray(d, "ylist");
|
||||||
|
s.z = readDoubleArray(d, "hlist");
|
||||||
|
s.v = readDoubleArray(d, "vlist");
|
||||||
|
s.projX = readDoubleArray(d, "projectXList");
|
||||||
|
s.projY = readDoubleArray(d, "projectYList");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorScale parseColorScale(const std::string& jsonText) {
|
||||||
|
const json root = json::parse(jsonText);
|
||||||
|
const json& d = root.at("data");
|
||||||
|
|
||||||
|
ColorScale cs;
|
||||||
|
auto propsIt = d.find("properties");
|
||||||
|
if (propsIt == d.end()) return cs;
|
||||||
|
auto barIt = propsIt->find("colorBar");
|
||||||
|
if (barIt == propsIt->end() || !barIt->is_array()) return cs;
|
||||||
|
|
||||||
|
for (const auto& entry : *barIt) {
|
||||||
|
if (!entry.is_array() || entry.size() < 2) continue;
|
||||||
|
const std::string valStr = entry[0].get<std::string>();
|
||||||
|
const std::string colStr = entry[1].get<std::string>();
|
||||||
|
cs.addStop(std::stod(valStr), parseColor(colStr, AlphaScale::Bit255));
|
||||||
|
}
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Anomaly> parseAnomalies(const std::string& jsonText) {
|
||||||
|
const json root = json::parse(jsonText);
|
||||||
|
const json& d = root.at("data");
|
||||||
|
|
||||||
|
std::vector<Anomaly> out;
|
||||||
|
if (!d.is_array()) return out;
|
||||||
|
out.reserve(d.size());
|
||||||
|
|
||||||
|
for (const auto& item : d) {
|
||||||
|
Anomaly a;
|
||||||
|
a.name = item.value("exceptionName", std::string{});
|
||||||
|
a.typeName = item.value("exceptionTypeName", std::string{});
|
||||||
|
a.markType = static_cast<AnomalyMarkType>(
|
||||||
|
item.value("exceptionMarkType", static_cast<int>(AnomalyMarkType::Polyline)));
|
||||||
|
|
||||||
|
// location.coordinate -> localPts
|
||||||
|
auto locIt = item.find("location");
|
||||||
|
if (locIt != item.end()) {
|
||||||
|
auto coordIt = locIt->find("coordinate");
|
||||||
|
if (coordIt != locIt->end() && coordIt->is_array()) {
|
||||||
|
for (const auto& pt : *coordIt) {
|
||||||
|
Vec2 v{pt.value("x", 0.0), pt.value("y", 0.0)};
|
||||||
|
a.localPts.push_back(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// legend 字段(存在性检查,缺则保留默认)
|
||||||
|
auto legIt = item.find("legend");
|
||||||
|
if (legIt != item.end()) {
|
||||||
|
const json& leg = *legIt;
|
||||||
|
a.lineColor = leg.value("polylineColor", a.lineColor);
|
||||||
|
a.lineWidth = leg.value("polylineWidth", a.lineWidth);
|
||||||
|
auto shapeIt = leg.find("polylineShape");
|
||||||
|
if (shapeIt != leg.end() && shapeIt->is_string()) {
|
||||||
|
a.dashed = (shapeIt->get<std::string>() == "dash");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push_back(std::move(a));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace geopro::data
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "model/Field.hpp"
|
||||||
|
#include "model/ColorScale.hpp"
|
||||||
|
#include "model/Anomaly.hpp"
|
||||||
|
namespace geopro::data {
|
||||||
|
|
||||||
|
// 纯函数样本 JSON 解析器(无 Qt/VTK)。输入为完整 JSON 文本,
|
||||||
|
// 内部读取顶层 "data" 子对象。字段缺失时做容错处理。
|
||||||
|
geopro::core::Grid parseGrid(const std::string& jsonText);
|
||||||
|
geopro::core::ScatterField parseScatter(const std::string& jsonText);
|
||||||
|
geopro::core::ColorScale parseColorScale(const std::string& jsonText);
|
||||||
|
std::vector<geopro::core::Anomaly> parseAnomalies(const std::string& jsonText);
|
||||||
|
|
||||||
|
} // namespace geopro::data
|
||||||
|
|
@ -29,4 +29,7 @@ target_sources(geopro_tests PRIVATE core/test_crs_transform.cpp)
|
||||||
target_sources(geopro_tests PRIVATE core/test_model_data.cpp)
|
target_sources(geopro_tests PRIVATE core/test_model_data.cpp)
|
||||||
target_link_libraries(geopro_tests PRIVATE geopro_core)
|
target_link_libraries(geopro_tests PRIVATE geopro_core)
|
||||||
|
|
||||||
|
target_sources(geopro_tests PRIVATE data/test_parsers.cpp)
|
||||||
|
target_link_libraries(geopro_tests PRIVATE geopro_data)
|
||||||
|
|
||||||
add_subdirectory(spike) # spike S3: banded contour 渲染验证
|
add_subdirectory(spike) # spike S3: banded contour 渲染验证
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "parse/SampleParsers.hpp"
|
||||||
|
using namespace geopro::data;
|
||||||
|
using namespace geopro::core;
|
||||||
|
|
||||||
|
static std::string slurp(const char* p){std::ifstream f(p);std::stringstream s;s<<f.rdbuf();return s.str();}
|
||||||
|
|
||||||
|
TEST(Parsers, Grid) {
|
||||||
|
Grid g = parseGrid(slurp("D:/dev/spike_data/grid.json"));
|
||||||
|
EXPECT_EQ(g.nx(), 100); EXPECT_EQ(g.ny(), 22);
|
||||||
|
EXPECT_EQ(g.values().size(), 100u*22u);
|
||||||
|
EXPECT_NEAR(g.vmin, -57.09, 0.1); EXPECT_NEAR(g.vmax, 828.08, 0.1);
|
||||||
|
EXPECT_EQ(g.lat.size(), 100u); EXPECT_EQ(g.lon.size(), 100u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Parsers, ColorScale) {
|
||||||
|
ColorScale cs = parseColorScale(slurp("D:/dev/spike_data/colorbar.json"));
|
||||||
|
EXPECT_FALSE(cs.empty());
|
||||||
|
auto c = cs.colorAt(-57.0); // 首段 -57.09 → rgba(0,0,170,255)
|
||||||
|
EXPECT_EQ(c.b, 170);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue