Skip to content

Commit

Permalink
WIP svg
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxxen committed Feb 12, 2024
1 parent b9a517f commit 8ac11a3
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 0 deletions.
2 changes: 2 additions & 0 deletions spatial/include/spatial/core/functions/aggregate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions spatial/include/spatial/core/functions/scalar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct CoreScalarFunctions {
RegisterStAsText(db);
RegisterStAsWKB(db);
RegisterStAsHEXWKB(db);
RegisterStAsSVG(db);
RegisterStCentroid(db);
RegisterStCollect(db);
RegisterStCollectionExtract(db);
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -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
)
46 changes: 46 additions & 0 deletions spatial/src/spatial/core/functions/aggregate/st_svg_agg.cpp
Original file line number Diff line number Diff line change
@@ -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('<svg viewBox="{} {} {} {}" width="{}" height="{}" xmlns="http://www.w3.org/2000/svg"> {} </svg>',
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<ConstantExpression>(Value::BIGINT(0));
info->function->default_parameters["padding"] = make_uniq<ConstantExpression>(Value::DOUBLE(0.1));
info->function->default_parameters["width"] = make_uniq<ConstantExpression>(Value::BIGINT(100));
info->function->default_parameters["height"] = make_uniq<ConstantExpression>(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
1 change: 1 addition & 0 deletions spatial/src/spatial/core/functions/scalar/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
107 changes: 107 additions & 0 deletions spatial/src/spatial/core/functions/scalar/st_assvg.cpp
Original file line number Diff line number Diff line change
@@ -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<string> 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<string> &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"(<circle cx="%f" cy="%f" %s/>)", vertex.x, vertex.y, attributes);
}
case GeometryType::LINESTRING: {
auto linestring = geom.GetLineString();
string result = StringUtil::Format("<polyline %s points=\"", attributes);
for (idx_t i = 0; i < linestring.Count(); i++) {
auto vertex = linestring.Vertices().Get(i);
result += StringUtil::Format("%f,%f ", vertex.x, vertex.y);
}
result += "\"/>";
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<string_t>(key_vec);
auto value_data = FlatVector::GetData<string_t>(value_vec);

auto count = args.size();

auto &lstate = GeometryFunctionLocalState::ResetAndGet(state);

case_insensitive_map_t<string> style;

BinaryExecutor::Execute<string_t, list_entry_t, string_t>(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

0 comments on commit 8ac11a3

Please sign in to comment.