diff --git a/spatial/include/spatial/core/functions/aggregate.hpp b/spatial/include/spatial/core/functions/aggregate.hpp index d6ab6717..f1233e2b 100644 --- a/spatial/include/spatial/core/functions/aggregate.hpp +++ b/spatial/include/spatial/core/functions/aggregate.hpp @@ -9,10 +9,12 @@ struct CoreAggregateFunctions { public: static void Register(DatabaseInstance &db) { RegisterStEnvelopeAgg(db); + RegisterStSvgAgg(db); } private: static void RegisterStEnvelopeAgg(DatabaseInstance &db); + static void RegisterStSvgAgg(DatabaseInstance &db); }; } // namespace core diff --git a/spatial/include/spatial/core/functions/scalar.hpp b/spatial/include/spatial/core/functions/scalar.hpp index eb73f4ee..1066a4be 100644 --- a/spatial/include/spatial/core/functions/scalar.hpp +++ b/spatial/include/spatial/core/functions/scalar.hpp @@ -13,6 +13,7 @@ struct CoreScalarFunctions { RegisterStAsText(db); RegisterStAsWKB(db); RegisterStAsHEXWKB(db); + RegisterStAsSVG(db); RegisterStCentroid(db); RegisterStCollect(db); RegisterStCollectionExtract(db); @@ -64,6 +65,9 @@ struct CoreScalarFunctions { // ST_AsHextWKB static void RegisterStAsHEXWKB(DatabaseInstance &db); + // ST_AsSVG + static void RegisterStAsSVG(DatabaseInstance &db); + // ST_AsWKB static void RegisterStAsWKB(DatabaseInstance &db); diff --git a/spatial/src/spatial/core/functions/aggregate/CMakeLists.txt b/spatial/src/spatial/core/functions/aggregate/CMakeLists.txt index 3047e80a..a42328df 100644 --- a/spatial/src/spatial/core/functions/aggregate/CMakeLists.txt +++ b/spatial/src/spatial/core/functions/aggregate/CMakeLists.txt @@ -1,5 +1,6 @@ set(EXTENSION_SOURCES ${EXTENSION_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/st_envelope_agg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/st_svg_agg.cpp PARENT_SCOPE ) \ No newline at end of file diff --git a/spatial/src/spatial/core/functions/aggregate/st_svg_agg.cpp b/spatial/src/spatial/core/functions/aggregate/st_svg_agg.cpp new file mode 100644 index 00000000..adcf4eaf --- /dev/null +++ b/spatial/src/spatial/core/functions/aggregate/st_svg_agg.cpp @@ -0,0 +1,46 @@ +#include "duckdb/common/types/null_value.hpp" +#include "duckdb/execution/expression_executor.hpp" +#include "duckdb/planner/expression/bound_constant_expression.hpp" + +#include "spatial/common.hpp" +#include "spatial/core/functions/aggregate.hpp" +#include "duckdb/function/scalar_macro_function.hpp" + +#include "duckdb/catalog/default/default_functions.hpp" + +namespace spatial { + +namespace core { + +void CoreAggregateFunctions::RegisterStSvgAgg(DatabaseInstance &db) { + + DefaultMacro macro = {nullptr}; + macro.schema = DEFAULT_SCHEMA; + macro.name = "st_svg_agg"; + macro.parameters[0] = "geom"; + macro.parameters[1] = "style"; + macro.macro = R"--( + format(' {} ', + st_xmin(st_envelope_agg(geom)) - (st_xmax(st_envelope_agg(geom)) - st_xmin(st_envelope_agg(geom))) * padding, + st_ymin(st_envelope_agg(geom)) - (st_ymax(st_envelope_agg(geom)) - st_ymin(st_envelope_agg(geom))) * padding, + (st_xmax(st_envelope_agg(geom)) - st_xmin(st_envelope_agg(geom))) + (st_xmax(st_envelope_agg(geom)) - st_xmin(st_envelope_agg(geom))) * padding, + (st_xmax(st_envelope_agg(geom)) - st_xmin(st_envelope_agg(geom))) + (st_ymax(st_envelope_agg(geom)) - st_ymin(st_envelope_agg(geom))) * padding, + width, + height, + string_agg(ST_AsSVG(geom, style), '')); + )--"; + + auto info = DefaultFunctionGenerator::CreateInternalMacroInfo(macro); + info->function->default_parameters["order_by"] = make_uniq(Value::BIGINT(0)); + info->function->default_parameters["padding"] = make_uniq(Value::DOUBLE(0.1)); + info->function->default_parameters["width"] = make_uniq(Value::BIGINT(100)); + info->function->default_parameters["height"] = make_uniq(Value::BIGINT(100)); + info->parameter_names = {"geom", "style", "order_by", "padding", "width", "height"}; + ExtensionUtil::RegisterFunction(db, *info); + + // ExtensionUtil::RegisterFunction(db, string_agg); +} + +} // namespace core + +} // namespace spatial diff --git a/spatial/src/spatial/core/functions/scalar/CMakeLists.txt b/spatial/src/spatial/core/functions/scalar/CMakeLists.txt index deb31072..f7c95191 100644 --- a/spatial/src/spatial/core/functions/scalar/CMakeLists.txt +++ b/spatial/src/spatial/core/functions/scalar/CMakeLists.txt @@ -3,6 +3,7 @@ set(EXTENSION_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/st_area.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_asgeojson.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_ashexwkb.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/st_assvg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_astext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_aswkb.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_centroid.cpp diff --git a/spatial/src/spatial/core/functions/scalar/st_assvg.cpp b/spatial/src/spatial/core/functions/scalar/st_assvg.cpp new file mode 100644 index 00000000..c79ba2fb --- /dev/null +++ b/spatial/src/spatial/core/functions/scalar/st_assvg.cpp @@ -0,0 +1,107 @@ +#include "duckdb/common/vector_operations/binary_executor.hpp" +#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" + +#include "spatial/common.hpp" +#include "spatial/core/functions/scalar.hpp" +#include "spatial/core/functions/common.hpp" +#include "spatial/core/geometry/geometry_factory.hpp" +#include "spatial/core/types.hpp" + +namespace spatial { + +namespace core { + +//------------------------------------------------------------------------------ +// GEOMETRY -> SVG +//------------------------------------------------------------------------------ + +static void SetDefaultStyle(case_insensitive_map_t style, GeometryType type) { + switch(type) { + case GeometryType::POINT: + style["r"] = "0.1"; + break; + case GeometryType::LINESTRING: + style["fill"] = "none"; + style["stroke"] = "black"; + style["stroke-width"] = "0.01"; + break; + default: + break; + } +} + +static string GeometryToSVG(const Geometry &geom, const case_insensitive_map_t &style) { + + string attributes; + for (auto &entry : style) { + attributes += StringUtil::Format("%s=\"%s\" ", entry.first.c_str(), entry.second.c_str()); + } + + switch(geom.Type()) { + case GeometryType::POINT: { + auto point = geom.GetPoint(); + auto vertex = point.GetVertex(); + return StringUtil::Format(R"()", vertex.x, vertex.y, attributes); + } + case GeometryType::LINESTRING: { + auto linestring = geom.GetLineString(); + string result = StringUtil::Format(""; + return result; + } + default: + throw NotImplementedException("SVG for geometry type not implemented"); + } +} + +static void GeometryAsSVGFunction(DataChunk &args, ExpressionState &state, Vector &result) { + D_ASSERT(args.data.size() == 2); + auto &geom_vec = args.data[0]; + auto &map_vec = args.data[1]; + auto &key_vec = MapVector::GetKeys(map_vec); + auto &value_vec = MapVector::GetValues(map_vec); + auto key_data = FlatVector::GetData(key_vec); + auto value_data = FlatVector::GetData(value_vec); + + auto count = args.size(); + + auto &lstate = GeometryFunctionLocalState::ResetAndGet(state); + + case_insensitive_map_t style; + + BinaryExecutor::Execute(geom_vec, map_vec, result, count, [&](string_t input, list_entry_t properties) { + // Deserialize the geometry + auto geometry = lstate.factory.Deserialize(input); + + // Reset the style + style.clear(); + + // Set defaults + SetDefaultStyle(style, geometry.Type()); + + // Set the style from the properties + for(auto i = properties.offset; i < properties.offset + properties.length; i++) { + style[key_data[i].GetString()] = value_data[i].GetString(); + } + + // Convert the geometry to SVG + return StringVector::AddString(result, GeometryToSVG(geometry, style)); + }); +} + +//------------------------------------------------------------------------------ +// Register functions +//------------------------------------------------------------------------------ +void CoreScalarFunctions::RegisterStAsSVG(DatabaseInstance &db) { + ScalarFunction func("ST_AsSVG", {GeoTypes::GEOMETRY(), LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR)}, LogicalType::VARCHAR, GeometryAsSVGFunction, nullptr, + nullptr, nullptr, GeometryFunctionLocalState::Init); + ExtensionUtil::RegisterFunction(db, func); +} + +} // namespace core + +} // namespace spatial \ No newline at end of file