-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #145 from Maxxen/dev
- Loading branch information
Showing
10 changed files
with
372 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#include "spatial/common.hpp" | ||
#include "spatial/core/types.hpp" | ||
#include "spatial/core/functions/scalar.hpp" | ||
#include "spatial/core/functions/common.hpp" | ||
#include "spatial/core/geometry/geometry.hpp" | ||
|
||
#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" | ||
#include "duckdb/common/vector_operations/generic_executor.hpp" | ||
|
||
namespace spatial { | ||
|
||
namespace core { | ||
|
||
static void MakeEnvelopeFunction(DataChunk &args, ExpressionState &state, Vector &result) { | ||
auto &lstate = GeometryFunctionLocalState::ResetAndGet(state); | ||
auto count = args.size(); | ||
|
||
auto &min_x_vec = args.data[0]; | ||
auto &min_y_vec = args.data[1]; | ||
auto &max_x_vec = args.data[2]; | ||
auto &max_y_vec = args.data[3]; | ||
|
||
using DOUBLE_TYPE = PrimitiveType<double>; | ||
using GEOMETRY_TYPE = PrimitiveType<string_t>; | ||
|
||
GenericExecutor::ExecuteQuaternary<DOUBLE_TYPE, DOUBLE_TYPE, DOUBLE_TYPE, DOUBLE_TYPE, GEOMETRY_TYPE>( | ||
min_x_vec, min_y_vec, max_x_vec, max_y_vec, result, count, | ||
[&](DOUBLE_TYPE x_min, DOUBLE_TYPE y_min, DOUBLE_TYPE x_max, DOUBLE_TYPE y_max) { | ||
uint32_t capacity = 5; | ||
auto envelope_geom = lstate.factory.CreatePolygon(1, &capacity); | ||
// Create the exterior ring in CCW order | ||
envelope_geom.Shell().Add(Vertex(x_min.val, y_min.val)); | ||
envelope_geom.Shell().Add(Vertex(x_min.val, y_max.val)); | ||
envelope_geom.Shell().Add(Vertex(x_max.val, y_max.val)); | ||
envelope_geom.Shell().Add(Vertex(x_max.val, y_min.val)); | ||
envelope_geom.Shell().Add(Vertex(x_min.val, y_min.val)); | ||
|
||
return lstate.factory.Serialize(result, Geometry(envelope_geom)); | ||
}); | ||
} | ||
|
||
void CoreScalarFunctions::RegisterStMakeEnvelope(DatabaseInstance &db) { | ||
|
||
ScalarFunctionSet set("ST_MakeEnvelope"); | ||
|
||
set.AddFunction(ScalarFunction({LogicalType::DOUBLE, LogicalType::DOUBLE, LogicalType::DOUBLE, LogicalType::DOUBLE}, | ||
GeoTypes::GEOMETRY(), MakeEnvelopeFunction, nullptr, nullptr, nullptr, | ||
GeometryFunctionLocalState::Init)); | ||
|
||
ExtensionUtil::RegisterFunction(db, set); | ||
} | ||
|
||
} // namespace core | ||
|
||
} // namespace spatial |
143 changes: 143 additions & 0 deletions
143
spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
#include "spatial/common.hpp" | ||
#include "spatial/core/types.hpp" | ||
#include "spatial/core/functions/scalar.hpp" | ||
#include "spatial/core/functions/common.hpp" | ||
#include "spatial/core/geometry/geometry.hpp" | ||
|
||
#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" | ||
#include "duckdb/common/vector_operations/unary_executor.hpp" | ||
#include "duckdb/common/vector_operations/binary_executor.hpp" | ||
|
||
namespace spatial { | ||
|
||
namespace core { | ||
|
||
static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state, Vector &result) { | ||
auto &lstate = GeometryFunctionLocalState::ResetAndGet(state); | ||
auto count = args.size(); | ||
|
||
auto &child_vec = ListVector::GetEntry(args.data[1]); | ||
UnifiedVectorFormat format; | ||
child_vec.ToUnifiedFormat(count, format); | ||
|
||
BinaryExecutor::Execute<string_t, list_entry_t, string_t>( | ||
args.data[0], args.data[1], result, count, [&](string_t line_blob, list_entry_t &rings_list) { | ||
// First, setup the shell | ||
auto shell_geom = lstate.factory.Deserialize(line_blob); | ||
if (shell_geom.Type() != GeometryType::LINESTRING) { | ||
throw InvalidInputException("ST_MakePolygon only accepts LINESTRING geometries"); | ||
} | ||
|
||
auto &shell = shell_geom.GetLineString(); | ||
auto shell_vert_count = shell.Count(); | ||
if (shell_vert_count < 4) { | ||
throw InvalidInputException("ST_MakePolygon shell requires at least 4 vertices"); | ||
} | ||
|
||
auto &shell_verts = shell.Vertices(); | ||
if (shell_verts[0] != shell_verts[shell_vert_count - 1]) { | ||
throw InvalidInputException( | ||
"ST_MakePolygon shell must be closed (first and last vertex must be equal)"); | ||
} | ||
|
||
// Validate and count the hole ring sizes | ||
auto holes_offset = rings_list.offset; | ||
auto holes_length = rings_list.length; | ||
|
||
vector<uint32_t> rings_counts; | ||
vector<LineString> rings; | ||
rings_counts.push_back(shell_vert_count); | ||
rings.push_back(shell); | ||
|
||
for (idx_t hole_idx = 0; hole_idx < holes_length; hole_idx++) { | ||
auto mapped_idx = format.sel->get_index(holes_offset + hole_idx); | ||
if (!format.validity.RowIsValid(mapped_idx)) { | ||
continue; | ||
} | ||
|
||
auto geometry_blob = UnifiedVectorFormat::GetData<string_t>(format)[mapped_idx]; | ||
auto hole_geometry = lstate.factory.Deserialize(geometry_blob); | ||
|
||
if (hole_geometry.Type() != GeometryType::LINESTRING) { | ||
throw InvalidInputException( | ||
StringUtil::Format("ST_MakePolygon hole #%lu is not a LINESTRING geometry", hole_idx + 1)); | ||
} | ||
auto &hole = hole_geometry.GetLineString(); | ||
auto hole_count = hole.Count(); | ||
if (hole_count < 4) { | ||
throw InvalidInputException( | ||
StringUtil::Format("ST_MakePolygon hole #%lu requires at least 4 vertices", hole_idx + 1)); | ||
} | ||
|
||
auto &ring_verts = hole.Vertices(); | ||
if (ring_verts[0] != ring_verts[hole_count - 1]) { | ||
throw InvalidInputException(StringUtil::Format( | ||
"ST_MakePolygon hole #%lu must be closed (first and last vertex must be equal)", hole_idx + 1)); | ||
} | ||
|
||
rings_counts.push_back(hole_count); | ||
rings.push_back(hole); | ||
} | ||
|
||
auto polygon = lstate.factory.CreatePolygon(rings_counts.size(), rings_counts.data()); | ||
|
||
for (auto ring_idx = 0; ring_idx < rings.size(); ring_idx++) { | ||
auto &new_ring = rings[ring_idx]; | ||
auto &poly_ring = polygon.Ring(ring_idx); | ||
for (auto &v : new_ring.Vertices()) { | ||
poly_ring.Add(v); | ||
} | ||
} | ||
|
||
return lstate.factory.Serialize(result, Geometry(polygon)); | ||
}); | ||
} | ||
|
||
static void MakePolygonFromShellFunction(DataChunk &args, ExpressionState &state, Vector &result) { | ||
auto &lstate = GeometryFunctionLocalState::ResetAndGet(state); | ||
auto count = args.size(); | ||
|
||
UnaryExecutor::Execute<string_t, string_t>(args.data[0], result, count, [&](string_t &line_blob) { | ||
auto line_geom = lstate.factory.Deserialize(line_blob); | ||
|
||
if (line_geom.Type() != GeometryType::LINESTRING) { | ||
throw InvalidInputException("ST_MakePolygon only accepts LINESTRING geometries"); | ||
} | ||
|
||
auto &line = line_geom.GetLineString(); | ||
auto line_count = line.Count(); | ||
if (line_count < 4) { | ||
throw InvalidInputException("ST_MakePolygon shell requires at least 4 vertices"); | ||
} | ||
|
||
auto &line_verts = line.Vertices(); | ||
if (line_verts[0] != line_verts[line_count - 1]) { | ||
throw InvalidInputException("ST_MakePolygon shell must be closed (first and last vertex must be equal)"); | ||
} | ||
|
||
auto polygon = lstate.factory.CreatePolygon(1, &line_count); | ||
for (auto &v : line_verts) { | ||
polygon.Shell().Add(v); | ||
} | ||
|
||
return lstate.factory.Serialize(result, Geometry(polygon)); | ||
}); | ||
} | ||
|
||
void CoreScalarFunctions::RegisterStMakePolygon(DatabaseInstance &db) { | ||
|
||
ScalarFunctionSet set("ST_MakePolygon"); | ||
|
||
set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY(), LogicalType::LIST(GeoTypes::GEOMETRY())}, | ||
GeoTypes::GEOMETRY(), MakePolygonFromRingsFunction, nullptr, nullptr, nullptr, | ||
GeometryFunctionLocalState::Init)); | ||
|
||
set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY()}, GeoTypes::GEOMETRY(), MakePolygonFromShellFunction, nullptr, | ||
nullptr, nullptr, GeometryFunctionLocalState::Init)); | ||
|
||
ExtensionUtil::RegisterFunction(db, set); | ||
} | ||
|
||
} // namespace core | ||
|
||
} // namespace spatial |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
require spatial | ||
|
||
statement ok | ||
COPY (SELECT ST_GeomFromText('POINT (1 1)')) | ||
TO '__TEST_DIR__/test_seq.json' | ||
WITH (FORMAT GDAL, DRIVER 'GeoJSONSeq'); | ||
|
||
statement ok | ||
COPY (SELECT ST_GeomFromText('POINT (1 1)')) | ||
TO '__TEST_DIR__/test_seq.shp' | ||
WITH (FORMAT GDAL, DRIVER 'ESRI ShapeFile'); | ||
|
||
# MVT is broken due to threading issues | ||
#statement ok | ||
#COPY (SELECT ST_GeomFromText('POINT (1 1)')) TO '__TEST_DIR__/test_mvt.mvt' WITH (FORMAT GDAL, DRIVER 'MVT'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require spatial | ||
|
||
query I | ||
SELECT ST_AsText(ST_MakeEnvelope(5, 5, 10, 10)); | ||
---- | ||
POLYGON ((5 5, 5 10, 10 10, 10 5, 5 5)) | ||
|
||
query I | ||
SELECT ST_AsText(ST_MakeEnvelope(5, NULL, 10, 10)); | ||
---- | ||
NULL |
Oops, something went wrong.