From fe5936a3a6c45b7204168708bf801542d3b18b12 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Sun, 7 Jun 2026 20:27:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(data):=20=E6=A0=B7=E6=9C=AC=20JSON=20?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=99=A8(grid/scatter/colorscale/anomaly)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CMakeLists.txt | 1 + src/data/CMakeLists.txt | 6 ++ src/data/parse/SampleParsers.cpp | 150 +++++++++++++++++++++++++++++++ src/data/parse/SampleParsers.hpp | 16 ++++ tests/CMakeLists.txt | 3 + tests/data/test_parsers.cpp | 23 +++++ 6 files changed, 199 insertions(+) create mode 100644 src/data/CMakeLists.txt create mode 100644 src/data/parse/SampleParsers.cpp create mode 100644 src/data/parse/SampleParsers.hpp create mode 100644 tests/data/test_parsers.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00f98a8..a1a7f2f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,4 +9,5 @@ # add_subdirectory(controller) # 联动编排 # add_subdirectory(core) +add_subdirectory(data) add_subdirectory(app) diff --git a/src/data/CMakeLists.txt b/src/data/CMakeLists.txt new file mode 100644 index 0000000..4d6798a --- /dev/null +++ b/src/data/CMakeLists.txt @@ -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) diff --git a/src/data/parse/SampleParsers.cpp b/src/data/parse/SampleParsers.cpp new file mode 100644 index 0000000..2000e01 --- /dev/null +++ b/src/data/parse/SampleParsers.cpp @@ -0,0 +1,150 @@ +#include "parse/SampleParsers.hpp" + +#include + +namespace geopro::data { + +using nlohmann::json; +using namespace geopro::core; + +namespace { + +// 读取 number 数组到 vector,缺字段返回空。 +std::vector readDoubleArray(const json& obj, const char* key) { + std::vector 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()); + return out; +} + +} // namespace + +Grid parseGrid(const std::string& jsonText) { + const json root = json::parse(jsonText); + const json& d = root.at("data"); + + const std::vector xs = readDoubleArray(d, "x"); + const std::vector ys = readDoubleArray(d, "y"); + const int nx = static_cast(xs.size()); + const int ny = static_cast(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(v.size()); ++j) { + const json& row = v[j]; + if (!row.is_array()) continue; + for (int i = 0; i < nx && i < static_cast(row.size()); ++i) { + g.valueAt(i, j) = row[i].get(); + } + } + } + + // 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(nx) * ny, 0.0); + for (int j = 0; j < ny && j < static_cast(z.size()); ++j) { + const json& row = z[j]; + if (!row.is_array()) continue; + for (int i = 0; i < nx && i < static_cast(row.size()); ++i) { + g.z[static_cast(j) * nx + i] = row[i].get(); + } + } + } + + 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(); + const std::string colStr = entry[1].get(); + cs.addStop(std::stod(valStr), parseColor(colStr, AlphaScale::Bit255)); + } + return cs; +} + +std::vector parseAnomalies(const std::string& jsonText) { + const json root = json::parse(jsonText); + const json& d = root.at("data"); + + std::vector 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( + item.value("exceptionMarkType", static_cast(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() == "dash"); + } + } + + out.push_back(std::move(a)); + } + return out; +} + +} // namespace geopro::data diff --git a/src/data/parse/SampleParsers.hpp b/src/data/parse/SampleParsers.hpp new file mode 100644 index 0000000..0526ca0 --- /dev/null +++ b/src/data/parse/SampleParsers.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#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 parseAnomalies(const std::string& jsonText); + +} // namespace geopro::data diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3cacdcb..db204e4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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_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 渲染验证 diff --git a/tests/data/test_parsers.cpp b/tests/data/test_parsers.cpp new file mode 100644 index 0000000..11d1084 --- /dev/null +++ b/tests/data/test_parsers.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#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<