From 4d3d8ee864ded47f178728e2d7b11e752cb61f89 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Tue, 23 Apr 2024 21:30:07 +0200 Subject: [PATCH 01/13] untyped representation --- .../spatial/core/geometry/geometry.hpp | 1653 ++++++++--------- .../core/geometry/geometry_processor.hpp | 2 +- .../spatial/core/geometry/geometry_type.hpp | 40 +- .../spatial/core/geometry/geometry_writer.hpp | 11 +- .../core/geometry/vertex_processor.hpp | 2 +- .../spatial/core/geometry/wkb_reader.hpp | 16 +- .../spatial/core/geometry/wkt_reader.hpp | 15 +- spatial/include/spatial/core/module.hpp | 1 - .../core/{geometry => util}/cursor.hpp | 0 spatial/include/spatial/core/util/math.hpp | 46 + .../spatial/core/util/misaligned_ptr.hpp | 92 + .../spatial/core/util/misaligned_ref.hpp | 40 + spatial/src/spatial/core/CMakeLists.txt | 1 + .../functions/aggregate/st_envelope_agg.cpp | 4 +- .../core/functions/cast/geometry_cast.cpp | 39 +- .../core/functions/cast/varchar_cast.cpp | 22 +- .../spatial/core/functions/cast/wkb_cast.cpp | 3 +- .../core/functions/scalar/st_asgeojson.cpp | 151 +- .../core/functions/scalar/st_collect.cpp | 62 +- .../functions/scalar/st_collectionextract.cpp | 130 +- .../core/functions/scalar/st_dimension.cpp | 2 +- .../functions/scalar/st_distance_sphere.cpp | 6 +- .../spatial/core/functions/scalar/st_dump.cpp | 37 +- .../core/functions/scalar/st_endpoint.cpp | 8 +- .../core/functions/scalar/st_exteriorring.cpp | 12 +- .../functions/scalar/st_flipcoordinates.cpp | 23 +- .../core/functions/scalar/st_force.cpp | 6 +- .../functions/scalar/st_geomfromhexwkb.cpp | 3 +- .../core/functions/scalar/st_geomfromtext.cpp | 2 +- .../core/functions/scalar/st_geomfromwkb.cpp | 5 +- .../core/functions/scalar/st_isempty.cpp | 5 +- .../core/functions/scalar/st_length.cpp | 35 +- .../core/functions/scalar/st_makeenvelope.cpp | 4 +- .../core/functions/scalar/st_makeline.cpp | 41 +- .../core/functions/scalar/st_makepolygon.cpp | 34 +- .../core/functions/scalar/st_ngeometries.cpp | 15 +- .../functions/scalar/st_ninteriorrings.cpp | 3 +- .../core/functions/scalar/st_npoints.cpp | 14 +- .../core/functions/scalar/st_perimeter.cpp | 41 +- .../core/functions/scalar/st_point.cpp | 2 +- .../core/functions/scalar/st_pointn.cpp | 10 +- .../core/functions/scalar/st_quadkey.cpp | 16 +- .../core/functions/scalar/st_startpoint.cpp | 11 +- .../src/spatial/core/geometry/CMakeLists.txt | 1 + .../src/spatial/core/geometry/geometry.cpp | 346 ++-- .../core/geometry/geometry_serialization.cpp | 146 +- .../src/spatial/core/geometry/wkb_reader.cpp | 60 +- .../src/spatial/core/geometry/wkt_reader.cpp | 51 +- .../core/io/shapefile/read_shapefile.cpp | 29 +- .../core/io/shapefile/shapefile_common.cpp | 7 +- spatial/src/spatial/core/util/math.cpp | 52 + spatial/src/spatial/gdal/file_handler.cpp | 3 +- .../src/spatial/gdal/functions/st_read.cpp | 3 +- .../functions/st_area_spheroid.cpp | 41 +- .../functions/st_length_spheroid.cpp | 44 +- .../functions/st_perimeter_spheroid.cpp | 41 +- .../geos/functions/scalar/st_is_valid.cpp | 52 +- spatial/src/spatial/geos/geos_wrappers.cpp | 19 +- spatial/src/spatial/proj/functions.cpp | 22 +- 59 files changed, 1874 insertions(+), 1708 deletions(-) rename spatial/include/spatial/core/{geometry => util}/cursor.hpp (100%) create mode 100644 spatial/include/spatial/core/util/math.hpp create mode 100644 spatial/include/spatial/core/util/misaligned_ptr.hpp create mode 100644 spatial/include/spatial/core/util/misaligned_ref.hpp create mode 100644 spatial/src/spatial/core/util/math.cpp diff --git a/spatial/include/spatial/core/geometry/geometry.hpp b/spatial/include/spatial/core/geometry/geometry.hpp index 1a019c6e..ba69d492 100644 --- a/spatial/include/spatial/core/geometry/geometry.hpp +++ b/spatial/include/spatial/core/geometry/geometry.hpp @@ -2,7 +2,8 @@ #include "spatial/common.hpp" #include "spatial/core/geometry/geometry_properties.hpp" -#include "spatial/core/geometry/cursor.hpp" +#include "spatial/core/util/cursor.hpp" +#include "spatial/core/util/misaligned_ptr.hpp" #include "spatial/core/geometry/geometry_type.hpp" #include "spatial/core/geometry/vertex.hpp" @@ -10,877 +11,910 @@ namespace spatial { namespace core { -//------------------------------------------------------------------------------ -// Geometry Objects -//------------------------------------------------------------------------------ - class Geometry; //------------------------------------------------------------------------------ -// Base Classes +// Geometry //------------------------------------------------------------------------------ -class BaseGeometry { - friend class Geometry; - friend class Wrap; +// Forward declare all accessors +struct AnyGeometry; +struct SinglePartGeometry; +struct MultiPartGeometry; +struct CollectionGeometry; +struct Point; +struct LineString; +struct Polygon; +struct MultiPoint; +struct MultiLineString; +struct MultiPolygon; +struct GeometryCollection; -protected: - GeometryType type; - GeometryProperties properties; - bool is_readonly; - uint32_t data_count; - union { - Geometry *part_data; - data_ptr_t vertex_data; - } data; - - BaseGeometry(GeometryType type, Geometry *part_data, bool is_readonly, bool has_z, bool has_m) - : type(type), properties(has_z, has_m), is_readonly(is_readonly), data_count(0), data({nullptr}) { - data.part_data = part_data; - } - - BaseGeometry(GeometryType type, data_ptr_t vert_data, bool is_readonly, bool has_z, bool has_m) - : type(type), properties(has_z, has_m), is_readonly(is_readonly), data_count(0), data({nullptr}) { - data.vertex_data = vert_data; - } - - // Copy Constructor - BaseGeometry(const BaseGeometry &other) - : type(other.type), properties(other.properties), is_readonly(true), data_count(other.data_count), - data({nullptr}) { - data = other.data; - } - - // Copy Assignment - BaseGeometry &operator=(const BaseGeometry &other) { - type = other.type; - properties = other.properties; - is_readonly = true; - data_count = other.data_count; - data = other.data; - return *this; - } - - // Move Constructor - BaseGeometry(BaseGeometry &&other) noexcept - : type(other.type), properties(other.properties), is_readonly(other.is_readonly), data_count(other.data_count), - data({nullptr}) { - data = other.data; - if (!other.is_readonly) { - // Take ownership of the data, and make the other object read-only - other.is_readonly = true; - } - } - - // Move Assignment - BaseGeometry &operator=(BaseGeometry &&other) noexcept { - type = other.type; - properties = other.properties; - is_readonly = other.is_readonly; - data_count = other.data_count; - data = other.data; - if (!other.is_readonly) { - // Take ownership of the data, and make the other object read-only - other.is_readonly = true; - } - return *this; - } +class Geometry { + friend struct SinglePartGeometry; + friend struct MultiPartGeometry; + friend struct CollectionGeometry; +private: + GeometryType type; + GeometryProperties properties; + bool is_readonly; + uint32_t data_count; + data_ptr_t data_ptr; + + Geometry(GeometryType type, GeometryProperties props, bool is_readonly, data_ptr_t data, uint32_t count) + : type(type), properties(props), is_readonly(is_readonly), data_count(count), data_ptr(data) {} public: - GeometryProperties GetProperties() const { - return properties; - } - GeometryProperties &GetProperties() { - return properties; - } - bool IsReadOnly() const { - return is_readonly; - } - uint32_t Count() const { - return data_count; - } -}; - -static_assert(sizeof(BaseGeometry) <= 16, "GeometryBase should be at most 16 bytes (can be less in WASM)"); - -//------------------------------------------------------------------------------ -// All of the following classes are just to provide a type-safe interface to the underlying geometry data, and to -// enable convenient matching of geometry types using function overloads. + // By default, create a read-only empty point + Geometry() : type(GeometryType::POINT), properties(false, false), is_readonly(true), data_count(0), data_ptr(nullptr) {} + + Geometry(GeometryType type, bool has_z, bool has_m) + : type(type), properties(has_z, has_m), is_readonly(true), data_count(0), data_ptr(nullptr) {} + + // Copy Constructor + Geometry(const Geometry &other) + : type(other.type), properties(other.properties), is_readonly(true), data_count(other.data_count), + data_ptr(other.data_ptr) { } + + // Copy Assignment + Geometry &operator=(const Geometry &other) { + type = other.type; + properties = other.properties; + is_readonly = true; + data_count = other.data_count; + data_ptr = other.data_ptr; + return *this; + } + + // Move Constructor + Geometry(Geometry &&other) noexcept + : type(other.type), properties(other.properties), is_readonly(other.is_readonly), data_count(other.data_count), + data_ptr(other.data_ptr) { + if (!other.is_readonly) { + // Take ownership of the data, and make the other object read-only + other.is_readonly = true; + } + } + + // Move Assignment + Geometry &operator=(Geometry &&other) noexcept { + type = other.type; + properties = other.properties; + is_readonly = other.is_readonly; + data_count = other.data_count; + data_ptr = other.data_ptr; + if (!other.is_readonly) { + // Take ownership of the data, and make the other object read-only + other.is_readonly = true; + } + return *this; + } -// A single part geometry, contains a single array of vertices -class SinglePartGeometry : public BaseGeometry { - friend class Geometry; - -protected: - SinglePartGeometry(GeometryType type, bool has_z, bool has_m) - : BaseGeometry(type, (data_ptr_t) nullptr, true, has_z, has_m) { - } - - SinglePartGeometry(GeometryType type, ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : BaseGeometry(type, (data_ptr_t) nullptr, false, has_z, has_m) { - data_count = count; - data.vertex_data = alloc.AllocateAligned(count * properties.VertexSize()); - } +public: + GeometryType GetType() const { return type; } + GeometryProperties &GetProperties() { return properties; } + const GeometryProperties &GetProperties() const { return properties; } + const_data_ptr_t GetData() const { return data_ptr; } + data_ptr_t GetData() { return data_ptr; } + bool IsReadOnly() const { return is_readonly; } + uint32_t Count() const { return data_count; } + + bool IsCollection() const { return GeometryTypes::IsCollection(type); } + bool IsMultiPart() const { return GeometryTypes::IsMultiPart(type); } + bool IsSinglePart() const { return GeometryTypes::IsSinglePart(type); } public: - uint32_t ByteSize() const { - return data_count * properties.VertexSize(); - } - - bool IsEmpty() const { - return data_count == 0; - } - - const_data_ptr_t GetData() const { - return data.vertex_data; - } - - void Set(uint32_t index, const VertexXY &vertex) { - D_ASSERT(index < data_count); - Store(vertex, data.vertex_data + index * properties.VertexSize()); - } - - void Set(uint32_t index, double x, double y) { - Set(index, VertexXY {x, y}); - } - - VertexXY Get(uint32_t index) const { - D_ASSERT(index < data_count); - return Load(data.vertex_data + index * properties.VertexSize()); - } - - template - void SetExact(uint32_t index, const V &vertex) { - static_assert(V::IS_VERTEX, "V must be a vertex type"); - D_ASSERT(V::HAS_Z == properties.HasZ()); - D_ASSERT(V::HAS_M == properties.HasM()); - D_ASSERT(index < data_count); - Store(vertex, data.vertex_data + index * sizeof(V)); - } - - template - V GetExact(uint32_t index) const { - static_assert(V::IS_VERTEX, "V must be a vertex type"); - D_ASSERT(V::HAS_Z == properties.HasZ()); - D_ASSERT(V::HAS_M == properties.HasM()); - D_ASSERT(index < data_count); - return Load(data.vertex_data + index * sizeof(V)); - } - - // Turn this geometry into a read-only reference to another geometry, starting at the specified index - void Reference(const SinglePartGeometry &other, uint32_t offset, uint32_t count); - - // Turn this geometry into a read-only reference to raw data - void ReferenceData(const_data_ptr_t data, uint32_t count, bool has_z, bool has_m); - - void ReferenceData(const_data_ptr_t data, uint32_t count) { - ReferenceData(data, count, properties.HasZ(), properties.HasM()); - } - - // Turn this geometry into a owning copy of another geometry, starting at the specified index - void Copy(ArenaAllocator &alloc, const SinglePartGeometry &other, uint32_t offset, uint32_t count); - - // Turn this geometry into a owning copy of raw data - void CopyData(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m); - - void CopyData(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count) { - CopyData(alloc, data, count, properties.HasZ(), properties.HasM()); - } - - // Resize the geometry, truncating or extending with zeroed vertices as needed - void Resize(ArenaAllocator &alloc, uint32_t new_count); - - // Append the data from another geometry - void Append(ArenaAllocator &alloc, const SinglePartGeometry &other); - - // Append the data from multiple other geometries - void Append(ArenaAllocator &alloc, const SinglePartGeometry *others, uint32_t others_count); - - // Force the geometry to have a specific vertex type, resizing or shrinking the data as needed - void SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, double default_z = 0, double default_m = 0); - - // If this geometry is read-only, make it mutable by copying the data - void MakeMutable(ArenaAllocator &alloc); - - // Print this geometry as a string, starting at the specified index and printing the specified number of vertices - // (useful for debugging) - string ToString(uint32_t start = 0, uint32_t count = 0) const; - - // Check if the geometry is closed (first and last vertex are the same) - // A geometry with 1 vertex is considered closed, 0 vertices are considered open - bool IsClosed() const; - - // Return the planar length of the geometry - double Length() const; + // Used for tag dispatching + struct Tags { + // Base types + struct AnyGeometry { + using ACCESS = ::spatial::core::AnyGeometry; + }; + struct SinglePartGeometry : public AnyGeometry { + using ACCESS = ::spatial::core::SinglePartGeometry; + }; + struct MultiPartGeometry : public AnyGeometry { + using ACCESS = ::spatial::core::MultiPartGeometry; + }; + struct CollectionGeometry : public MultiPartGeometry { + using ACCESS = ::spatial::core::CollectionGeometry; + }; + // Concrete types + struct Point : public SinglePartGeometry { + using ACCESS = ::spatial::core::Point; + }; + struct LineString : public SinglePartGeometry { + using ACCESS = ::spatial::core::LineString; + }; + struct Polygon : public MultiPartGeometry { + using ACCESS = ::spatial::core::Polygon; + }; + struct MultiPoint : public CollectionGeometry { + using ACCESS = ::spatial::core::MultiPoint; + }; + struct MultiLineString : public CollectionGeometry { + using ACCESS = ::spatial::core::MultiLineString; + }; + struct MultiPolygon : public CollectionGeometry { + using ACCESS = ::spatial::core::MultiPolygon; + }; + struct GeometryCollection : public CollectionGeometry { + using ACCESS = ::spatial::core::GeometryCollection; + }; + }; + + template + static auto Visit(Geometry& geom, ARGS&&... args) -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { + switch(geom.type) { + case GeometryType::POINT: return T::Case(Tags::Point{}, geom, std::forward(args)...); + case GeometryType::LINESTRING: return T::Case(Tags::LineString{}, geom, std::forward(args)...); + case GeometryType::POLYGON: return T::Case(Tags::Polygon{}, geom, std::forward(args)...); + case GeometryType::MULTIPOINT: return T::Case(Tags::MultiPoint{}, geom, std::forward(args)...); + case GeometryType::MULTILINESTRING: return T::Case(Tags::MultiLineString{}, geom, std::forward(args)...); + case GeometryType::MULTIPOLYGON: return T::Case(Tags::MultiPolygon{}, geom, std::forward(args)...); + case GeometryType::GEOMETRYCOLLECTION: return T::Case(Tags::GeometryCollection{}, geom, std::forward(args)...); + default: throw NotImplementedException("Geometry::Match"); + } + } + + template + static auto Visit(const Geometry &geom, ARGS&&... args) -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { + switch(geom.type) { + case GeometryType::POINT: return T::Case(Tags::Point{}, geom, std::forward(args)...); + case GeometryType::LINESTRING: return T::Case(Tags::LineString{}, geom, std::forward(args)...); + case GeometryType::POLYGON: return T::Case(Tags::Polygon{}, geom, std::forward(args)...); + case GeometryType::MULTIPOINT: return T::Case(Tags::MultiPoint{}, geom, std::forward(args)...); + case GeometryType::MULTILINESTRING: return T::Case(Tags::MultiLineString{}, geom, std::forward(args)...); + case GeometryType::MULTIPOLYGON: return T::Case(Tags::MultiPolygon{}, geom, std::forward(args)...); + case GeometryType::GEOMETRYCOLLECTION: return T::Case(Tags::GeometryCollection{}, geom, std::forward(args)...); + default: throw NotImplementedException("Geometry::Match"); + } + } + + // TODO: Swap this to only have two create methods, + // and use mutating methods for Reference/Copy + static Geometry Create(ArenaAllocator &alloc, GeometryType type, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(GeometryType type, bool has_z, bool has_m); + + static geometry_t Serialize(const Geometry &geom, Vector &result); + static Geometry Deserialize(ArenaAllocator &arena, const geometry_t &data); + + static bool IsEmpty(const Geometry &geom); + static uint32_t GetDimension(const Geometry &geom, bool recurse); + void SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, double default_z = 0, double default_m = 0); + + // Iterate over all points in the geometry, recursing into collections + template + static void ExtractPoints(const Geometry &geom, FUNC &&func); + + // Iterate over all lines in the geometry, recursing into collections + template + static void ExtractLines(const Geometry &geom, FUNC &&func); + + // Iterate over all polygons in the geometry, recursing into collections + template + static void ExtractPolygons(const Geometry &geom, FUNC &&func); }; -// A multi-part geometry, contains multiple parts -class MultiPartGeometry : public BaseGeometry { -protected: - MultiPartGeometry(GeometryType type, bool has_z, bool has_m) - : BaseGeometry(type, (Geometry *)nullptr, true, has_z, has_m) { - } +inline Geometry Geometry::Create(ArenaAllocator &alloc, GeometryType type, uint32_t count, bool has_z, bool has_m) { + GeometryProperties props(has_z, has_m); + auto single_part = GeometryTypes::IsSinglePart(type); + auto elem_size = single_part ? props.VertexSize() : sizeof(Geometry); + auto geom = Geometry(type, props, false, alloc.AllocateAligned(count * elem_size), count); + return geom; +} - MultiPartGeometry(GeometryType type, ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : BaseGeometry(type, (Geometry *)nullptr, false, has_z, has_m) { - data_count = count; - data.vertex_data = alloc.AllocateAligned(count * sizeof(BaseGeometry)); - } +inline Geometry Geometry::CreateEmpty(GeometryType type, bool has_z, bool has_m) { + GeometryProperties props(has_z, has_m); + return Geometry(type, props, false, nullptr, 0); +} +//------------------------------------------------------------------------------ +// Iterators +//------------------------------------------------------------------------------ +class PartView { +private: + Geometry* beg_ptr; + Geometry* end_ptr; public: - bool IsEmpty() const; - - Geometry &operator[](uint32_t index); - Geometry *begin(); - Geometry *end(); - - const Geometry &operator[](uint32_t index) const; - const Geometry *begin() const; - const Geometry *end() const; + PartView(Geometry *beg, Geometry *end) : beg_ptr(beg), end_ptr(end) {} + Geometry* begin() { return beg_ptr; } + Geometry* end() { return end_ptr; } + Geometry& operator[](uint32_t index) { return beg_ptr[index]; } +}; - void Resize(ArenaAllocator &alloc, uint32_t new_count); +class ConstPartView { +private: + const Geometry* beg_ptr; + const Geometry* end_ptr; +public: + ConstPartView(const Geometry *beg, const Geometry *end) : beg_ptr(beg), end_ptr(end) {} + const Geometry* begin() { return beg_ptr; } + const Geometry* end() { return end_ptr; } + const Geometry& operator[](uint32_t index) { return beg_ptr[index]; } }; -class CollectionGeometry : public MultiPartGeometry { -protected: - CollectionGeometry(GeometryType type, bool has_z, bool has_m) : MultiPartGeometry(type, has_z, has_m) { - } - CollectionGeometry(GeometryType type, ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : MultiPartGeometry(type, alloc, count, has_z, has_m) { - } +class VertexView { +private: + data_ptr_t beg_ptr; + data_ptr_t end_ptr; + uint32_t vertex_size; +public: + VertexView(data_ptr_t beg, data_ptr_t end, uint32_t vertex_size) + : beg_ptr(beg), end_ptr(end), vertex_size(vertex_size) {} + + StridedPtr begin() { return { beg_ptr, vertex_size }; } + StridedPtr end() { return { end_ptr, vertex_size }; } + MisalignedRef operator[](uint32_t index) { + D_ASSERT(beg_ptr + index * vertex_size < end_ptr); + return { beg_ptr + index * vertex_size }; + } }; -template -class TypedCollectionGeometry : public CollectionGeometry { -protected: - TypedCollectionGeometry(GeometryType type, bool has_z, bool has_m) : CollectionGeometry(type, has_z, has_m) { - } - TypedCollectionGeometry(GeometryType type, ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); +class ConstVertexView { +private: + const_data_ptr_t beg_ptr; + const_data_ptr_t end_ptr; + uint32_t vertex_size; +public: + ConstVertexView(const_data_ptr_t beg, const_data_ptr_t end, uint32_t vertex_size) + : beg_ptr(beg), end_ptr(end), vertex_size(vertex_size) {} + + ConstStridedPtr begin() const { return { beg_ptr, vertex_size }; } + ConstStridedPtr end() const { return { end_ptr, vertex_size }; } + ConstMisalignedRef operator[](uint32_t index) { + D_ASSERT(beg_ptr + index * vertex_size < end_ptr); + return { beg_ptr + index * vertex_size }; + } +}; +template +class TypedVertexView { + static_assert(V::IS_VERTEX, "V must be a vertex type"); + data_ptr_t beg_ptr; + data_ptr_t end_ptr; public: - T &operator[](uint32_t index); - T *begin(); - T *end(); + TypedVertexView(data_ptr_t beg, data_ptr_t end) + : beg_ptr(beg), end_ptr(end) {} + + MisalignedPtr begin() { return { beg_ptr }; } + MisalignedPtr end() { return { end_ptr }; } + MisalignedRef operator[](uint32_t index) { + D_ASSERT(beg_ptr + index * sizeof(V) < end_ptr); + return { beg_ptr + index * sizeof(V) }; + } +}; - const T &operator[](uint32_t index) const; - const T *begin() const; - const T *end() const; +template +class ConstTypedVertexView { + static_assert(V::IS_VERTEX, "V must be a vertex type"); + const_data_ptr_t beg_ptr; + const_data_ptr_t end_ptr; +public: + ConstTypedVertexView(const_data_ptr_t beg, const_data_ptr_t end) + : beg_ptr(beg), end_ptr(end) {} + + ConstMisalignedPtr begin() { return { beg_ptr }; } + ConstMisalignedPtr end() { return { end_ptr }; } + ConstMisalignedRef operator[](uint32_t index) { + D_ASSERT(beg_ptr + index * sizeof(V) < end_ptr); + return { beg_ptr + index * sizeof(V) }; + } }; //------------------------------------------------------------------------------ -// Concrete Classes +// Accessors //------------------------------------------------------------------------------ -// These are the actual Geometry types that are instantiated and used in practice - -class Point : public SinglePartGeometry { -protected: - friend class TypedCollectionGeometry; - friend class GeometryCollection; - Point(bool has_z = false, bool has_m = false) : SinglePartGeometry(TYPE, has_z, has_m) { - } - Point(ArenaAllocator &alloc, uint32_t size, bool has_z, bool has_m) - : SinglePartGeometry(TYPE, alloc, size, has_z, has_m) { - } +//------------------------------------------------------------------------------ +// AnyGeometry +//------------------------------------------------------------------------------ +struct AnyGeometry {}; -public: - static constexpr GeometryType TYPE = GeometryType::POINT; - - static Point Empty(bool has_z = false, bool has_m = false) { - return Point(has_z, has_m); - } - static Point Create(ArenaAllocator &alloc, uint32_t size, bool has_z, bool has_m) { - return Point(alloc, size, has_z, has_m); - } - - // Helpers - template - static Point FromVertex(ArenaAllocator &alloc, const V &vertex) { - static_assert(V::IS_VERTEX, "V must be a vertex type"); - auto point = Point::Create(alloc, 1, V::HAS_Z, V::HAS_M); - point.SetExact(0, vertex); - return point; - } - - static Point CopyFromData(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { - auto point = Point::Create(alloc, 1, has_z, has_m); - point.CopyData(alloc, data, 1); - return point; - } - - static Point FromReference(const SinglePartGeometry &other, uint32_t offset) { - Point point(other.GetProperties().HasZ(), other.GetProperties().HasM()); - point.Reference(other, offset, 1); - return point; - } +//------------------------------------------------------------------------------ +// SinglePartGeometry +//------------------------------------------------------------------------------ +struct SinglePartGeometry { + + // Turn this geometry into a read-only reference to raw data + static void ReferenceData(Geometry &geom, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { + geom.data_count = count; + geom.data_ptr = const_cast(data); + geom.is_readonly = true; + geom.properties.SetZ(has_z); + geom.properties.SetM(has_m); + } + + // Turn this geometry into a read-only reference to another geometry, starting at the specified index + static void ReferenceData(Geometry &geom, const Geometry &other, uint32_t offset, uint32_t count) { + D_ASSERT(GeometryTypes::IsSinglePart(other.GetType())); + D_ASSERT(offset + count <= other.data_count); + auto vertex_size = other.properties.VertexSize(); + auto has_z = other.properties.HasZ(); + auto has_m = other.properties.HasM(); + ReferenceData(geom, other.data_ptr + offset * vertex_size, count, has_z, has_m); + } + + static void ReferenceData(Geometry &geom, const_data_ptr_t data, uint32_t count) { + ReferenceData(geom, data, count, geom.properties.HasZ(), geom.properties.HasM()); + } + + // Turn this geometry into a owning copy of raw data + static void CopyData(Geometry &geom, ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { + auto old_vertex_size = geom.properties.VertexSize(); + geom.properties.SetZ(has_z); + geom.properties.SetM(has_m); + auto new_vertex_size = geom.properties.VertexSize(); + if(geom.is_readonly) { + geom.data_ptr = alloc.AllocateAligned(count * new_vertex_size); + } else if(geom.data_count != count) { + geom.data_ptr = alloc.ReallocateAligned(geom.data_ptr, geom.data_count * old_vertex_size, count * new_vertex_size); + } + memcpy(geom.data_ptr, data, count * new_vertex_size); + geom.data_count = count; + geom.is_readonly = false; + } + + // Turn this geometry into a owning copy of another geometry, starting at the specified index + static void CopyData(Geometry &geom, ArenaAllocator &alloc, const Geometry &other, uint32_t offset, uint32_t count) { + D_ASSERT(GeometryTypes::IsSinglePart(other.GetType())); + D_ASSERT(offset + count <= other.data_count); + auto vertex_size = geom.properties.VertexSize(); + auto has_z = other.properties.HasZ(); + auto has_m = other.properties.HasM(); + CopyData(geom, alloc, other.data_ptr + offset * vertex_size, count, has_z, has_m); + } + + static void CopyData(Geometry &geom, ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count) { + CopyData(geom, alloc, data, count, geom.properties.HasZ(), geom.properties.HasM()); + } + + // Resize the geometry, truncating or extending with zeroed vertices as needed + static void Resize(Geometry &geom, ArenaAllocator &alloc, uint32_t new_count); + + // Append the data from another geometry + static void Append(Geometry &geom, ArenaAllocator &alloc, const Geometry &other); + + // Append the data from multiple other geometries + static void Append(Geometry &geom, ArenaAllocator &alloc, const Geometry *others, uint32_t others_count); + + // Force the geometry to have a specific vertex type, resizing or shrinking the data as needed + static void SetVertexType(Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z = 0, double default_m = 0); + + // If this geometry is read-only, make it mutable by copying the data + static void MakeMutable(Geometry &geom, ArenaAllocator &alloc); + + // Print this geometry as a string, starting at the specified index and printing the specified number of vertices + // (useful for debugging) + static string ToString(const Geometry &geom, uint32_t start = 0, uint32_t count = 0); + + // Check if the geometry is closed (first and last vertex are the same) + // A geometry with 1 vertex is considered closed, 0 vertices are considered open + static bool IsClosed(const Geometry &geom); + static bool IsEmpty(const Geometry &geom); + + // Return the planar length of the geometry + static double Length(const Geometry &geom); + + static VertexXY GetVertex(const Geometry &geom, uint32_t index); + static void SetVertex(Geometry &geom, uint32_t index, const VertexXY &vertex); + + template + static V GetVertex(const Geometry &geom, uint32_t index); + + template + static void SetVertex(Geometry &geom, uint32_t index, const V &vertex); + + template + static MisalignedRef Vertex(Geometry &geom, uint32_t index); + static MisalignedRef Vertex(Geometry &geom, uint32_t index); + + template + static ConstMisalignedRef Vertex(const Geometry &geom, uint32_t index); + static ConstMisalignedRef Vertex(const Geometry &geom, uint32_t index); + + static uint32_t VertexCount(const Geometry &geom); + static uint32_t VertexSize(const Geometry &geom); + static uint32_t ByteSize(const Geometry &geom); + + static VertexView Vertices(Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + auto count = VertexCount(geom); + auto size = VertexSize(geom); + return { geom.GetData(), geom.GetData() + count * size, size }; + } + + static ConstVertexView Vertices(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + auto count = VertexCount(geom); + auto size = VertexSize(geom); + return { geom.GetData(), geom.GetData() + count * size, size }; + } }; -class LineString : public SinglePartGeometry { -protected: - friend class TypedCollectionGeometry; - friend class Polygon; +inline VertexXY SinglePartGeometry::GetVertex(const Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return Load(geom.GetData() + index * geom.GetProperties().VertexSize()); +} - LineString(bool has_z = false, bool has_m = false) : SinglePartGeometry(TYPE, has_z, has_m) { - } - LineString(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : SinglePartGeometry(TYPE, alloc, count, has_z, has_m) { - } +inline void SinglePartGeometry::SetVertex(Geometry &geom, uint32_t index, const VertexXY &vertex) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(index < geom.data_count); + Store(vertex, geom.GetData() + index * geom.GetProperties().VertexSize()); +} -public: - static constexpr GeometryType TYPE = GeometryType::LINESTRING; - - static LineString Empty(bool has_z = false, bool has_m = false) { - return LineString(has_z, has_m); - } - static LineString Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return LineString(alloc, count, has_z, has_m); - } - - static LineString CopyFromData(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, - bool has_m) { - LineString line(alloc, count, has_z, has_m); - line.CopyData(alloc, data, count); - return line; - } -}; +template +inline V SinglePartGeometry::GetVertex(const Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); + D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); + D_ASSERT(index < geom.data_count); + return Load(geom.GetData() + index * sizeof(V)); +} -class Polygon : public MultiPartGeometry { -protected: - friend class TypedCollectionGeometry; +template +inline void SinglePartGeometry::SetVertex(Geometry &geom, uint32_t index, const V &vertex) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); + D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); + D_ASSERT(index < geom.data_count); + Store(vertex, geom.GetData() + index * sizeof(V)); +} - Polygon(bool has_z = false, bool has_m = false) : MultiPartGeometry(TYPE, has_z, has_m) { - } - Polygon(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); +template +inline MisalignedRef SinglePartGeometry::Vertex(Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); + D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); + D_ASSERT(index < geom.data_count); + return { geom.GetData() + index * sizeof(V) }; +} -public: - static constexpr GeometryType TYPE = GeometryType::POLYGON; - - static Polygon Empty(bool has_z = false, bool has_m = false) { - return Polygon(has_z, has_m); - } - static Polygon Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return Polygon(alloc, count, has_z, has_m); - } - - LineString &operator[](uint32_t index); - LineString *begin(); - LineString *end(); - - const LineString &operator[](uint32_t index) const; - const LineString *begin() const; - const LineString *end() const; - - // TODO: Generalize this to take a min/max vertex instead - static Polygon FromBox(ArenaAllocator &alloc, double minx, double miny, double maxx, double maxy) { - Polygon box(alloc, 1, false, false); - auto &ring = box[0]; - ring.Resize(alloc, 5); - ring.SetExact(0, VertexXY {minx, miny}); - ring.SetExact(1, VertexXY {minx, maxy}); - ring.SetExact(2, VertexXY {maxx, maxy}); - ring.SetExact(3, VertexXY {maxx, miny}); - ring.SetExact(4, VertexXY {minx, miny}); - return box; - } -}; +inline MisalignedRef SinglePartGeometry::Vertex(Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return { geom.GetData() + index * geom.GetProperties().VertexSize() }; +} -class MultiPoint : public TypedCollectionGeometry { -protected: - MultiPoint(bool has_z = false, bool has_m = false) : TypedCollectionGeometry(TYPE, has_z, has_m) { - } - MultiPoint(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : TypedCollectionGeometry(TYPE, alloc, count, has_z, has_m) { - } +template +inline ConstMisalignedRef SinglePartGeometry::Vertex(const Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); + D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); + D_ASSERT(index < geom.data_count); + return { geom.GetData() + index * sizeof(V) }; +} -public: - static constexpr GeometryType TYPE = GeometryType::MULTIPOINT; - - static MultiPoint Empty(bool has_z = false, bool has_m = false) { - return MultiPoint(has_z, has_m); - } - static MultiPoint Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return MultiPoint(alloc, count, has_z, has_m); - } -}; +inline ConstMisalignedRef SinglePartGeometry::Vertex(const Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return { geom.GetData() + index * geom.GetProperties().VertexSize() }; +} -class MultiLineString : public TypedCollectionGeometry { -protected: - MultiLineString(bool has_z = false, bool has_m = false) : TypedCollectionGeometry(TYPE, has_z, has_m) { - } - MultiLineString(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : TypedCollectionGeometry(TYPE, alloc, count, has_z, has_m) { - } +inline uint32_t SinglePartGeometry::VertexCount(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + return geom.data_count; +} -public: - static constexpr GeometryType TYPE = GeometryType::MULTILINESTRING; - - static MultiLineString Empty(bool has_z = false, bool has_m = false) { - return MultiLineString(has_z, has_m); - } - static MultiLineString Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return MultiLineString(alloc, count, has_z, has_m); - } -}; +inline uint32_t SinglePartGeometry::VertexSize(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + return geom.GetProperties().VertexSize(); +} -class MultiPolygon : public TypedCollectionGeometry { -protected: - MultiPolygon(bool has_z = false, bool has_m = false) : TypedCollectionGeometry(TYPE, has_z, has_m) { - } - MultiPolygon(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : TypedCollectionGeometry(TYPE, alloc, count, has_z, has_m) { - } +inline uint32_t SinglePartGeometry::ByteSize(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + return geom.data_count * geom.GetProperties().VertexSize(); +} -public: - static constexpr GeometryType TYPE = GeometryType::MULTIPOLYGON; - - static MultiPolygon Empty(bool has_z = false, bool has_m = false) { - return MultiPolygon(has_z, has_m); - } - static MultiPolygon Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return MultiPolygon(alloc, count, has_z, has_m); - } +inline bool SinglePartGeometry::IsEmpty(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + return geom.data_count == 0; +} + +//------------------------------------------------------------------------------ +// MultiPartGeometry +//------------------------------------------------------------------------------ +struct MultiPartGeometry { + + // static void Resize(Geometry &geom, ArenaAllocator &alloc, uint32_t new_count); + + static uint32_t PartCount(const Geometry &geom); + static Geometry& Part(Geometry &geom, uint32_t index); + static const Geometry& Part(const Geometry &geom, uint32_t index); + static PartView Parts(Geometry &geom); + static ConstPartView Parts(const Geometry &geom); + + static bool IsEmpty(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + for(uint32_t i = 0; i < geom.data_count; i++) { + if(!Geometry::IsEmpty(Part(geom, i))) { + return false; + } + } + return true; + } }; -class GeometryCollection : public CollectionGeometry { -protected: - GeometryCollection(bool has_z = false, bool has_m = false) : CollectionGeometry(TYPE, has_z, has_m) { - } - GeometryCollection(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); +inline uint32_t MultiPartGeometry::PartCount(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + return geom.data_count; +} -public: - static constexpr GeometryType TYPE = GeometryType::GEOMETRYCOLLECTION; - - static GeometryCollection Empty(bool has_z = false, bool has_m = false) { - return GeometryCollection(has_z, has_m); - } - static GeometryCollection Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return GeometryCollection(alloc, count, has_z, has_m); - } -}; +inline Geometry& MultiPartGeometry::Part(Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return *reinterpret_cast(geom.GetData() + index * sizeof(Geometry)); +} -class Geometry { - union { - Point point; - LineString linestring; - Polygon polygon; - MultiPoint multipoint; - MultiLineString multilinestring; - MultiPolygon multipolygon; - GeometryCollection collection; - }; +inline const Geometry& MultiPartGeometry::Part(const Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return *reinterpret_cast(geom.GetData() + index * sizeof(Geometry)); +} -public: - // This is legal because all members is standard layout and have the same common initial sequence - // Additionally, a union is pointer-interconvertible with its first member - GeometryType GetType() const { - return point.type; - } - GeometryProperties GetProperties() const { - return point.properties; - } - GeometryProperties &GetProperties() { - return point.properties; - } - bool IsReadOnly() const { - return reinterpret_cast(*this).IsReadOnly(); - } - bool IsCollection() const { - auto type = GetType(); - return type == GeometryType::MULTIPOINT || type == GeometryType::MULTILINESTRING || - type == GeometryType::MULTIPOLYGON || type == GeometryType::GEOMETRYCOLLECTION; - } - - // NOLINTBEGIN - Geometry(Point point) : point(point) { - } - Geometry(LineString linestring) : linestring(linestring) { - } - Geometry(Polygon polygon) : polygon(polygon) { - } - Geometry(MultiPoint multipoint) : multipoint(multipoint) { - } - Geometry(MultiLineString multilinestring) : multilinestring(multilinestring) { - } - Geometry(MultiPolygon multipolygon) : multipolygon(multipolygon) { - } - Geometry(GeometryCollection collection) : collection(collection) { - } - // NOLINTEND - - // Copy Constructor - Geometry(const Geometry &other) { - switch (other.GetType()) { - case GeometryType::POINT: - new (&point) Point(other.point); - break; - case GeometryType::LINESTRING: - new (&linestring) LineString(other.linestring); - break; - case GeometryType::POLYGON: - new (&polygon) Polygon(other.polygon); - break; - case GeometryType::MULTIPOINT: - new (&multipoint) MultiPoint(other.multipoint); - break; - case GeometryType::MULTILINESTRING: - new (&multilinestring) MultiLineString(other.multilinestring); - break; - case GeometryType::MULTIPOLYGON: - new (&multipolygon) MultiPolygon(other.multipolygon); - break; - case GeometryType::GEOMETRYCOLLECTION: - new (&collection) GeometryCollection(other.collection); - break; - default: - throw NotImplementedException("Geometry::Geometry(const Geometry&)"); - } - } - - // Copy Assignment - Geometry &operator=(const Geometry &other) { - if (this == &other) { - return *this; - } - this->~Geometry(); - switch (other.GetType()) { - case GeometryType::POINT: - new (&point) Point(other.point); - break; - case GeometryType::LINESTRING: - new (&linestring) LineString(other.linestring); - break; - case GeometryType::POLYGON: - new (&polygon) Polygon(other.polygon); - break; - case GeometryType::MULTIPOINT: - new (&multipoint) MultiPoint(other.multipoint); - break; - case GeometryType::MULTILINESTRING: - new (&multilinestring) MultiLineString(other.multilinestring); - break; - case GeometryType::MULTIPOLYGON: - new (&multipolygon) MultiPolygon(other.multipolygon); - break; - case GeometryType::GEOMETRYCOLLECTION: - new (&collection) GeometryCollection(other.collection); - break; - default: - throw NotImplementedException("Geometry::operator=(const Geometry&)"); - } - return *this; - } - - // Move Constructor - Geometry(Geometry &&other) noexcept { - switch (other.GetType()) { - case GeometryType::POINT: - new (&point) Point(std::move(other.point)); - break; - case GeometryType::LINESTRING: - new (&linestring) LineString(std::move(other.linestring)); - break; - case GeometryType::POLYGON: - new (&polygon) Polygon(std::move(other.polygon)); - break; - case GeometryType::MULTIPOINT: - new (&multipoint) MultiPoint(std::move(other.multipoint)); - break; - case GeometryType::MULTILINESTRING: - new (&multilinestring) MultiLineString(std::move(other.multilinestring)); - break; - case GeometryType::MULTIPOLYGON: - new (&multipolygon) MultiPolygon(std::move(other.multipolygon)); - break; - case GeometryType::GEOMETRYCOLLECTION: - new (&collection) GeometryCollection(std::move(other.collection)); - break; - default: - D_ASSERT(false); - new (&point) Point(std::move(other.point)); - break; - } - } - - // Move Assignment - Geometry &operator=(Geometry &&other) noexcept { - if (this == &other) { - return *this; - } - this->~Geometry(); - switch (other.GetType()) { - case GeometryType::POINT: - new (&point) Point(std::move(other.point)); - break; - case GeometryType::LINESTRING: - new (&linestring) LineString(std::move(other.linestring)); - break; - case GeometryType::POLYGON: - new (&polygon) Polygon(std::move(other.polygon)); - break; - case GeometryType::MULTIPOINT: - new (&multipoint) MultiPoint(std::move(other.multipoint)); - break; - case GeometryType::MULTILINESTRING: - new (&multilinestring) MultiLineString(std::move(other.multilinestring)); - break; - case GeometryType::MULTIPOLYGON: - new (&multipolygon) MultiPolygon(std::move(other.multipolygon)); - break; - case GeometryType::GEOMETRYCOLLECTION: - new (&collection) GeometryCollection(std::move(other.collection)); - break; - default: - D_ASSERT(false); - new (&point) Point(std::move(other.point)); - break; - } - return *this; - } - - template - T &As() & { - D_ASSERT(GetType() == T::TYPE); - return reinterpret_cast(*this); - } - - template - const T &As() const & { - D_ASSERT(GetType() == T::TYPE); - return reinterpret_cast(*this); - } - - // Apply a functor to the contained geometry - template - auto Visit(ARGS &&...args) const -> decltype(F::Apply(std::declval(), std::forward(args)...)) { - switch (GetType()) { - case GeometryType::POINT: - return F::Apply(const_cast(point), std::forward(args)...); - case GeometryType::LINESTRING: - return F::Apply(const_cast(linestring), std::forward(args)...); - case GeometryType::POLYGON: - return F::Apply(const_cast(polygon), std::forward(args)...); - case GeometryType::MULTIPOINT: - return F::Apply(const_cast(multipoint), std::forward(args)...); - case GeometryType::MULTILINESTRING: - return F::Apply(const_cast(multilinestring), std::forward(args)...); - case GeometryType::MULTIPOLYGON: - return F::Apply(const_cast(multipolygon), std::forward(args)...); - case GeometryType::GEOMETRYCOLLECTION: - return F::Apply(const_cast(collection), std::forward(args)...); - default: - throw NotImplementedException("Geometry::Visit()"); - } - } - - // Apply a functor to the contained geometry - template - auto Visit(ARGS &&...args) -> decltype(F::Apply(std::declval(), std::forward(args)...)) { - switch (GetType()) { - case GeometryType::POINT: - return F::Apply(static_cast(point), std::forward(args)...); - case GeometryType::LINESTRING: - return F::Apply(static_cast(linestring), std::forward(args)...); - case GeometryType::POLYGON: - return F::Apply(static_cast(polygon), std::forward(args)...); - case GeometryType::MULTIPOINT: - return F::Apply(static_cast(multipoint), std::forward(args)...); - case GeometryType::MULTILINESTRING: - return F::Apply(static_cast(multilinestring), std::forward(args)...); - case GeometryType::MULTIPOLYGON: - return F::Apply(static_cast(multipolygon), std::forward(args)...); - case GeometryType::GEOMETRYCOLLECTION: - return F::Apply(static_cast(collection), std::forward(args)...); - default: - throw NotImplementedException("Geometry::Visit()"); - } - } - - uint32_t GetDimension(bool skip_empty) const { - if (skip_empty && IsEmpty()) { - return 0; - } - struct op { - static uint32_t Apply(const Point &, bool) { - return 0; - } - static uint32_t Apply(const LineString &, bool) { - return 1; - } - static uint32_t Apply(const Polygon &, bool) { - return 2; - } - static uint32_t Apply(const MultiPoint &, bool) { - return 0; - } - static uint32_t Apply(const MultiLineString &, bool) { - return 1; - } - static uint32_t Apply(const MultiPolygon &, bool) { - return 2; - } - static uint32_t Apply(const GeometryCollection &gc, bool skip_empty) { - uint32_t max = 0; - - for (const auto &item : gc) { - max = std::max(max, item.GetDimension(skip_empty)); - } - return max; - } - }; - return Visit(skip_empty); - } - - bool IsEmpty() const { - struct op { - static bool Apply(const SinglePartGeometry &g) { - return g.IsEmpty(); - } - static bool Apply(const MultiPartGeometry &g) { - return g.IsEmpty(); - } - }; - return Visit(); - } - - void SetVertexType(ArenaAllocator &arena, bool has_z, bool has_m, double default_z = 0, double default_m = 0) { - struct op { - static void Apply(SinglePartGeometry &g, ArenaAllocator &arena, bool has_z, bool has_m, double default_z, - double default_m) { - g.SetVertexType(arena, has_z, has_m); - } - static void Apply(MultiPartGeometry &g, ArenaAllocator &arena, bool has_z, bool has_m, double default_z, - double default_m) { - g.properties.SetZ(has_z); - g.properties.SetM(has_m); - for (auto &part : g) { - part.SetVertexType(arena, has_z, has_m, default_z, default_m); - } - } - }; - Visit(arena, has_z, has_m, default_z, default_m); - } - - geometry_t Serialize(Vector &result); - static Geometry Deserialize(ArenaAllocator &arena, const geometry_t &data); +inline PartView MultiPartGeometry::Parts(Geometry &geom) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + auto ptr = reinterpret_cast(geom.GetData()); + return { ptr, ptr + geom.data_count }; +} + +inline ConstPartView MultiPartGeometry::Parts(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + auto ptr = reinterpret_cast(geom.GetData()); + return { ptr, ptr + geom.data_count }; +} + +//------------------------------------------------------------------------------ +// CollectionGeometry +//------------------------------------------------------------------------------ +struct CollectionGeometry : public MultiPartGeometry { +protected: + static Geometry Create(ArenaAllocator &alloc, GeometryType type, vector &items, bool has_z, bool has_m) { + D_ASSERT(GeometryTypes::IsCollection(type)); + auto collection = Geometry::Create(alloc, type, items.size(), has_z, has_m); + for(uint32_t i = 0; i < items.size(); i++) { + CollectionGeometry::Part(collection, i) = std::move(items[i]); + } + return collection; + } }; //------------------------------------------------------------------------------ -// Inlined Methods +// Point //------------------------------------------------------------------------------ +struct Point : public SinglePartGeometry { + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + + template + static Geometry CreateFromVertex(ArenaAllocator &alloc, const V &vertex); + + static Geometry CreateFromCopy(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { + auto point = Point::Create(alloc, 1, has_z, has_m); + SinglePartGeometry::CopyData(point, alloc, data, count, has_z, has_m); + return point; + } + + // Methods + template + static V GetVertex(const Geometry &geom); + + template + static void SetVertex(Geometry &geom, const V &vertex); -inline Polygon::Polygon(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : MultiPartGeometry(TYPE, alloc, count, has_z, has_m) { - auto ptr = data.part_data; - for (uint32_t i = 0; i < count; i++) { - new (ptr++) LineString(alloc, 0, has_z, has_m); - } + // Constants + static const constexpr GeometryType TYPE = GeometryType::POINT; + static const constexpr int SURFACE_DIMENSION = 0; +}; + +inline Geometry Point::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { + return Geometry::Create(alloc, TYPE, count, has_z, has_m); } -inline GeometryCollection::GeometryCollection(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) - : CollectionGeometry(TYPE, alloc, count, has_z, has_m) { - auto ptr = data.part_data; - for (uint32_t i = 0; i < count; i++) { - new (ptr++) Point(has_z, has_m); - } +inline Geometry Point::CreateEmpty(bool has_z, bool has_m) { + return Geometry::CreateEmpty(TYPE, has_z, has_m); } -template -inline TypedCollectionGeometry::TypedCollectionGeometry(GeometryType type, ArenaAllocator &alloc, uint32_t count, - bool has_z, bool has_m) - : CollectionGeometry(type, alloc, count, has_z, has_m) { - auto ptr = data.part_data; - for (uint32_t i = 0; i < count; i++) { - new (ptr++) T(has_z, has_m); - } +template +inline Geometry Point::CreateFromVertex(ArenaAllocator &alloc, const V &vertex) { + auto point = Create(alloc, 1, V::HAS_Z, V::HAS_M); + Point::SetVertex(point, vertex); + return point; } -//------------------- -// MultiPartGeometry -//------------------- +template +inline V Point::GetVertex(const Geometry &geom) { + D_ASSERT(geom.GetType() == TYPE); + D_ASSERT(geom.Count() == 1); + D_ASSERT(geom.GetProperties().HasZ() == V::HAS_Z); + D_ASSERT(geom.GetProperties().HasM() == V::HAS_M); + return SinglePartGeometry::GetVertex(geom, 0); +} -inline bool MultiPartGeometry::IsEmpty() const { - for (const auto &part : *this) { - if (!part.IsEmpty()) { - return false; - } - } - return true; +template +void Point::SetVertex(Geometry &geom, const V &vertex) { + D_ASSERT(geom.GetType() == TYPE); + D_ASSERT(geom.Count() == 1); + D_ASSERT(geom.GetProperties().HasZ() == V::HAS_Z); + D_ASSERT(geom.GetProperties().HasM() == V::HAS_M); + SinglePartGeometry::SetVertex(geom, 0, vertex); } -inline Geometry &MultiPartGeometry::operator[](uint32_t index) { - return data.part_data[index]; +//------------------------------------------------------------------------------ +// LineString +//------------------------------------------------------------------------------ +struct LineString : public SinglePartGeometry { + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + + static Geometry CreateFromCopy(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { + auto line = LineString::Create(alloc, 1, has_z, has_m); + SinglePartGeometry::CopyData(line, alloc, data, count, has_z, has_m); + return line; + } + + // TODO: Wrap + // Create a new LineString referencing a slice of the this linestring + static Geometry GetSliceAsReference(const Geometry &geom, uint32_t start, uint32_t count) { + auto line = LineString::CreateEmpty(geom.GetProperties().HasZ(), geom.GetProperties().HasM()); + SinglePartGeometry::ReferenceData(line, geom, start, count); + return line; + } + + // TODO: Wrap + // Create a new LineString referencing a single point in the this linestring + static Geometry GetPointAsReference(const Geometry &geom, uint32_t index) { + auto count = index >= geom.Count() ? 0 : 1; + auto point = Point::CreateEmpty(geom.GetProperties().HasZ(), geom.GetProperties().HasM()); + SinglePartGeometry::ReferenceData(point, geom, index, count); + return point; + } + + // Constants + static const constexpr GeometryType TYPE = GeometryType::LINESTRING; + static const constexpr int SURFACE_DIMENSION = 1; +}; + +inline Geometry LineString::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { + return Geometry::Create(alloc, TYPE, count, has_z, has_m); } -inline Geometry *MultiPartGeometry::begin() { - return data.part_data; + +inline Geometry LineString::CreateEmpty(bool has_z, bool has_m) { + return Geometry::CreateEmpty(TYPE, has_z, has_m); } -inline Geometry *MultiPartGeometry::end() { - return data.part_data + data_count; + +//------------------------------------------------------------------------------ +// Polygon +//------------------------------------------------------------------------------ +struct Polygon : public MultiPartGeometry { + // Constructors + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry CreateFromBox(ArenaAllocator &alloc, double minx, double miny, double maxx, double maxy); + + // Methods + + // Constants + static const constexpr GeometryType TYPE = GeometryType::POLYGON; + static const constexpr int SURFACE_DIMENSION = 2; +}; + +inline Geometry Polygon::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for(uint32_t i = 0; i < count; i++) { + // Placement new + new (&Polygon::Part(geom, i)) Geometry(GeometryType::LINESTRING, has_z, has_m); + } + return geom; } -inline const Geometry &MultiPartGeometry::operator[](uint32_t index) const { - return data.part_data[index]; +inline Geometry Polygon::CreateEmpty(bool has_z, bool has_m) { + return Geometry::CreateEmpty(TYPE, has_z, has_m); } -inline const Geometry *MultiPartGeometry::begin() const { - return data.part_data; + +inline Geometry Polygon::CreateFromBox(ArenaAllocator &alloc, double minx, double miny, double maxx, double maxy) { + auto polygon = Polygon::Create(alloc, 1, false, false); + auto &ring = Polygon::Part(polygon, 0); + LineString::Resize(ring, alloc, 5); + LineString::SetVertex(ring, 0, { minx, miny }); + LineString::SetVertex(ring, 1, { miny, maxy }); + LineString::SetVertex(ring, 2, { maxx, maxy }); + LineString::SetVertex(ring, 3, { maxx, miny }); + LineString::SetVertex(ring, 4, { minx, miny }); + return polygon; } -inline const Geometry *MultiPartGeometry::end() const { - return data.part_data + data_count; + +//------------------------------------------------------------------------------ +// MultiPoint +//------------------------------------------------------------------------------ +struct MultiPoint : public CollectionGeometry { + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + + // Constants + static const constexpr GeometryType TYPE = GeometryType::MULTIPOINT; + static const constexpr int SURFACE_DIMENSION = 0; +}; + +inline Geometry MultiPoint::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for(uint32_t i = 0; i < count; i++) { + // Placement new + new (&MultiPoint::Part(geom, i)) Geometry(GeometryType::POINT, has_z, has_m); + } + return geom; } -//----------------- -// Polygon -//----------------- +inline Geometry MultiPoint::CreateEmpty(bool has_z, bool has_m) { + return Geometry::CreateEmpty(TYPE, has_z, has_m); +} -inline LineString &Polygon::operator[](uint32_t index) { - return reinterpret_cast(data.part_data[index]); +inline Geometry MultiPoint::Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m) { + return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } -inline LineString *Polygon::begin() { - return reinterpret_cast(data.part_data); + +//------------------------------------------------------------------------------ +// MultiLineString +//------------------------------------------------------------------------------ +struct MultiLineString : public CollectionGeometry { + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + + + // Constants + static const constexpr GeometryType TYPE = GeometryType::MULTILINESTRING; + static const constexpr int SURFACE_DIMENSION = 1; +}; + +inline Geometry MultiLineString::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for(uint32_t i = 0; i < count; i++) { + // Placement new + new (&MultiLineString::Part(geom, i)) Geometry(GeometryType::LINESTRING, has_z, has_m); + } + return geom; } -inline LineString *Polygon::end() { - return reinterpret_cast(data.part_data + data_count); + +inline Geometry MultiLineString::CreateEmpty(bool has_z, bool has_m) { + return Geometry::CreateEmpty(TYPE, has_z, has_m); } -inline const LineString &Polygon::operator[](uint32_t index) const { - return reinterpret_cast(data.part_data[index]); +inline Geometry MultiLineString::Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m) { + return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } -inline const LineString *Polygon::begin() const { - return reinterpret_cast(data.part_data); + +//------------------------------------------------------------------------------ +// MultiPolygon +//------------------------------------------------------------------------------ +struct MultiPolygon : public CollectionGeometry { + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + + // Constants + static const constexpr GeometryType TYPE = GeometryType::MULTIPOLYGON; + static const constexpr int SURFACE_DIMENSION = 2; +}; + +inline Geometry MultiPolygon::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for(uint32_t i = 0; i < count; i++) { + // Placement new + new (&MultiPolygon::Part(geom, i)) Geometry(GeometryType::POLYGON, has_z, has_m); + } + return geom; } -inline const LineString *Polygon::end() const { - return reinterpret_cast(data.part_data + data_count); + +inline Geometry MultiPolygon::CreateEmpty(bool has_z, bool has_m) { + return Geometry::CreateEmpty(TYPE, has_z, has_m); +} + +inline Geometry MultiPolygon::Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m) { + return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } -//----------------- -// Collection -//----------------- +//------------------------------------------------------------------------------ +// GeometryCollection +//------------------------------------------------------------------------------ +struct GeometryCollection : public CollectionGeometry { + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + + // Constants + static const constexpr GeometryType TYPE = GeometryType::GEOMETRYCOLLECTION; +}; -template -inline T &TypedCollectionGeometry::operator[](uint32_t index) { - return reinterpret_cast(data.part_data[index]); +inline Geometry GeometryCollection::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for(uint32_t i = 0; i < count; i++) { + // Placement new + new (&GeometryCollection::Part(geom, i)) Geometry(GeometryType::GEOMETRYCOLLECTION, has_z, has_m); + } + return geom; } -template -inline T *TypedCollectionGeometry::begin() { - return reinterpret_cast(data.part_data); + +inline Geometry GeometryCollection::CreateEmpty(bool has_z, bool has_m) { + return Geometry::CreateEmpty(TYPE, has_z, has_m); } -template -inline T *TypedCollectionGeometry::end() { - return reinterpret_cast(data.part_data + data_count); + +inline Geometry GeometryCollection::Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m) { + return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } -template -inline const T &TypedCollectionGeometry::operator[](uint32_t index) const { - return reinterpret_cast(data.part_data[index]); +//------------------------------------------------------------------------------ +// Inlined Functions +//------------------------------------------------------------------------------ +template +inline void Geometry::ExtractPoints(const Geometry &geom, FUNC &&func) { + struct op { + static void Case(Geometry::Tags::Point, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiPoint, const Geometry &geom, FUNC &&func) { + auto parts = MultiPoint::Parts(geom); + for(auto &part : parts) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + auto parts = GeometryCollection::Parts(geom); + for(auto &part : parts) { + Visit(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } + }; + Visit(geom, std::forward(func)); } -template -inline const T *TypedCollectionGeometry::begin() const { - return reinterpret_cast(data.part_data); +template +inline void Geometry::ExtractLines(const Geometry &geom, FUNC &&func) { + struct op { + static void Case(Geometry::Tags::LineString, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiLineString, const Geometry &geom, FUNC &&func) { + auto parts = MultiLineString::Parts(geom); + for(auto &part : parts) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + auto parts = GeometryCollection::Parts(geom); + for(auto &part : parts) { + Visit(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } + }; + Visit(geom, std::forward(func)); } -template -inline const T *TypedCollectionGeometry::end() const { - return reinterpret_cast(data.part_data + data_count); +template +inline void Geometry::ExtractPolygons(const Geometry &geom, FUNC &&func){ + struct op { + static void Case(Geometry::Tags::Polygon, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiPolygon, const Geometry &geom, FUNC &&func) { + auto parts = MultiPolygon::Parts(geom); + for(auto &part : parts) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + auto parts = GeometryCollection::Parts(geom); + for(auto &part : parts) { + Visit(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } + }; + Visit(geom, std::forward(func)); } //------------------------------------------------------------------------------ @@ -888,57 +922,6 @@ inline const T *TypedCollectionGeometry::end() const { //------------------------------------------------------------------------------ static_assert(std::is_standard_layout::value, "Geometry must be standard layout"); -static_assert(std::is_standard_layout::value, "BaseGeometry must be standard layout"); -static_assert(std::is_standard_layout::value, "SinglePartBase must be standard layout"); -static_assert(std::is_standard_layout::value, "Point must be standard layout"); -static_assert(std::is_standard_layout::value, "Point must be standard layout"); -static_assert(std::is_standard_layout::value, "Polygon must be standard layout"); -static_assert(std::is_standard_layout::value, "LineString must be standard layout"); -static_assert(std::is_standard_layout::value, "MultiPolygon must be standard layout"); -static_assert(std::is_standard_layout::value, "MultiLineString must be standard layout"); -static_assert(std::is_standard_layout::value, "MultiPoint must be standard layout"); -static_assert(std::is_standard_layout::value, "GeometryCollection must be standard layout"); - -//------------------------------------------------------------------------------ -// Utils -//------------------------------------------------------------------------------ - -struct Utils { - static string format_coord(double d); - static string format_coord(double x, double y); - static string format_coord(double x, double y, double z); - static string format_coord(double x, double y, double z, double m); - - static inline float DoubleToFloatDown(double d) { - if (d > static_cast(std::numeric_limits::max())) { - return std::numeric_limits::max(); - } - if (d <= static_cast(std::numeric_limits::lowest())) { - return std::numeric_limits::lowest(); - } - - auto f = static_cast(d); - if (static_cast(f) <= d) { - return f; - } - return std::nextafter(f, std::numeric_limits::lowest()); - } - - static inline float DoubleToFloatUp(double d) { - if (d >= static_cast(std::numeric_limits::max())) { - return std::numeric_limits::max(); - } - if (d < static_cast(std::numeric_limits::lowest())) { - return std::numeric_limits::lowest(); - } - - auto f = static_cast(d); - if (static_cast(f) >= d) { - return f; - } - return std::nextafter(f, std::numeric_limits::max()); - } -}; } // namespace core diff --git a/spatial/include/spatial/core/geometry/geometry_processor.hpp b/spatial/include/spatial/core/geometry/geometry_processor.hpp index ffd5cf28..3567bcae 100644 --- a/spatial/include/spatial/core/geometry/geometry_processor.hpp +++ b/spatial/include/spatial/core/geometry/geometry_processor.hpp @@ -2,7 +2,7 @@ #include "spatial/common.hpp" #include "spatial/core/geometry/geometry.hpp" -#include "spatial/core/geometry/cursor.hpp" +#include "spatial/core/util/cursor.hpp" #include "spatial/core/geometry/geometry_type.hpp" namespace spatial { diff --git a/spatial/include/spatial/core/geometry/geometry_type.hpp b/spatial/include/spatial/core/geometry/geometry_type.hpp index 0daaf377..2e58648e 100644 --- a/spatial/include/spatial/core/geometry/geometry_type.hpp +++ b/spatial/include/spatial/core/geometry/geometry_type.hpp @@ -2,7 +2,7 @@ #include "spatial/common.hpp" #include "spatial/core/geometry/bbox.hpp" #include "spatial/core/geometry/geometry_properties.hpp" -#include "spatial/core/geometry/cursor.hpp" +#include "spatial/core/util/cursor.hpp" namespace spatial { @@ -18,6 +18,44 @@ enum class GeometryType : uint8_t { GEOMETRYCOLLECTION }; +struct GeometryTypes { + static bool IsSinglePart(GeometryType type) { + return type == GeometryType::POINT || type == GeometryType::LINESTRING; + } + + static bool IsMultiPart(GeometryType type) { + return type == GeometryType::POLYGON || + type == GeometryType::MULTIPOINT || type == GeometryType::MULTILINESTRING || + type == GeometryType::MULTIPOLYGON || type == GeometryType::GEOMETRYCOLLECTION; + } + + static bool IsCollection(GeometryType type) { + return type == GeometryType::MULTIPOINT || type == GeometryType::MULTILINESTRING || + type == GeometryType::MULTIPOLYGON || type == GeometryType::GEOMETRYCOLLECTION; + } + + static string ToString(GeometryType type) { + switch (type) { + case GeometryType::POINT: + return "POINT"; + case GeometryType::LINESTRING: + return "LINESTRING"; + case GeometryType::POLYGON: + return "POLYGON"; + case GeometryType::MULTIPOINT: + return "MULTIPOINT"; + case GeometryType::MULTILINESTRING: + return "MULTILINESTRING"; + case GeometryType::MULTIPOLYGON: + return "MULTIPOLYGON"; + case GeometryType::GEOMETRYCOLLECTION: + return "GEOMETRYCOLLECTION"; + default: + return StringUtil::Format("UNKNOWN(%d)", static_cast(type)); + } + } +}; + enum class SerializedGeometryType : uint32_t { POINT = 0, LINESTRING, diff --git a/spatial/include/spatial/core/geometry/geometry_writer.hpp b/spatial/include/spatial/core/geometry/geometry_writer.hpp index da333009..2cde435d 100644 --- a/spatial/include/spatial/core/geometry/geometry_writer.hpp +++ b/spatial/include/spatial/core/geometry/geometry_writer.hpp @@ -2,7 +2,8 @@ #include "spatial/common.hpp" #include "spatial/core/geometry/geometry.hpp" -#include "spatial/core/geometry/cursor.hpp" +#include "spatial/core/util/cursor.hpp" +#include "spatial/core/util/math.hpp" #include "spatial/core/geometry/geometry_type.hpp" #include "spatial/core/geometry/geometry_processor.hpp" namespace spatial { @@ -150,10 +151,10 @@ class GeometryWriter { if (properties.HasBBox()) { // Write the bbox after the first 8 bytes - buffer.WriteOffset(Utils::DoubleToFloatDown(stats.min_x), 8); - buffer.WriteOffset(Utils::DoubleToFloatDown(stats.min_y), 12); - buffer.WriteOffset(Utils::DoubleToFloatUp(stats.max_x), 16); - buffer.WriteOffset(Utils::DoubleToFloatUp(stats.max_y), 20); + buffer.WriteOffset(MathUtil::DoubleToFloatDown(stats.min_x), 8); + buffer.WriteOffset(MathUtil::DoubleToFloatDown(stats.min_y), 12); + buffer.WriteOffset(MathUtil::DoubleToFloatUp(stats.max_x), 16); + buffer.WriteOffset(MathUtil::DoubleToFloatUp(stats.max_y), 20); string_t blob = string_t {const_char_ptr_cast(buffer.GetPtr()), buffer.Size()}; return blob; } else { diff --git a/spatial/include/spatial/core/geometry/vertex_processor.hpp b/spatial/include/spatial/core/geometry/vertex_processor.hpp index 842db895..4d154cd8 100644 --- a/spatial/include/spatial/core/geometry/vertex_processor.hpp +++ b/spatial/include/spatial/core/geometry/vertex_processor.hpp @@ -2,7 +2,7 @@ #include "spatial/common.hpp" #include "spatial/core/geometry/geometry.hpp" -#include "spatial/core/geometry/cursor.hpp" +#include "spatial/core/util/cursor.hpp" #include "spatial/core/geometry/geometry_type.hpp" namespace spatial { diff --git a/spatial/include/spatial/core/geometry/wkb_reader.hpp b/spatial/include/spatial/core/geometry/wkb_reader.hpp index 34baec11..3f41613d 100644 --- a/spatial/include/spatial/core/geometry/wkb_reader.hpp +++ b/spatial/include/spatial/core/geometry/wkb_reader.hpp @@ -22,16 +22,16 @@ class WKBReader { uint32_t ReadInt(Cursor &cursor, bool little_endian); double ReadDouble(Cursor &cursor, bool little_endian); WKBType ReadType(Cursor &cursor, bool little_endian); - void ReadVertices(Cursor &cursor, bool little_endian, bool has_z, bool has_m, SinglePartGeometry &geometry); + void ReadVertices(Cursor &cursor, bool little_endian, bool has_z, bool has_m, Geometry &geometry); // Geometries - Point ReadPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - LineString ReadLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - Polygon ReadPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - MultiPoint ReadMultiPoint(Cursor &cursor, bool little_endian); - MultiLineString ReadMultiLineString(Cursor &cursor, bool little_endian); - MultiPolygon ReadMultiPolygon(Cursor &cursor, bool little_endian); - GeometryCollection ReadGeometryCollection(Cursor &cursor, bool little_endian); + Geometry ReadPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadMultiPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadMultiLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadMultiPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadGeometryCollection(Cursor &cursor, bool little_endian, bool has_z, bool has_m); Geometry ReadGeometry(Cursor &cursor); public: diff --git a/spatial/include/spatial/core/geometry/wkt_reader.hpp b/spatial/include/spatial/core/geometry/wkt_reader.hpp index e545fb89..75ac14bb 100644 --- a/spatial/include/spatial/core/geometry/wkt_reader.hpp +++ b/spatial/include/spatial/core/geometry/wkt_reader.hpp @@ -25,13 +25,14 @@ class WKTReader { void Expect(char c); void ParseVertex(vector &coords); pair> ParseVertices(); - Point ParsePoint(); - LineString ParseLineString(); - Polygon ParsePolygon(); - MultiPoint ParseMultiPoint(); - MultiLineString ParseMultiLineString(); - MultiPolygon ParseMultiPolygon(); - GeometryCollection ParseGeometryCollection(); + + Geometry ParsePoint(); + Geometry ParseLineString(); + Geometry ParsePolygon(); + Geometry ParseMultiPoint(); + Geometry ParseMultiLineString(); + Geometry ParseMultiPolygon(); + Geometry ParseGeometryCollection(); void CheckZM(); Geometry ParseGeometry(); Geometry ParseWKT(); diff --git a/spatial/include/spatial/core/module.hpp b/spatial/include/spatial/core/module.hpp index 941e37a6..c6b69790 100644 --- a/spatial/include/spatial/core/module.hpp +++ b/spatial/include/spatial/core/module.hpp @@ -1,6 +1,5 @@ #pragma once #include "spatial/common.hpp" -#include "spatial/core/geometry/cursor.hpp" #include "spatial/core/geometry/geometry.hpp" namespace spatial { diff --git a/spatial/include/spatial/core/geometry/cursor.hpp b/spatial/include/spatial/core/util/cursor.hpp similarity index 100% rename from spatial/include/spatial/core/geometry/cursor.hpp rename to spatial/include/spatial/core/util/cursor.hpp diff --git a/spatial/include/spatial/core/util/math.hpp b/spatial/include/spatial/core/util/math.hpp new file mode 100644 index 00000000..4743d878 --- /dev/null +++ b/spatial/include/spatial/core/util/math.hpp @@ -0,0 +1,46 @@ +#include "spatial/common.hpp" + +namespace spatial { + +namespace core { + +struct MathUtil { + static string format_coord(double d); + static string format_coord(double x, double y); + static string format_coord(double x, double y, double z); + static string format_coord(double x, double y, double z, double m); + + static inline float DoubleToFloatDown(double d) { + if (d > static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + if (d <= static_cast(std::numeric_limits::lowest())) { + return std::numeric_limits::lowest(); + } + + auto f = static_cast(d); + if (static_cast(f) <= d) { + return f; + } + return std::nextafter(f, std::numeric_limits::lowest()); + } + + static inline float DoubleToFloatUp(double d) { + if (d >= static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + if (d < static_cast(std::numeric_limits::lowest())) { + return std::numeric_limits::lowest(); + } + + auto f = static_cast(d); + if (static_cast(f) >= d) { + return f; + } + return std::nextafter(f, std::numeric_limits::max()); + } +}; + +} // namespace core + +} // namespace spatial \ No newline at end of file diff --git a/spatial/include/spatial/core/util/misaligned_ptr.hpp b/spatial/include/spatial/core/util/misaligned_ptr.hpp new file mode 100644 index 00000000..1cf2f824 --- /dev/null +++ b/spatial/include/spatial/core/util/misaligned_ptr.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include "spatial/common.hpp" +#include "spatial/core/util/misaligned_ref.hpp" + +namespace spatial { + +namespace core { + + +template +class MisalignedPtrBase { +private: + PTR ptr_; +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = TYPE; + using difference_type = std::ptrdiff_t; + + reference operator*() const noexcept { return {ptr_}; } + reference operator[](std::size_t i) noexcept { return reference(ptr_ + i * sizeof(TYPE)); } + value_type operator[](std::size_t i) const noexcept { return Load(ptr_ + i * sizeof(TYPE)); } + + MisalignedPtrBase(PTR ptr) noexcept : ptr_(ptr) {} + MisalignedPtrBase operator++(int) noexcept { return MisalignedPtr(ptr_ + sizeof(TYPE)); } + MisalignedPtrBase operator--(int) noexcept { return MisalignedPtr(ptr_ - sizeof(TYPE)); } + MisalignedPtrBase operator+(difference_type d) noexcept { return MisalignedPtr(ptr_ + d * sizeof(TYPE)); } + MisalignedPtrBase operator-(difference_type d) noexcept { return MisalignedPtr(ptr_ - d * sizeof(TYPE)); } + + MisalignedPtrBase& operator++() noexcept { ptr_ += sizeof(TYPE); return *this; } + MisalignedPtrBase& operator--() noexcept { ptr_ -= sizeof(TYPE); return *this; } + MisalignedPtrBase& operator+=(difference_type d) noexcept { ptr_ += d * sizeof(TYPE); return *this; } + MisalignedPtrBase& operator-=(difference_type d) noexcept { ptr_ -= d * sizeof(TYPE); return *this; } + + bool operator==(MisalignedPtrBase const& other) noexcept { return ptr_ == other.ptr_; } + bool operator!=(MisalignedPtrBase const& other) noexcept { return ptr_ != other.ptr_; } +}; + +template +class MisalignedPtr : public MisalignedPtrBase, MisalignedRef> { }; + +template +class ConstMisalignedPtr : public MisalignedPtrBase, ConstMisalignedRef> { }; + + +template +class StridedPtrBase { +private: + PTR ptr; + size_t stride; +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = TYPE; + using difference_type = std::ptrdiff_t; + + reference operator*() const noexcept { return {ptr}; } + reference operator[](std::size_t i) noexcept { return reference(ptr + i * stride); } + value_type operator[](std::size_t i) const noexcept { return Load(ptr + i * stride); } + + StridedPtrBase(PTR ptr, size_t stride) noexcept : ptr(ptr), stride(stride) {} + StridedPtrBase operator++(int) noexcept { return StridedPtrBase(ptr + stride, stride); } + StridedPtrBase operator--(int) noexcept { return StridedPtrBase(ptr - stride, stride); } + StridedPtrBase operator+(difference_type d) noexcept { return StridedPtrBase(ptr + d * stride, stride); } + StridedPtrBase operator-(difference_type d) noexcept { return StridedPtrBase(ptr - d * stride, stride); } + + StridedPtrBase& operator++() noexcept { ptr += stride; return *this; } + StridedPtrBase& operator--() noexcept { ptr -= stride; return *this; } + StridedPtrBase& operator+=(difference_type d) noexcept { ptr += d * stride; return *this; } + StridedPtrBase& operator-=(difference_type d) noexcept { ptr -= d * stride; return *this; } + + bool operator==(StridedPtrBase const& other) noexcept { return ptr == other.ptr; } + bool operator!=(StridedPtrBase const& other) noexcept { return ptr != other.ptr; } +}; + +template +class StridedPtr : public StridedPtrBase, MisalignedRef> { +public: + StridedPtr(data_ptr_t ptr, size_t stride) noexcept + : StridedPtrBase, MisalignedRef>(ptr, stride) {} +}; + +template +class ConstStridedPtr : public StridedPtrBase, ConstMisalignedRef> { + public: + ConstStridedPtr(const_data_ptr_t ptr, size_t stride) noexcept + : StridedPtrBase, ConstMisalignedRef>(ptr, stride) {} +}; + + +} // namespace core + +} // namespace spatial \ No newline at end of file diff --git a/spatial/include/spatial/core/util/misaligned_ref.hpp b/spatial/include/spatial/core/util/misaligned_ref.hpp new file mode 100644 index 00000000..611c83e7 --- /dev/null +++ b/spatial/include/spatial/core/util/misaligned_ref.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "spatial/common.hpp" + +namespace spatial { + +namespace core { + +template +class MisalignedRef { + using TYPE = typename std::remove_const::type; + data_ptr_t ptr_; + +public: + MisalignedRef(data_ptr_t ptr) noexcept : ptr_(ptr) {} + + operator TYPE() const noexcept { return Load(ptr_); } + MisalignedRef& operator=(TYPE const& v) noexcept { + Store(v, ptr_); + return *this; + } + void reset(data_ptr_t ptr) noexcept { ptr_ = ptr; } + data_ptr_t ptr() const noexcept { return ptr_; } +}; + + +template +class ConstMisalignedRef { + using TYPE = typename std::remove_const::type; + const_data_ptr_t ptr_; +public: + ConstMisalignedRef(const_data_ptr_t ptr) noexcept : ptr_(ptr) {} + operator TYPE() const noexcept { return Load(ptr_); } + const_data_ptr_t ptr() const noexcept { return ptr_; } +}; + + +} // namespace core + +} // namespace spatial \ No newline at end of file diff --git a/spatial/src/spatial/core/CMakeLists.txt b/spatial/src/spatial/core/CMakeLists.txt index a97c59c9..a817dc0d 100644 --- a/spatial/src/spatial/core/CMakeLists.txt +++ b/spatial/src/spatial/core/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(geometry) add_subdirectory(functions) add_subdirectory(io) +add_subdirectory(util) set(EXTENSION_SOURCES ${EXTENSION_SOURCES} diff --git a/spatial/src/spatial/core/functions/aggregate/st_envelope_agg.cpp b/spatial/src/spatial/core/functions/aggregate/st_envelope_agg.cpp index 40dab936..e6a58c7d 100644 --- a/spatial/src/spatial/core/functions/aggregate/st_envelope_agg.cpp +++ b/spatial/src/spatial/core/functions/aggregate/st_envelope_agg.cpp @@ -75,8 +75,8 @@ struct EnvelopeAggFunction { finalize_data.ReturnNull(); } else { auto &arena = finalize_data.input.allocator; - auto box = Polygon::FromBox(arena, state.xmin, state.ymin, state.xmax, state.ymax); - target = Geometry(box).Serialize(finalize_data.result); + auto box = Polygon::CreateFromBox(arena, state.xmin, state.ymin, state.xmax, state.ymax); + target = Geometry::Serialize(box, finalize_data.result); } } diff --git a/spatial/src/spatial/core/functions/cast/geometry_cast.cpp b/spatial/src/spatial/core/functions/cast/geometry_cast.cpp index 67d4174e..44b911d2 100644 --- a/spatial/src/spatial/core/functions/cast/geometry_cast.cpp +++ b/spatial/src/spatial/core/functions/cast/geometry_cast.cpp @@ -22,8 +22,8 @@ static bool Point2DToGeometryCast(Vector &source, Vector &result, idx_t count, C auto &arena = lstate.arena; GenericExecutor::ExecuteUnary(source, result, count, [&](POINT_TYPE &point) { - auto geom = Point::FromVertex(arena, VertexXY {point.a_val, point.b_val}); - return Geometry(geom).Serialize(result); + auto geom = Point::CreateFromVertex(arena, VertexXY {point.a_val, point.b_val}); + return Geometry::Serialize(geom, result); }); return true; } @@ -43,12 +43,11 @@ static bool GeometryToPoint2DCast(Vector &source, Vector &result, idx_t count, C if (geom.GetType() != GeometryType::POINT) { throw ConversionException("Cannot cast non-point GEOMETRY to POINT_2D"); } - auto &point = geom.As(); - if (point.IsEmpty()) { + if (Point::IsEmpty(geom)) { // TODO: Maybe make this return NULL instead throw ConversionException("Cannot cast empty point GEOMETRY to POINT_2D"); } - auto vertex = point.Get(0); + auto vertex = Point::GetVertex(geom); return POINT_TYPE {vertex.x, vertex.y}; }); return true; @@ -72,9 +71,9 @@ static bool LineString2DToGeometryCast(Vector &source, Vector &result, idx_t cou for (idx_t i = 0; i < line.length; i++) { auto x = x_data[line.offset + i]; auto y = y_data[line.offset + i]; - geom.SetExact(i, VertexXY {x, y}); + LineString::SetVertex(geom, i, VertexXY {x, y}); } - return Geometry(geom).Serialize(result); + return Geometry::Serialize(geom, result); }); return true; } @@ -97,15 +96,15 @@ static bool GeometryToLineString2DCast(Vector &source, Vector &result, idx_t cou throw ConversionException("Cannot cast non-linestring GEOMETRY to LINESTRING_2D"); } - auto line = Geometry::Deserialize(arena, geom).As(); - auto line_size = line.Count(); + auto line = Geometry::Deserialize(arena, geom); + auto line_size = LineString::VertexCount(line); auto entry = list_entry_t(total_coords, line_size); total_coords += line_size; ListVector::Reserve(result, total_coords); for (idx_t i = 0; i < line_size; i++) { - auto vertex = line.Get(i); + auto vertex = LineString::GetVertex(line, i); x_data[entry.offset + i] = vertex.x; y_data[entry.offset + i] = vertex.y; } @@ -134,15 +133,15 @@ static bool Polygon2DToGeometryCast(Vector &source, Vector &result, idx_t count, for (idx_t i = 0; i < poly.length; i++) { auto ring = ring_entries[poly.offset + i]; - auto &ring_array = geom[i]; - ring_array.Resize(arena, ring.length); + auto &ring_array = Polygon::Part(geom, i); + LineString::Resize(ring_array, arena, ring.length); for (idx_t j = 0; j < ring.length; j++) { auto x = x_data[ring.offset + j]; auto y = y_data[ring.offset + j]; - ring_array.SetExact(j, VertexXY {x, y}); + LineString::SetVertex(ring_array, j, VertexXY {x, y}); } } - return Geometry(geom).Serialize(result); + return Geometry::Serialize(geom, result); }); return true; } @@ -163,14 +162,14 @@ static bool GeometryToPolygon2DCast(Vector &source, Vector &result, idx_t count, if (geom.GetType() != GeometryType::POLYGON) { throw ConversionException("Cannot cast non-polygon GEOMETRY to POLYGON_2D"); } - auto poly = Geometry::Deserialize(arena, geom).As(); - auto poly_size = poly.Count(); + auto poly = Geometry::Deserialize(arena, geom); + auto poly_size = Polygon::PartCount(poly); auto poly_entry = list_entry_t(total_rings, poly_size); ListVector::Reserve(result, total_rings + poly_size); for (idx_t ring_idx = 0; ring_idx < poly_size; ring_idx++) { - auto ring = poly[ring_idx]; + auto ring = Polygon::Part(poly, ring_idx); auto ring_size = ring.Count(); auto ring_entry = list_entry_t(total_coords, ring_size); @@ -185,7 +184,7 @@ static bool GeometryToPolygon2DCast(Vector &source, Vector &result, idx_t count, ring_entries[total_rings + ring_idx] = ring_entry; for (idx_t j = 0; j < ring_size; j++) { - auto vert = ring.Get(j); + auto vert = LineString::GetVertex(ring, j); x_data[ring_entry.offset + j] = vert.x; y_data[ring_entry.offset + j] = vert.y; } @@ -216,8 +215,8 @@ static bool Box2DToGeometryCast(Vector &source, Vector &result, idx_t count, Cas auto miny = box.b_val; auto maxx = box.c_val; auto maxy = box.d_val; - auto polygon = Polygon::FromBox(arena, minx, miny, maxx, maxy); - return Geometry(polygon).Serialize(result); + auto polygon = Polygon::CreateFromBox(arena, minx, miny, maxx, maxy); + return Geometry::Serialize(polygon, result); }); return true; } diff --git a/spatial/src/spatial/core/functions/cast/varchar_cast.cpp b/spatial/src/spatial/core/functions/cast/varchar_cast.cpp index 0297eb58..a341f9ee 100644 --- a/spatial/src/spatial/core/functions/cast/varchar_cast.cpp +++ b/spatial/src/spatial/core/functions/cast/varchar_cast.cpp @@ -5,6 +5,7 @@ #include "spatial/core/functions/common.hpp" #include "spatial/core/geometry/geometry_processor.hpp" #include "spatial/core/geometry/wkt_reader.hpp" +#include "spatial/core/util/math.hpp" #include "duckdb/function/cast/cast_function_set.hpp" #include "duckdb/common/operator/cast_operators.hpp" #include "duckdb/common/vector_operations/generic_executor.hpp" @@ -29,7 +30,7 @@ void CoreVectorOperations::Point2DToVarchar(Vector &source, Vector &result, idx_ return StringVector::AddString(result, "POINT EMPTY"); } - return StringVector::AddString(result, StringUtil::Format("POINT (%s)", Utils::format_coord(x, y))); + return StringVector::AddString(result, StringUtil::Format("POINT (%s)", MathUtil::format_coord(x, y))); }); } @@ -52,7 +53,7 @@ void CoreVectorOperations::LineString2DToVarchar(Vector &source, Vector &result, string result_str = "LINESTRING ("; for (idx_t i = offset; i < offset + length; i++) { - result_str += Utils::format_coord(x_data[i], y_data[i]); + result_str += MathUtil::format_coord(x_data[i], y_data[i]); if (i < offset + length - 1) { result_str += ", "; } @@ -89,7 +90,7 @@ void CoreVectorOperations::Polygon2DToVarchar(Vector &source, Vector &result, id auto ring_length = ring_entry.length; result_str += "("; for (idx_t j = ring_offset; j < ring_offset + ring_length; j++) { - result_str += Utils::format_coord(x_data[j], y_data[j]); + result_str += MathUtil::format_coord(x_data[j], y_data[j]); if (j < ring_offset + ring_length - 1) { result_str += ", "; } @@ -112,8 +113,8 @@ void CoreVectorOperations::Box2DToVarchar(Vector &source, Vector &result, idx_t using VARCHAR_TYPE = PrimitiveType; GenericExecutor::ExecuteUnary(source, result, count, [&](BOX_TYPE &box) { return StringVector::AddString(result, - StringUtil::Format("BOX(%s, %s)", Utils::format_coord(box.a_val, box.b_val), - Utils::format_coord(box.c_val, box.d_val))); + StringUtil::Format("BOX(%s, %s)", MathUtil::format_coord(box.a_val, box.b_val), + MathUtil::format_coord(box.c_val, box.d_val))); }); } @@ -136,7 +137,7 @@ class GeometryTextProcessor final : GeometryProcessor { auto y = Load(dims[1] + i * strides[1]); auto z = Load(dims[2] + i * strides[2]); auto m = Load(dims[3] + i * strides[3]); - text += Utils::format_coord(x, y, z, m); + text += MathUtil::format_coord(x, y, z, m); if (i < count - 1) { text += ", "; } @@ -146,7 +147,7 @@ class GeometryTextProcessor final : GeometryProcessor { auto x = Load(dims[0] + i * strides[0]); auto y = Load(dims[1] + i * strides[1]); auto zm = Load(dims[2] + i * strides[2]); - text += Utils::format_coord(x, y, zm); + text += MathUtil::format_coord(x, y, zm); if (i < count - 1) { text += ", "; } @@ -156,7 +157,7 @@ class GeometryTextProcessor final : GeometryProcessor { auto x = Load(dims[0] + i * strides[0]); auto y = Load(dims[1] + i * strides[1]); auto m = Load(dims[3] + i * strides[3]); - text += Utils::format_coord(x, y, m); + text += MathUtil::format_coord(x, y, m); if (i < count - 1) { text += ", "; } @@ -165,7 +166,7 @@ class GeometryTextProcessor final : GeometryProcessor { for (uint32_t i = 0; i < count; i++) { auto x = Load(dims[0] + i * strides[0]); auto y = Load(dims[1] + i * strides[1]); - text += Utils::format_coord(x, y); + text += MathUtil::format_coord(x, y); if (i < count - 1) { text += ", "; @@ -323,7 +324,8 @@ static bool TextToGeometryCast(Vector &source, Vector &result, idx_t count, Cast UnaryExecutor::ExecuteWithNulls( source, result, count, [&](string_t &wkt, ValidityMask &mask, idx_t idx) { try { - return reader.Parse(wkt).Serialize(result); + auto geom = reader.Parse(wkt); + return Geometry::Serialize(geom, result); } catch (InvalidInputException &e) { if (success) { success = false; diff --git a/spatial/src/spatial/core/functions/cast/wkb_cast.cpp b/spatial/src/spatial/core/functions/cast/wkb_cast.cpp index 1da45e20..1d2ed1f1 100644 --- a/spatial/src/spatial/core/functions/cast/wkb_cast.cpp +++ b/spatial/src/spatial/core/functions/cast/wkb_cast.cpp @@ -25,7 +25,8 @@ static bool WKBToGeometryCast(Vector &source, Vector &result, idx_t count, CastP UnaryExecutor::ExecuteWithNulls( source, result, count, [&](string_t input, ValidityMask &mask, idx_t idx) { try { - return reader.Deserialize(input).Serialize(result); + auto geom = reader.Deserialize(input); + return Geometry::Serialize(geom, result); } catch (SerializationException &e) { if (success) { success = false; diff --git a/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp b/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp index ee65ee81..d01b4684 100644 --- a/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp @@ -58,7 +58,7 @@ class JSONAllocator { // GEOMETRY -> GEOJSON Fragment //------------------------------------------------------------------------------ -static void VerticesToGeoJSON(const SinglePartGeometry &vertices, yyjson_mut_doc *doc, yyjson_mut_val *arr) { +static void VerticesToGeoJSON(const Geometry &vertices, yyjson_mut_doc *doc, yyjson_mut_val *arr) { // TODO: If the vertexvector is empty, do we null, add an empty array or a pair of NaN? auto haz_z = vertices.GetProperties().HasZ(); auto has_m = vertices.GetProperties().HasM(); @@ -66,7 +66,7 @@ static void VerticesToGeoJSON(const SinglePartGeometry &vertices, yyjson_mut_doc if (haz_z && has_m) { for (uint32_t i = 0; i < vertices.Count(); i++) { auto coord = yyjson_mut_arr(doc); - auto vert = vertices.GetExact(i); + auto vert = SinglePartGeometry::GetVertex(vertices, i); yyjson_mut_arr_add_real(doc, coord, vert.x); yyjson_mut_arr_add_real(doc, coord, vert.y); yyjson_mut_arr_add_real(doc, coord, vert.z); @@ -75,7 +75,7 @@ static void VerticesToGeoJSON(const SinglePartGeometry &vertices, yyjson_mut_doc } else if (haz_z) { for (uint32_t i = 0; i < vertices.Count(); i++) { auto coord = yyjson_mut_arr(doc); - auto vert = vertices.GetExact(i); + auto vert = SinglePartGeometry::GetVertex(vertices, i); yyjson_mut_arr_add_real(doc, coord, vert.x); yyjson_mut_arr_add_real(doc, coord, vert.y); yyjson_mut_arr_add_real(doc, coord, vert.z); @@ -84,7 +84,7 @@ static void VerticesToGeoJSON(const SinglePartGeometry &vertices, yyjson_mut_doc } else if (has_m) { for (uint32_t i = 0; i < vertices.Count(); i++) { auto coord = yyjson_mut_arr(doc); - auto vert = vertices.GetExact(i); + auto vert = SinglePartGeometry::GetVertex(vertices, i); yyjson_mut_arr_add_real(doc, coord, vert.x); yyjson_mut_arr_add_real(doc, coord, vert.y); yyjson_mut_arr_append(arr, coord); @@ -92,7 +92,7 @@ static void VerticesToGeoJSON(const SinglePartGeometry &vertices, yyjson_mut_doc } else { for (uint32_t i = 0; i < vertices.Count(); i++) { auto coord = yyjson_mut_arr(doc); - auto vert = vertices.GetExact(i); + auto vert = SinglePartGeometry::GetVertex(vertices, i); yyjson_mut_arr_add_real(doc, coord, vert.x); yyjson_mut_arr_add_real(doc, coord, vert.y); yyjson_mut_arr_append(arr, coord); @@ -103,30 +103,30 @@ static void VerticesToGeoJSON(const SinglePartGeometry &vertices, yyjson_mut_doc struct ToGeoJSONFunctor { // Point - static void Apply(const Point &point, yyjson_mut_doc *doc, yyjson_mut_val *obj) { + static void Case(Geometry::Tags::Point, const Geometry &point, yyjson_mut_doc *doc, yyjson_mut_val *obj) { yyjson_mut_obj_add_str(doc, obj, "type", "Point"); auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - if (!point.IsEmpty()) { + if (!Point::IsEmpty(point)) { auto has_z = point.GetProperties().HasZ(); auto has_m = point.GetProperties().HasM(); if (has_z && has_m) { - auto vert = point.GetExact(0); + auto vert = Point::GetVertex(point); yyjson_mut_arr_add_real(doc, coords, vert.x); yyjson_mut_arr_add_real(doc, coords, vert.y); yyjson_mut_arr_add_real(doc, coords, vert.z); } else if (has_z) { - auto vert = point.GetExact(0); + auto vert = Point::GetVertex(point); yyjson_mut_arr_add_real(doc, coords, vert.x); yyjson_mut_arr_add_real(doc, coords, vert.y); yyjson_mut_arr_add_real(doc, coords, vert.z); } else if (has_m) { - auto vert = point.GetExact(0); + auto vert = Point::GetVertex(point); yyjson_mut_arr_add_real(doc, coords, vert.x); yyjson_mut_arr_add_real(doc, coords, vert.y); } else { - auto vert = point.GetExact(0); + auto vert = Point::GetVertex(point); yyjson_mut_arr_add_real(doc, coords, vert.x); yyjson_mut_arr_add_real(doc, coords, vert.y); } @@ -134,7 +134,7 @@ struct ToGeoJSONFunctor { } // LineString - static void Apply(const LineString &line, yyjson_mut_doc *doc, yyjson_mut_val *obj) { + static void Case(Geometry::Tags::LineString, const Geometry &line, yyjson_mut_doc *doc, yyjson_mut_val *obj) { yyjson_mut_obj_add_str(doc, obj, "type", "LineString"); auto coords = yyjson_mut_arr(doc); @@ -143,73 +143,80 @@ struct ToGeoJSONFunctor { } // Polygon - static void Apply(const Polygon &poly, yyjson_mut_doc *doc, yyjson_mut_val *obj) { + static void Case(Geometry::Tags::Polygon, const Geometry &poly, yyjson_mut_doc *doc, yyjson_mut_val *obj) { yyjson_mut_obj_add_str(doc, obj, "type", "Polygon"); auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - for (const auto &ring : poly) { - auto ring_coords = yyjson_mut_arr(doc); - VerticesToGeoJSON(ring, doc, ring_coords); - yyjson_mut_arr_append(coords, ring_coords); - } + for(uint32_t i = 0; i < Polygon::PartCount(poly); i++) { + auto &ring = Polygon::Part(poly, i); + auto ring_coords = yyjson_mut_arr(doc); + VerticesToGeoJSON(ring, doc, ring_coords); + yyjson_mut_arr_append(coords, ring_coords); + } } // MultiPoint - static void Apply(const MultiPoint &mpoint, yyjson_mut_doc *doc, yyjson_mut_val *obj) { + static void Case(Geometry::Tags::MultiPoint, const Geometry &mpoint, yyjson_mut_doc *doc, yyjson_mut_val *obj) { yyjson_mut_obj_add_str(doc, obj, "type", "MultiPoint"); auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - for (const auto &point : mpoint) { - VerticesToGeoJSON(point, doc, coords); - } + for(uint32_t i = 0; i < MultiPoint::PartCount(mpoint); i++) { + auto &point = MultiPoint::Part(mpoint, i); + VerticesToGeoJSON(point, doc, coords); + } } // MultiLineString - static void Apply(const MultiLineString &mline, yyjson_mut_doc *doc, yyjson_mut_val *obj) { + static void Case(Geometry::Tags::MultiLineString, const Geometry &mline, yyjson_mut_doc *doc, yyjson_mut_val *obj) { yyjson_mut_obj_add_str(doc, obj, "type", "MultiLineString"); auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - for (const auto &line : mline) { - auto line_coords = yyjson_mut_arr(doc); - VerticesToGeoJSON(line, doc, line_coords); - yyjson_mut_arr_append(coords, line_coords); - } + for(uint32_t i = 0; i < MultiLineString::PartCount(mline); i++) { + auto &line = MultiLineString::Part(mline, i); + auto line_coords = yyjson_mut_arr(doc); + VerticesToGeoJSON(line, doc, line_coords); + yyjson_mut_arr_append(coords, line_coords); + } } // MultiPolygon - static void Apply(const MultiPolygon &mpoly, yyjson_mut_doc *doc, yyjson_mut_val *obj) { + static void Case(Geometry::Tags::MultiPolygon, const Geometry &mpoly, yyjson_mut_doc *doc, yyjson_mut_val *obj) { yyjson_mut_obj_add_str(doc, obj, "type", "MultiPolygon"); auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - for (const auto &poly : mpoly) { - auto poly_coords = yyjson_mut_arr(doc); - for (const auto &ring : poly) { - auto ring_coords = yyjson_mut_arr(doc); - VerticesToGeoJSON(ring, doc, ring_coords); - yyjson_mut_arr_append(poly_coords, ring_coords); - } - yyjson_mut_arr_append(coords, poly_coords); - } + + for(uint32_t i = 0; i < MultiPolygon::PartCount(mpoly); i++) { + auto &poly = MultiPolygon::Part(mpoly, i); + auto poly_coords = yyjson_mut_arr(doc); + for (uint32_t j = 0; j < Polygon::PartCount(poly); j++) { + auto &ring = Polygon::Part(poly, j); + auto ring_coords = yyjson_mut_arr(doc); + VerticesToGeoJSON(ring, doc, ring_coords); + yyjson_mut_arr_append(poly_coords, ring_coords); + } + yyjson_mut_arr_append(coords, poly_coords); + } } // GeometryCollection - static void Apply(const GeometryCollection &collection, yyjson_mut_doc *doc, yyjson_mut_val *obj) { + static void Case(Geometry::Tags::GeometryCollection, const Geometry &collection, yyjson_mut_doc *doc, yyjson_mut_val *obj) { yyjson_mut_obj_add_str(doc, obj, "type", "GeometryCollection"); auto arr = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "geometries", arr); - for (auto &geom : collection) { - auto geom_obj = yyjson_mut_obj(doc); - geom.Visit(doc, geom_obj); - yyjson_mut_arr_append(arr, geom_obj); - } - } + for(uint32_t i = 0; i < GeometryCollection::PartCount(collection); i++) { + auto &geom = GeometryCollection::Part(collection, i); + auto geom_obj = yyjson_mut_obj(doc); + Geometry::Visit(geom, doc, geom_obj); + yyjson_mut_arr_append(arr, geom_obj); + } + } }; static void GeometryToGeoJSONFragmentFunction(DataChunk &args, ExpressionState &state, Vector &result) { @@ -222,13 +229,13 @@ static void GeometryToGeoJSONFragmentFunction(DataChunk &args, ExpressionState & JSONAllocator json_allocator(lstate.arena); UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { - auto geometry = Geometry::Deserialize(lstate.arena, input); + auto geom = Geometry::Deserialize(lstate.arena, input); auto doc = yyjson_mut_doc_new(json_allocator.GetYYJSONAllocator()); auto obj = yyjson_mut_obj(doc); yyjson_mut_doc_set_root(doc, obj); - geometry.Visit(doc, obj); + Geometry::Visit(geom, doc, obj); size_t json_size = 0; // TODO: YYJSON_WRITE_PRETTY @@ -242,11 +249,11 @@ static void GeometryToGeoJSONFragmentFunction(DataChunk &args, ExpressionState & // GEOJSON Fragment -> GEOMETRY //------------------------------------------------------------------------------ -static Point PointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { +static Geometry PointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { auto len = yyjson_arr_size(coord_array); if (len == 0) { // empty point - return Point::Empty(has_z, false); + return Point::CreateEmpty(has_z, false); } if (len < 2) { throw InvalidInputException("GeoJSON input coordinates field is not an array of at least length 2: %s", @@ -273,18 +280,18 @@ static Point PointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, co raw.GetString()); } auto z = yyjson_get_num(z_val); - return Point::FromVertex(arena, VertexXYZ {x, y, z}); + return Point::CreateFromVertex(arena, VertexXYZ {x, y, z}); } else { - return Point::FromVertex(arena, VertexXY {x, y}); + return Point::CreateFromVertex(arena, VertexXY {x, y}); } } -static LineString VerticesFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, +static Geometry VerticesFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { auto len = yyjson_arr_size(coord_array); if (len == 0) { // Empty - return LineString::Empty(false, false); + return LineString::CreateEmpty(false, false); } else { // Sniff the coordinates to see if we have Z bool has_any_z = false; @@ -335,25 +342,25 @@ static LineString VerticesFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &a z = yyjson_get_num(z_val); } if (has_any_z) { - vertices.SetExact(idx, VertexXYZ {x, y, z}); + LineString::SetVertex(vertices, idx, {x, y, z}); } else { - vertices.SetExact(idx, VertexXY {x, y}); + LineString::SetVertex(vertices, idx, {x, y }); } } return vertices; } } -static LineString LineStringFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, +static Geometry LineStringFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { return VerticesFromGeoJSON(coord_array, arena, raw, has_z); } -static Polygon PolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { +static Geometry PolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { auto num_rings = yyjson_arr_size(coord_array); if (num_rings == 0) { // Empty - return Polygon::Empty(has_z, false); + return Polygon::CreateEmpty(has_z, false); } else { // Polygon auto polygon = Polygon::Create(arena, num_rings, has_z, false); @@ -364,19 +371,19 @@ static Polygon PolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena throw InvalidInputException("GeoJSON input coordinates field is not an array of arrays: %s", raw.GetString()); } - polygon[idx] = VerticesFromGeoJSON(ring_val, arena, raw, has_z); + Polygon::Part(polygon, idx) = VerticesFromGeoJSON(ring_val, arena, raw, has_z); } return polygon; } } -static MultiPoint MultiPointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, +static Geometry MultiPointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { auto num_points = yyjson_arr_size(coord_array); if (num_points == 0) { // Empty - return MultiPoint::Empty(has_z, false); + return MultiPoint::CreateEmpty(has_z, false); } else { // MultiPoint auto multi_point = MultiPoint::Create(arena, num_points, has_z, false); @@ -391,18 +398,18 @@ static MultiPoint MultiPointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator throw InvalidInputException( "GeoJSON input coordinates field is not an array of arrays of length >= 2: %s", raw.GetString()); } - multi_point[idx] = PointFromGeoJSON(point_val, arena, raw, has_z); + MultiPoint::Part(multi_point, idx) = PointFromGeoJSON(point_val, arena, raw, has_z); } return multi_point; } } -static MultiLineString MultiLineStringFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, +static Geometry MultiLineStringFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { auto num_linestrings = yyjson_arr_size(coord_array); if (num_linestrings == 0) { // Empty - return MultiLineString::Empty(has_z, false); + return MultiLineString::CreateEmpty(has_z, false); } else { // MultiLineString auto multi_linestring = MultiLineString::Create(arena, num_linestrings, has_z, false); @@ -413,19 +420,19 @@ static MultiLineString MultiLineStringFromGeoJSON(yyjson_val *coord_array, Arena throw InvalidInputException("GeoJSON input coordinates field is not an array of arrays: %s", raw.GetString()); } - multi_linestring[idx] = LineStringFromGeoJSON(linestring_val, arena, raw, has_z); + MultiLineString::Part(multi_linestring, idx) = LineStringFromGeoJSON(linestring_val, arena, raw, has_z); } return multi_linestring; } } -static MultiPolygon MultiPolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, +static Geometry MultiPolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { auto num_polygons = yyjson_arr_size(coord_array); if (num_polygons == 0) { // Empty - return MultiPolygon::Empty(has_z, false); + return MultiPolygon::CreateEmpty(has_z, false); } else { // MultiPolygon auto multi_polygon = MultiPolygon::Create(arena, num_polygons, has_z, false); @@ -436,7 +443,7 @@ static MultiPolygon MultiPolygonFromGeoJSON(yyjson_val *coord_array, ArenaAlloca throw InvalidInputException("GeoJSON input coordinates field is not an array of arrays: %s", raw.GetString()); } - multi_polygon[idx] = PolygonFromGeoJSON(polygon_val, arena, raw, has_z); + MultiPolygon::Part(multi_polygon, idx) = PolygonFromGeoJSON(polygon_val, arena, raw, has_z); } return multi_polygon; @@ -445,7 +452,7 @@ static MultiPolygon MultiPolygonFromGeoJSON(yyjson_val *coord_array, ArenaAlloca static Geometry FromGeoJSON(yyjson_val *root, ArenaAllocator &arena, const string_t &raw, bool &has_z); -static GeometryCollection GeometryCollectionFromGeoJSON(yyjson_val *root, ArenaAllocator &arena, const string_t &raw, +static Geometry GeometryCollectionFromGeoJSON(yyjson_val *root, ArenaAllocator &arena, const string_t &raw, bool &has_z) { auto geometries_val = yyjson_obj_get(root, "geometries"); if (!geometries_val) { @@ -457,14 +464,14 @@ static GeometryCollection GeometryCollectionFromGeoJSON(yyjson_val *root, ArenaA auto num_geometries = yyjson_arr_size(geometries_val); if (num_geometries == 0) { // Empty - return GeometryCollection::Empty(has_z, false); + return GeometryCollection::CreateEmpty(has_z, false); } else { // GeometryCollection auto geometry_collection = GeometryCollection::Create(arena, num_geometries, has_z, false); size_t idx, max; yyjson_val *geometry_val; yyjson_arr_foreach(geometries_val, idx, max, geometry_val) { - geometry_collection[idx] = FromGeoJSON(geometry_val, arena, raw, has_z); + GeometryCollection::Part(geometry_collection, idx) = FromGeoJSON(geometry_val, arena, raw, has_z); } return geometry_collection; @@ -540,7 +547,7 @@ static void GeoJSONFragmentToGeometryFunction(DataChunk &args, ExpressionState & // Ensure the geometries has consistent Z values geom.SetVertexType(lstate.arena, has_z, false); } - return geom.Serialize(result); + return Geometry::Serialize(geom, result); } }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_collect.cpp b/spatial/src/spatial/core/functions/scalar/st_collect.cpp index f01d30a8..fba20892 100644 --- a/spatial/src/spatial/core/functions/scalar/st_collect.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_collect.cpp @@ -27,6 +27,10 @@ static void CollectFunction(DataChunk &args, ExpressionState &state, Vector &res // First figure out if we have Z or M bool has_z = false; bool has_m = false; + bool all_points = true; + bool all_lines = true; + bool all_polygons = true; + for (idx_t i = offset; i < offset + length; i++) { auto mapped_idx = format.sel->get_index(i); if (format.validity.RowIsValid(mapped_idx)) { @@ -37,7 +41,6 @@ static void CollectFunction(DataChunk &args, ExpressionState &state, Vector &res } } - // TODO: Peek the types first vector geometries; for (idx_t i = offset; i < offset + length; i++) { auto mapped_idx = format.sel->get_index(i); @@ -45,62 +48,31 @@ static void CollectFunction(DataChunk &args, ExpressionState &state, Vector &res auto geometry_blob = ((geometry_t *)format.data)[mapped_idx]; auto geometry = Geometry::Deserialize(arena, geometry_blob); // Dont add empty geometries - if (!geometry.IsEmpty()) { - geometries.push_back(geometry); + if (!Geometry::IsEmpty(geometry)) { + all_points = all_points && geometry_blob.GetType() == GeometryType::POINT; + all_lines = all_lines && geometry_blob.GetType() == GeometryType::LINESTRING; + all_polygons = all_polygons && geometry_blob.GetType() == GeometryType::POLYGON; + + // Ensure all geometries have the same Z and M + geometry.SetVertexType(arena, has_z, has_m); + geometries.push_back(std::move(geometry)); } } } if (geometries.empty()) { - return Geometry(GeometryCollection::Empty(has_z, has_m)).Serialize(result); - } - - bool all_points = true; - bool all_lines = true; - bool all_polygons = true; - - for (auto &geometry : geometries) { - if (geometry.GetType() != GeometryType::POINT) { - all_points = false; - } - if (geometry.GetType() != GeometryType::LINESTRING) { - all_lines = false; - } - if (geometry.GetType() != GeometryType::POLYGON) { - all_polygons = false; - } + return Geometry::Serialize(GeometryCollection::CreateEmpty(has_z, has_m), result); } // TODO: Dont upcast the children, just append them. - if (all_points) { - auto collection = MultiPoint::Create(arena, geometries.size(), has_z, has_m); - for (idx_t i = 0; i < geometries.size(); i++) { - geometries[i].SetVertexType(arena, has_z, has_m); - collection[i] = geometries[i].As(); - } - return Geometry(collection).Serialize(result); + return Geometry::Serialize(MultiPoint::Create(arena, geometries, has_z, has_m), result); } else if (all_lines) { - auto collection = MultiLineString::Create(arena, geometries.size(), has_z, has_m); - for (idx_t i = 0; i < geometries.size(); i++) { - geometries[i].SetVertexType(arena, has_z, has_m); - collection[i] = geometries[i].As(); - } - return Geometry(collection).Serialize(result); + return Geometry::Serialize(MultiLineString::Create(arena, geometries, has_z, has_m), result); } else if (all_polygons) { - auto collection = MultiPolygon::Create(arena, geometries.size(), has_z, has_m); - for (idx_t i = 0; i < geometries.size(); i++) { - geometries[i].SetVertexType(arena, has_z, has_m); - collection[i] = geometries[i].As(); - } - return Geometry(collection).Serialize(result); + return Geometry::Serialize(MultiPolygon::Create(arena, geometries, has_z, has_m), result); } else { - auto collection = GeometryCollection::Create(arena, geometries.size(), has_z, has_m); - for (idx_t i = 0; i < geometries.size(); i++) { - geometries[i].SetVertexType(arena, has_z, has_m); - collection[i] = geometries[i]; - } - return Geometry(collection).Serialize(result); + return Geometry::Serialize(GeometryCollection::Create(arena, geometries, has_z, has_m), result); } }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp b/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp index fa93bc2f..7ded923e 100644 --- a/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp @@ -15,24 +15,22 @@ namespace core { //------------------------------------------------------------------------------ // GEOMETRY //------------------------------------------------------------------------------ -static void CollectPoints(Geometry &geom, vector &points) { +static void CollectPoints(const Geometry &geom, vector &points) { switch (geom.GetType()) { case GeometryType::POINT: { - points.push_back(geom.As()); + points.push_back(geom); break; } case GeometryType::MULTIPOINT: { - auto &multipoint = geom.As(); - for (auto &point : multipoint) { - points.push_back(point); - } + for(uint32_t i = 0; i < MultiPoint::PartCount(geom); i++) { + points.push_back(MultiPoint::Part(geom, i)); + } break; } case GeometryType::GEOMETRYCOLLECTION: { - auto &col = geom.As(); - for (auto &g : col) { - CollectPoints(g, points); - } + for (uint32_t i = 0; i < GeometryCollection::PartCount(geom); i++) { + CollectPoints(GeometryCollection::Part(geom, i), points); + } } default: { break; @@ -40,24 +38,22 @@ static void CollectPoints(Geometry &geom, vector &points) { } } -static void CollectLines(Geometry &geom, vector &lines) { +static void CollectLines(Geometry &geom, vector &lines) { switch (geom.GetType()) { case GeometryType::LINESTRING: { - lines.push_back(geom.As()); + lines.push_back(geom); break; } case GeometryType::MULTILINESTRING: { - auto &multilines = geom.As(); - for (auto &line : multilines) { - lines.push_back(line); - } + for(uint32_t i = 0; i < MultiLineString::PartCount(geom); i++) { + lines.push_back(MultiLineString::Part(geom, i)); + } break; } case GeometryType::GEOMETRYCOLLECTION: { - auto &col = geom.As(); - for (auto &g : col) { - CollectLines(g, lines); - } + for (uint32_t i = 0; i < GeometryCollection::PartCount(geom); i++) { + CollectLines(GeometryCollection::Part(geom, i), lines); + } } default: { break; @@ -65,24 +61,22 @@ static void CollectLines(Geometry &geom, vector &lines) { } } -static void CollectPolygons(Geometry &geom, vector &polys) { +static void CollectPolygons(Geometry &geom, vector &polys) { switch (geom.GetType()) { case GeometryType::POLYGON: { - polys.push_back(geom.As()); + polys.push_back(geom); break; } case GeometryType::MULTIPOLYGON: { - auto &multipolys = geom.As(); - for (auto &poly : multipolys) { - polys.push_back(poly); - } - break; + for(uint32_t i = 0; i < MultiPolygon::PartCount(geom); i++) { + polys.push_back(MultiPolygon::Part(geom, i)); + } + break; } case GeometryType::GEOMETRYCOLLECTION: { - auto &col = geom.As(); - for (auto &g : col) { - CollectPolygons(g, polys); - } + for (uint32_t i = 0; i < GeometryCollection::PartCount(geom); i++) { + CollectPolygons(GeometryCollection::Part(geom, i), polys); + } } default: { break; @@ -108,22 +102,24 @@ static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &stat return input; } else if (geometry.IsCollection()) { // if it is a geometry collection, we need to collect all points - if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !geometry.IsEmpty()) { - vector points; + if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { + vector points; CollectPoints(geometry, points); uint32_t size = points.size(); auto mpoint = MultiPoint::Create(arena, size, props.HasZ(), props.HasM()); for (uint32_t i = 0; i < size; i++) { - mpoint[i] = points[i]; + MultiPoint::Part(mpoint, i) = points[i]; } - return Geometry(mpoint).Serialize(result); + return Geometry::Serialize(mpoint, result); } // otherwise, we return an empty multipoint - return Geometry(MultiPoint::Empty(props.HasZ(), props.HasM())).Serialize(result); + auto empty = MultiPoint::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); } else { // otherwise if its not a collection, we return an empty point - return Geometry(Point::Empty(props.HasZ(), props.HasM())).Serialize(result); + auto empty = Point::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); } } case 2: { @@ -132,22 +128,24 @@ static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &stat return input; } else if (geometry.IsCollection()) { // if it is a geometry collection, we need to collect all lines - if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !geometry.IsEmpty()) { - vector lines; + if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { + vector lines; CollectLines(geometry, lines); uint32_t size = lines.size(); auto mline = MultiLineString::Create(arena, size, props.HasZ(), props.HasM()); for (uint32_t i = 0; i < size; i++) { - mline[i] = lines[i]; + MultiLineString::Part(mline, i) = lines[i]; } - return Geometry(mline).Serialize(result); + return Geometry::Serialize(mline, result); } // otherwise, we return an empty multilinestring - return Geometry(MultiLineString::Empty(props.HasZ(), props.HasM())).Serialize(result); + auto empty = MultiLineString::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); } else { // otherwise if its not a collection, we return an empty linestring - return Geometry(LineString::Empty(props.HasZ(), props.HasM())).Serialize(result); + auto empty = LineString::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); } } case 3: { @@ -155,22 +153,24 @@ static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &stat return input; } else if (geometry.IsCollection()) { // if it is a geometry collection, we need to collect all polygons - if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !geometry.IsEmpty()) { - vector polys; + if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { + vector polys; CollectPolygons(geometry, polys); uint32_t size = polys.size(); auto mpoly = MultiPolygon::Create(arena, size, props.HasZ(), props.HasM()); for (uint32_t i = 0; i < size; i++) { - mpoly[i] = polys[i]; + MultiPolygon::Part(mpoly, i) = polys[i]; } - return Geometry(mpoly).Serialize(result); + return Geometry::Serialize(mpoly, result); } // otherwise, we return an empty multipolygon - return Geometry(MultiPolygon::Empty(props.HasZ(), props.HasM())).Serialize(result); + auto empty = MultiPolygon::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); } else { // otherwise if its not a collection, we return an empty polygon - return Geometry(Polygon::Empty(props.HasZ(), props.HasM())).Serialize(result); + auto empty = Polygon::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); } } default: @@ -191,49 +191,47 @@ static void CollectionExtractAutoFunction(DataChunk &args, ExpressionState &stat UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { if (input.GetType() == GeometryType::GEOMETRYCOLLECTION) { auto props = input.GetProperties(); - auto geometry = Geometry::Deserialize(arena, input); - - auto &collection = geometry.As(); - if (collection.IsEmpty()) { + auto collection = Geometry::Deserialize(arena, input); + if (GeometryCollection::IsEmpty(collection)) { return input; } // Find the highest dimension of the geometries in the collection // Empty geometries are ignored - auto dim = geometry.GetDimension(true); + auto dim = Geometry::GetDimension(collection, true); switch (dim) { // Point case case 0: { - vector points; - CollectPoints(geometry, points); + vector points; + CollectPoints(collection, points); uint32_t size = points.size(); auto mpoint = MultiPoint::Create(arena, size, props.HasZ(), props.HasM()); for (uint32_t i = 0; i < size; i++) { - mpoint[i] = points[i]; + MultiPoint::Part(mpoint, i) = points[i]; } - return Geometry(mpoint).Serialize(result); + return Geometry::Serialize(mpoint, result); } // LineString case case 1: { - vector lines; - CollectLines(geometry, lines); + vector lines; + CollectLines(collection, lines); uint32_t size = lines.size(); auto mline = MultiLineString::Create(arena, size, props.HasZ(), props.HasM()); for (uint32_t i = 0; i < size; i++) { - mline[i] = lines[i]; + MultiLineString::Part(mline, i) = lines[i]; } - return Geometry(mline).Serialize(result); + return Geometry::Serialize(mline, result); } // Polygon case case 2: { - vector polys; - CollectPolygons(geometry, polys); + vector polys; + CollectPolygons(collection, polys); uint32_t size = polys.size(); auto mpoly = MultiPolygon::Create(arena, size, props.HasZ(), props.HasM()); for (uint32_t i = 0; i < size; i++) { - mpoly[i] = polys[i]; + MultiPolygon::Part(mpoly, i) = polys[i]; } - return Geometry(mpoly).Serialize(result); + return Geometry::Serialize(mpoly, result); } default: { throw InternalException("Invalid dimension in collection extract"); diff --git a/spatial/src/spatial/core/functions/scalar/st_dimension.cpp b/spatial/src/spatial/core/functions/scalar/st_dimension.cpp index d36f9ebb..1edcae24 100644 --- a/spatial/src/spatial/core/functions/scalar/st_dimension.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_dimension.cpp @@ -23,7 +23,7 @@ static void DimensionFunction(DataChunk &args, ExpressionState &state, Vector &r UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { auto geometry = Geometry::Deserialize(lstate.arena, input); - return geometry.GetDimension(false); + return Geometry::GetDimension(geometry, false); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_distance_sphere.cpp b/spatial/src/spatial/core/functions/scalar/st_distance_sphere.cpp index 61ecbe79..21f08fd6 100644 --- a/spatial/src/spatial/core/functions/scalar/st_distance_sphere.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_distance_sphere.cpp @@ -68,11 +68,11 @@ static void GeometryHaversineFunction(DataChunk &args, ExpressionState &state, V } auto left_geom = Geometry::Deserialize(lstate.arena, left); auto right_geom = Geometry::Deserialize(lstate.arena, right); - if (left_geom.IsEmpty() || right_geom.IsEmpty()) { + if (Point::IsEmpty(left_geom) || Point::IsEmpty(right_geom)) { throw InvalidInputException("ST_Distance_Sphere does not support EMPTY geometries"); } - auto v1 = left_geom.As().Get(0); - auto v2 = right_geom.As().Get(0); + auto v1 = Point::GetVertex(left_geom); + auto v2 = Point::GetVertex(right_geom); return HaversineFunction(v1.x, v1.y, v2.x, v2.y); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_dump.cpp b/spatial/src/spatial/core/functions/scalar/st_dump.cpp index 2cf5c8bb..db904029 100644 --- a/spatial/src/spatial/core/functions/scalar/st_dump.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_dump.cpp @@ -46,35 +46,12 @@ static void DumpFunction(DataChunk &args, ExpressionState &state, Vector &result auto current_path = std::get<1>(current); stack.pop_back(); - - if (current_geom.GetType() == GeometryType::MULTIPOINT) { - auto mpoint = current_geom.As(); - for (int32_t i = 0; i < mpoint.Count(); i++) { - auto path = current_path; - path.push_back(i + 1); // path is 1-indexed - stack.emplace_back(mpoint[i], path); - } - } else if (current_geom.GetType() == GeometryType::MULTILINESTRING) { - auto mline = current_geom.As(); - for (int32_t i = 0; i < mline.Count(); i++) { - auto path = current_path; - path.push_back(i + 1); - stack.emplace_back(mline[i], path); - } - } else if (current_geom.GetType() == GeometryType::MULTIPOLYGON) { - auto mpoly = current_geom.As(); - for (int32_t i = 0; i < mpoly.Count(); i++) { - auto path = current_path; - path.push_back(i + 1); - stack.emplace_back(mpoly[i], path); - } - } else if (current_geom.GetType() == GeometryType::GEOMETRYCOLLECTION) { - auto collection = current_geom.As(); - for (int32_t i = 0; i < collection.Count(); i++) { - auto path = current_path; - path.push_back(i + 1); - stack.emplace_back(collection[i], path); - } + if(current_geom.IsCollection()) { + for(int32_t i = 0; i < CollectionGeometry::PartCount(current_geom); i++) { + auto path = current_path; + path.push_back(i + 1); // path is 1-indexed + stack.emplace_back(CollectionGeometry::Part(current_geom, i), path); + } } else { items.push_back(current); } @@ -107,7 +84,7 @@ static void DumpFunction(DataChunk &args, ExpressionState &state, Vector &result for (idx_t i = 0; i < geom_length; i++) { // Write the geometry auto &item_blob = std::get<0>(items[i]); - geom_data[geom_offset + i] = item_blob.Serialize(*result_geom_vec); + geom_data[geom_offset + i] = Geometry::Serialize(item_blob, *result_geom_vec); // Now write the paths auto &path = std::get<1>(items[i]); diff --git a/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp b/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp index c31102dd..dc2d8165 100644 --- a/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp @@ -71,16 +71,16 @@ static void GeometryEndPointFunction(DataChunk &args, ExpressionState &state, Ve return geometry_t {}; } - auto line = Geometry::Deserialize(lstate.arena, input).As(); - auto point_count = line.Count(); + auto line = Geometry::Deserialize(lstate.arena, input); + auto point_count = LineString::VertexCount(line); if (point_count == 0) { mask.SetInvalid(row_idx); return geometry_t {}; } - auto point = Point::FromReference(line, point_count - 1); - return Geometry(point).Serialize(result); + auto point = LineString::GetPointAsReference(line, point_count - 1); + return Geometry::Serialize(point, result); }); } //------------------------------------------------------------------------------ diff --git a/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp b/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp index 1b89ddb6..29a7780b 100644 --- a/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp @@ -103,14 +103,14 @@ static void GeometryExteriorRingFunction(DataChunk &args, ExpressionState &state return geometry_t {}; } - auto polygon = Geometry::Deserialize(arena, input).As(); - if (polygon.IsEmpty()) { - return Geometry(LineString::Empty(polygon.GetProperties().HasZ(), polygon.GetProperties().HasM())) - .Serialize(result); + auto polygon = Geometry::Deserialize(arena, input); + if (Polygon::IsEmpty(polygon)) { + auto empty = LineString::CreateEmpty(polygon.GetProperties().HasZ(), polygon.GetProperties().HasM()); + return Geometry::Serialize(empty, result); } - auto &shell = polygon[0]; - return Geometry(shell).Serialize(result); + auto &shell = Polygon::Part(polygon, 0); + return Geometry::Serialize(shell, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp b/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp index 00a6f526..81836450 100644 --- a/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp @@ -162,25 +162,26 @@ static void GeometryFlipCoordinatesFunction(DataChunk &args, ExpressionState &st auto count = args.size(); struct FlipOp { - static void Apply(SinglePartGeometry &geom, ArenaAllocator &arena) { - geom.MakeMutable(arena); - for (idx_t i = 0; i < geom.Count(); i++) { - auto vertex = geom.Get(i); + static void Case(Geometry::Tags::SinglePartGeometry, Geometry &geom, ArenaAllocator &arena) { + SinglePartGeometry::MakeMutable(geom, arena); + for (idx_t i = 0; i < SinglePartGeometry::VertexCount(geom); i++) { + auto vertex = SinglePartGeometry::GetVertex(geom, i); std::swap(vertex.x, vertex.y); - geom.Set(i, vertex); + SinglePartGeometry::SetVertex(geom, i, vertex); } } - static void Apply(MultiPartGeometry &geom, ArenaAllocator &arena) { - for (auto &part : geom) { - part.Visit(arena); - } + static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, ArenaAllocator &arena) { + for (uint32_t i = 0; i < MultiPartGeometry::PartCount(geom); i++) { + auto &part = MultiPartGeometry::Part(geom, i); + Geometry::Visit(part, arena); + } } }; UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { auto geom = Geometry::Deserialize(lstate.arena, input); - geom.Visit(lstate.arena); - return geom.Serialize(result); + Geometry::Visit(geom, lstate.arena); + return Geometry::Serialize(geom, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_force.cpp b/spatial/src/spatial/core/functions/scalar/st_force.cpp index 7ccd89ab..ae1720dd 100644 --- a/spatial/src/spatial/core/functions/scalar/st_force.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_force.cpp @@ -27,7 +27,7 @@ static void GeometryFunction(DataChunk &args, ExpressionState &state, Vector &re input, z_values, m_values, result, count, [&](const geometry_t &blob, double default_z, double default_m) { auto geom = Geometry::Deserialize(arena, blob); geom.SetVertexType(arena, HAS_Z, HAS_M, default_z, default_m); - return geom.Serialize(result); + return Geometry::Serialize(geom, result); }); } else if (HAS_Z || HAS_M) { @@ -39,13 +39,13 @@ static void GeometryFunction(DataChunk &args, ExpressionState &state, Vector &re auto geom = Geometry::Deserialize(arena, blob); geom.SetVertexType(arena, HAS_Z, HAS_M, def_z, def_m); - return geom.Serialize(result); + return Geometry::Serialize(geom, result); }); } else { UnaryExecutor::Execute(input, result, count, [&](const geometry_t &blob) { auto geom = Geometry::Deserialize(arena, blob); geom.SetVertexType(arena, HAS_Z, HAS_M); - return geom.Serialize(result); + return Geometry::Serialize(geom, result); }); } } diff --git a/spatial/src/spatial/core/functions/scalar/st_geomfromhexwkb.cpp b/spatial/src/spatial/core/functions/scalar/st_geomfromhexwkb.cpp index 43445fda..2e2b9f14 100644 --- a/spatial/src/spatial/core/functions/scalar/st_geomfromhexwkb.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_geomfromhexwkb.cpp @@ -48,7 +48,8 @@ void GeometryFromHEXWKB(DataChunk &args, ExpressionState &state, Vector &result) blob_ptr[blob_idx++] = (byte_a << 4) + byte_b; } - return reader.Deserialize(blob_ptr, blob_size).Serialize(result); + auto geom = reader.Deserialize(blob_ptr, blob_size); + return Geometry::Serialize(geom, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_geomfromtext.cpp b/spatial/src/spatial/core/functions/scalar/st_geomfromtext.cpp index c52d41a9..d8b8512a 100644 --- a/spatial/src/spatial/core/functions/scalar/st_geomfromtext.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_geomfromtext.cpp @@ -45,7 +45,7 @@ static void GeometryFromWKTFunction(DataChunk &args, ExpressionState &state, Vec [&](string_t &wkt, ValidityMask &mask, idx_t idx) { try { auto geom = reader.Parse(wkt); - return geom.Serialize(result); + return Geometry::Serialize(geom, result); } catch (InvalidInputException &error) { if (!info.ignore_invalid) { throw; diff --git a/spatial/src/spatial/core/functions/scalar/st_geomfromwkb.cpp b/spatial/src/spatial/core/functions/scalar/st_geomfromwkb.cpp index eb5c6da8..6d72beaa 100644 --- a/spatial/src/spatial/core/functions/scalar/st_geomfromwkb.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_geomfromwkb.cpp @@ -274,7 +274,10 @@ static void GeometryFromWKBFunction(DataChunk &args, ExpressionState &state, Vec WKBReader reader(arena); UnaryExecutor::Execute( - input, result, count, [&](string_t input) { return reader.Deserialize(input).Serialize(result); }); + input, result, count, [&](string_t input) { + auto geom = reader.Deserialize(input); + return Geometry::Serialize(geom, result); + }); } //------------------------------------------------------------------------------ diff --git a/spatial/src/spatial/core/functions/scalar/st_isempty.cpp b/spatial/src/spatial/core/functions/scalar/st_isempty.cpp index 2c658b23..11448594 100644 --- a/spatial/src/spatial/core/functions/scalar/st_isempty.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_isempty.cpp @@ -53,7 +53,10 @@ static void GeometryIsEmptyFunction(DataChunk &args, ExpressionState &state, Vec auto count = args.size(); UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { return Geometry::Deserialize(lstate.arena, input).IsEmpty(); }); + input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(lstate.arena, input); + return Geometry::IsEmpty(geom); + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/core/functions/scalar/st_length.cpp b/spatial/src/spatial/core/functions/scalar/st_length.cpp index 90b80b56..46481cbe 100644 --- a/spatial/src/spatial/core/functions/scalar/st_length.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_length.cpp @@ -54,34 +54,15 @@ static void GeometryLengthFunction(DataChunk &args, ExpressionState &state, Vect auto &input = args.data[0]; auto count = args.size(); - struct op { - static double Apply(const LineString &line) { - return line.Length(); - } - - static double Apply(const MultiLineString &mline) { - double sum = 0.0; - for (const auto &line : mline) { - sum += line.Length(); - } - return sum; - } - - static double Apply(const GeometryCollection &collection) { - double sum = 0.0; - for (const auto &geom : collection) { - sum += geom.Visit(); - } - return sum; - } - - static double Apply(const BaseGeometry &) { - return 0.0; - } - }; - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { return Geometry::Deserialize(arena, input).Visit(); }); + input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + double length = 0.0; + Geometry::ExtractLines(geom, [&](const Geometry &line) { + length += LineString::Length(line); + }); + return length; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp b/spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp index 101f79c9..446514f0 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp @@ -26,8 +26,8 @@ static void MakeEnvelopeFunction(DataChunk &args, ExpressionState &state, Vector GenericExecutor::ExecuteQuaternary( 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) { - auto box = Polygon::FromBox(lstate.arena, x_min.val, y_min.val, x_max.val, y_max.val); - return Geometry(box).Serialize(result); + auto box = Polygon::CreateFromBox(lstate.arena, x_min.val, y_min.val, x_max.val, y_max.val); + return Geometry::Serialize(box, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_makeline.cpp b/spatial/src/spatial/core/functions/scalar/st_makeline.cpp index 43f6509f..1615a4bf 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makeline.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makeline.cpp @@ -25,7 +25,7 @@ static void MakeLineListFunction(DataChunk &args, ExpressionState &state, Vector auto offset = geometry_list.offset; auto length = geometry_list.length; - auto line_geom = LineString::Create(arena, length, false, false); + auto line = LineString::Create(arena, length, false, false); uint32_t vertex_idx = 0; for (idx_t i = offset; i < offset + length; i++) { @@ -44,23 +44,21 @@ static void MakeLineListFunction(DataChunk &args, ExpressionState &state, Vector if (geometry_blob.GetProperties().HasZ() || geometry_blob.GetProperties().HasM()) { throw InvalidInputException("ST_MakeLine from list does not support Z or M geometries"); } - - auto point = Geometry::Deserialize(arena, geometry_blob).As(); - if (point.IsEmpty()) { + auto point = Geometry::Deserialize(arena, geometry_blob); + if (Point::IsEmpty(point)) { continue; } - auto vertex = point.Get(0); - line_geom.Set(vertex_idx++, vertex.x, vertex.y); + LineString::Vertex(line, vertex_idx++) = Point::GetVertex(point); } // Shrink the vertex array to the actual size - line_geom.Resize(arena, vertex_idx); + LineString::Resize(line, arena, vertex_idx); - if (line_geom.Count() == 1) { + if (line.Count() == 1) { throw InvalidInputException("ST_MakeLine requires zero or two or more POINT geometries"); } - return Geometry(line_geom).Serialize(result); + return Geometry::Serialize(line, result); }); } @@ -72,37 +70,36 @@ static void MakeLineBinaryFunction(DataChunk &args, ExpressionState &state, Vect BinaryExecutor::Execute( args.data[0], args.data[1], result, count, [&](geometry_t &geom_blob_left, geometry_t &geom_blob_right) { - if (geom_blob_left.GetType() != GeometryType::POINT || geom_blob_right.GetType() != GeometryType::POINT) { + + if (geom_blob_left.GetType() != GeometryType::POINT || geom_blob_right.GetType() != GeometryType::POINT) { throw InvalidInputException("ST_MakeLine only accepts POINT geometries"); } auto geometry_left = Geometry::Deserialize(arena, geom_blob_left); auto geometry_right = Geometry::Deserialize(arena, geom_blob_right); - if (geometry_left.IsEmpty() && geometry_right.IsEmpty()) { + if (Point::IsEmpty(geometry_left) && Point::IsEmpty(geometry_right)) { // Empty linestring - return Geometry(LineString::Empty(false, false)).Serialize(result); + auto empty = LineString::CreateEmpty(false, false); + return Geometry::Serialize(empty, result); } - if (geometry_left.IsEmpty() || geometry_right.IsEmpty()) { + if (Point::IsEmpty(geometry_left) || Point::IsEmpty(geometry_right)) { throw InvalidInputException("ST_MakeLine requires zero or two or more POINT geometries"); } auto has_z = geom_blob_left.GetProperties().HasZ() || geom_blob_right.GetProperties().HasZ(); auto has_m = geom_blob_left.GetProperties().HasM() || geom_blob_right.GetProperties().HasM(); - auto &point_left = geometry_left.As(); - auto &point_right = geometry_right.As(); - // TODO: Dont upcast the child geometries, just append and let the append function handle upcasting of the // target instead. - point_left.SetVertexType(arena, has_z, has_m); - point_right.SetVertexType(arena, has_z, has_m); + geometry_left.SetVertexType(arena, has_z, has_m); + geometry_right.SetVertexType(arena, has_z, has_m); - auto line_geom = LineString::Empty(has_z, has_m); - line_geom.Append(arena, point_left); - line_geom.Append(arena, point_right); - return Geometry(line_geom).Serialize(result); + auto line_geom = LineString::CreateEmpty(has_z, has_m); + LineString::Append(line_geom, arena, geometry_left); + LineString::Append(line_geom, arena, geometry_right); + return Geometry::Serialize(line_geom, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp b/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp index e3e6c5f8..eb253b03 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp @@ -33,13 +33,12 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state throw InvalidInputException("ST_MakePolygon does not support Z or M geometries"); } - auto shell_geom = Geometry::Deserialize(arena, line_blob); - auto &shell = shell_geom.As(); - if (shell.Count() < 4) { + auto shell = Geometry::Deserialize(arena, line_blob); + if (LineString::VertexCount(shell) < 4) { throw InvalidInputException("ST_MakePolygon shell requires at least 4 vertices"); } - if (!shell.IsClosed()) { + if (!LineString::IsClosed(shell)) { throw InvalidInputException( "ST_MakePolygon shell must be closed (first and last vertex must be equal)"); } @@ -48,7 +47,7 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state auto holes_offset = rings_list.offset; auto holes_length = rings_list.length; - vector rings; + vector rings; rings.push_back(shell); for (idx_t hole_idx = 0; hole_idx < holes_length; hole_idx++) { @@ -64,18 +63,17 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state throw InvalidInputException("ST_MakePolygon does not support Z or M geometries"); } - auto hole_geometry = Geometry::Deserialize(arena, geometry_blob); - if (hole_geometry.GetType() != GeometryType::LINESTRING) { + auto hole = Geometry::Deserialize(arena, geometry_blob); + if (hole.GetType() != GeometryType::LINESTRING) { throw InvalidInputException( StringUtil::Format("ST_MakePolygon hole #%lu is not a LINESTRING geometry", hole_idx + 1)); } - auto &hole = hole_geometry.As(); - if (hole.Count() < 4) { + if (LineString::VertexCount(hole) < 4) { throw InvalidInputException( StringUtil::Format("ST_MakePolygon hole #%lu requires at least 4 vertices", hole_idx + 1)); } - if (!hole.IsClosed()) { + if (!LineString::IsClosed(hole)) { throw InvalidInputException(StringUtil::Format( "ST_MakePolygon hole #%lu must be closed (first and last vertex must be equal)", hole_idx + 1)); } @@ -85,9 +83,9 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state auto polygon = Polygon::Create(arena, rings.size(), false, false); for (idx_t ring_idx = 0; ring_idx < rings.size(); ring_idx++) { - polygon[ring_idx] = std::move(rings[ring_idx]); + Polygon::Part(polygon, ring_idx) = std::move(rings[ring_idx]); } - return Geometry(polygon).Serialize(result); + return Geometry::Serialize(polygon, result); }); } @@ -102,23 +100,21 @@ static void MakePolygonFromShellFunction(DataChunk &args, ExpressionState &state throw InvalidInputException("ST_MakePolygon only accepts LINESTRING geometries"); } - auto line_geom = Geometry::Deserialize(arena, line_blob); - auto &line = line_geom.As(); + auto line = Geometry::Deserialize(arena, line_blob); - auto line_count = line.Count(); - if (line_count < 4) { + if (LineString::VertexCount(line) < 4) { throw InvalidInputException("ST_MakePolygon shell requires at least 4 vertices"); } - if (!line.IsClosed()) { + if (!LineString::IsClosed(line)) { throw InvalidInputException("ST_MakePolygon shell must be closed (first and last vertex must be equal)"); } auto props = line_blob.GetProperties(); auto polygon = Polygon::Create(arena, 1, props.HasZ(), props.HasM()); - polygon[0] = std::move(line); - return Geometry(polygon).Serialize(result); + Polygon::Part(polygon, 0) = std::move(line); + return Geometry::Serialize(polygon, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp b/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp index 394ed5e6..76153da6 100644 --- a/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp @@ -22,17 +22,18 @@ static void GeometryNGeometriesFunction(DataChunk &args, ExpressionState &state, UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { struct op { - static int32_t Apply(const CollectionGeometry &collection) { - return static_cast(collection.Count()); + static int32_t Case(Geometry::Tags::CollectionGeometry, const Geometry &collection) { + return static_cast(CollectionGeometry::PartCount(collection)); } - static int32_t Apply(const Polygon &geom) { - return geom.IsEmpty() ? 0 : 1; + static int32_t Case(Geometry::Tags::Polygon, const Geometry &geom) { + return Polygon::IsEmpty(geom) ? 0 : 1; } - static int32_t Apply(const SinglePartGeometry &geom) { - return geom.IsEmpty() ? 0 : 1; + static int32_t Case(Geometry::Tags::SinglePartGeometry, const Geometry &geom) { + return SinglePartGeometry::IsEmpty(geom)? 0 : 1; } }; - return Geometry::Deserialize(ctx.arena, input).Visit(); + auto geom = Geometry::Deserialize(ctx.arena, input); + return Geometry::Visit(geom); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_ninteriorrings.cpp b/spatial/src/spatial/core/functions/scalar/st_ninteriorrings.cpp index 3f4fa4d7..52be0ea1 100644 --- a/spatial/src/spatial/core/functions/scalar/st_ninteriorrings.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_ninteriorrings.cpp @@ -40,7 +40,8 @@ static void GeometryInteriorRingsFunction(DataChunk &args, ExpressionState &stat validity.SetInvalid(idx); return 0; } - auto rings = Geometry::Deserialize(arena, input).As().Count(); + auto polygon = Geometry::Deserialize(arena, input); + auto rings = Polygon::PartCount(polygon); return rings == 0 ? 0 : static_cast(rings - 1); // -1 for the exterior ring }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_npoints.cpp b/spatial/src/spatial/core/functions/scalar/st_npoints.cpp index ecb259c0..3009df67 100644 --- a/spatial/src/spatial/core/functions/scalar/st_npoints.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_npoints.cpp @@ -76,20 +76,24 @@ static void GeometryNumPointsFunction(DataChunk &args, ExpressionState &state, V auto count = args.size(); struct op { - static uint32_t Apply(const SinglePartGeometry &geom) { + static uint32_t Case(Geometry::Tags::SinglePartGeometry, const Geometry &geom) { return geom.Count(); } - static uint32_t Apply(const MultiPartGeometry &geom) { + static uint32_t Case(Geometry::Tags::MultiPartGeometry, const Geometry &geom) { uint32_t count = 0; - for (const auto &part : geom) { - count += part.Visit(); + for(uint32_t i = 0; i < MultiPartGeometry::PartCount(geom); i++) { + auto part = MultiPartGeometry::Part(geom, i); + count += Geometry::Visit(part); } return count; } }; UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { return Geometry::Deserialize(arena, input).Visit(); }); + input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + return Geometry::Visit(geom); + }); } //------------------------------------------------------------------------------ diff --git a/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp b/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp index 82a81f99..4630e883 100644 --- a/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp @@ -78,38 +78,17 @@ static void GeometryPerimeterFunction(DataChunk &args, ExpressionState &state, V auto &input = args.data[0]; auto count = args.size(); - struct op { - static double Apply(const Polygon &poly) { - double sum = 0; - for (const auto &ring : poly) { - sum += ring.Length(); - } - return sum; - } - - static double Apply(const MultiPolygon &mline) { - double sum = 0.0; - for (const auto &poly : mline) { - sum += Apply(poly); - } - return sum; - } - - static double Apply(const GeometryCollection &collection) { - double sum = 0.0; - for (const auto &geom : collection) { - sum += geom.Visit(); - } - return sum; - } - - static double Apply(const BaseGeometry &) { - return 0.0; - } - }; - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { return Geometry::Deserialize(arena, input).Visit(); }); + input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + double perimeter = 0.0; + Geometry::ExtractPolygons(geom, [&](const Geometry &poly) { + for(auto &p : Polygon::Parts(poly)) { + perimeter += LineString::Length(p); + } + }); + return perimeter; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/core/functions/scalar/st_point.cpp b/spatial/src/spatial/core/functions/scalar/st_point.cpp index 86535f1f..3cfa568d 100644 --- a/spatial/src/spatial/core/functions/scalar/st_point.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_point.cpp @@ -107,7 +107,7 @@ static void PointFunction(DataChunk &args, ExpressionState &state, Vector &resul auto count = args.size(); BinaryExecutor::Execute(x, y, result, count, [&](double x, double y) { - return Geometry(Point::FromVertex(arena, VertexXY {x, y})).Serialize(result); + return Geometry::Serialize(Point::CreateFromVertex(arena, VertexXY {x, y}), result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_pointn.cpp b/spatial/src/spatial/core/functions/scalar/st_pointn.cpp index 1040a7cb..0c79e898 100644 --- a/spatial/src/spatial/core/functions/scalar/st_pointn.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_pointn.cpp @@ -79,8 +79,8 @@ static void GeometryPointNFunction(DataChunk &args, ExpressionState &state, Vect mask.SetInvalid(row_idx); return geometry_t {}; } - auto line = Geometry::Deserialize(arena, input).As(); - auto point_count = line.Count(); + auto line = Geometry::Deserialize(arena, input); + auto point_count = LineString::VertexCount(line); if (point_count == 0 || index == 0 || index < -static_cast(point_count) || index > static_cast(point_count)) { @@ -89,10 +89,8 @@ static void GeometryPointNFunction(DataChunk &args, ExpressionState &state, Vect } auto actual_index = index < 0 ? point_count + index : index - 1; - - auto point = Point::FromReference(line, actual_index); - - return Geometry(point).Serialize(result); + auto point = LineString::GetPointAsReference(line, actual_index); + return Geometry::Serialize(point, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_quadkey.cpp b/spatial/src/spatial/core/functions/scalar/st_quadkey.cpp index 650ac718..0cbe6a99 100644 --- a/spatial/src/spatial/core/functions/scalar/st_quadkey.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_quadkey.cpp @@ -69,23 +69,19 @@ static void GeometryQuadKeyFunction(DataChunk &args, ExpressionState &state, Vec BinaryExecutor::Execute( geom, level, result, count, [&](geometry_t input, int32_t level) { + if (level < 1 || level > 23) { + throw InvalidInputException("ST_QuadKey: Level must be between 1 and 23"); + } if (input.GetType() != GeometryType::POINT) { throw InvalidInputException("ST_QuadKey: Only POINT geometries are supported"); } auto point = Geometry::Deserialize(arena, input); - if (point.IsEmpty()) { + if (Point::IsEmpty(point)) { throw InvalidInputException("ST_QuadKey: Empty geometries are not supported"); } - auto vertex = point.As().Get(0); - auto x = vertex.x; - auto y = vertex.y; - - if (level < 1 || level > 23) { - throw InvalidInputException("ST_QuadKey: Level must be between 1 and 23"); - } - + auto vertex = Point::GetVertex(point); char buffer[64]; - GetQuadKey(x, y, level, buffer); + GetQuadKey(vertex.x, vertex.y, level, buffer); return StringVector::AddString(result, buffer, level); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp b/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp index c024485a..9577e418 100644 --- a/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp @@ -71,16 +71,13 @@ static void GeometryStartPointFunction(DataChunk &args, ExpressionState &state, return geometry_t {}; } - auto line = Geometry::Deserialize(lstate.arena, input).As(); - - if (line.IsEmpty()) { + auto line = Geometry::Deserialize(lstate.arena, input); + if (LineString::IsEmpty(line)) { mask.SetInvalid(row_idx); return geometry_t {}; } - - auto point = Point::FromReference(line, 0); - - return Geometry(point).Serialize(result); + auto point = LineString::GetPointAsReference(line, 0); + return Geometry::Serialize(point, result); }); } //------------------------------------------------------------------------------ diff --git a/spatial/src/spatial/core/geometry/CMakeLists.txt b/spatial/src/spatial/core/geometry/CMakeLists.txt index 0367fdb7..e119e0d8 100644 --- a/spatial/src/spatial/core/geometry/CMakeLists.txt +++ b/spatial/src/spatial/core/geometry/CMakeLists.txt @@ -6,5 +6,6 @@ set(EXTENSION_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/wkb_reader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wkb_writer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wkt_reader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/geometry2.cpp PARENT_SCOPE ) \ No newline at end of file diff --git a/spatial/src/spatial/core/geometry/geometry.cpp b/spatial/src/spatial/core/geometry/geometry.cpp index dcefe584..e9d99867 100644 --- a/spatial/src/spatial/core/geometry/geometry.cpp +++ b/spatial/src/spatial/core/geometry/geometry.cpp @@ -8,6 +8,7 @@ namespace core { //------------------------------------------------------------------------------ // Single Part Geometry //------------------------------------------------------------------------------ +/* void SinglePartGeometry::Reference(const SinglePartGeometry &other, uint32_t offset, uint32_t count) { properties = other.properties; data.vertex_data = other.data.vertex_data + offset * properties.VertexSize(); @@ -41,109 +42,116 @@ void SinglePartGeometry::CopyData(ArenaAllocator &alloc, const_data_ptr_t data_p memcpy(data.vertex_data, data_ptr, properties.VertexSize() * count); is_readonly = false; } +*/ +void SinglePartGeometry::Resize(Geometry& geom, ArenaAllocator &alloc, uint32_t new_count) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); -void SinglePartGeometry::Resize(ArenaAllocator &alloc, uint32_t new_count) { - auto vertex_size = properties.VertexSize(); - if (new_count == data_count) { + auto vertex_size = geom.properties.VertexSize(); + if (new_count == geom.data_count) { return; } - if (data.vertex_data == nullptr) { - data.vertex_data = alloc.AllocateAligned(vertex_size * new_count); - data_count = new_count; - is_readonly = false; - memset(data.vertex_data, 0, vertex_size * new_count); + if (geom.data_ptr == nullptr) { + geom.data_ptr = alloc.AllocateAligned(vertex_size * new_count); + geom.data_count = new_count; + geom.is_readonly = false; + memset(geom.data_ptr, 0, vertex_size * new_count); return; } - if (!IsReadOnly()) { - data.vertex_data = alloc.Reallocate(data.vertex_data, data_count * vertex_size, vertex_size * new_count); - data_count = new_count; + if (!geom.is_readonly) { + geom.data_ptr = alloc.Reallocate(geom.data_ptr, geom.data_count * vertex_size, vertex_size * new_count); + geom.data_count = new_count; } else { auto new_data = alloc.AllocateAligned(vertex_size * new_count); memset(new_data, 0, vertex_size * new_count); - auto copy_count = std::min(data_count, new_count); - memcpy(new_data, data.vertex_data, vertex_size * copy_count); - data.vertex_data = new_data; - data_count = new_count; - is_readonly = false; + auto copy_count = std::min(geom.data_count, new_count); + memcpy(new_data, geom.data_ptr, vertex_size * copy_count); + geom.data_ptr = new_data; + geom.data_count = new_count; + geom.is_readonly = false; } } -void SinglePartGeometry::Append(ArenaAllocator &alloc, const SinglePartGeometry &other) { - Append(alloc, &other, 1); +void SinglePartGeometry::Append(Geometry &geom, ArenaAllocator &alloc, const Geometry &other) { + Append(geom, alloc, &other, 1); } -void SinglePartGeometry::Append(ArenaAllocator &alloc, const SinglePartGeometry *others, uint32_t others_count) { - if (IsReadOnly()) { - MakeMutable(alloc); +void SinglePartGeometry::Append(Geometry &geom, ArenaAllocator &alloc, const Geometry *others, uint32_t others_count) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); + if (geom.IsReadOnly()) { + MakeMutable(geom, alloc); } - auto old_count = data_count; + auto old_count = geom.data_count; auto new_count = old_count; for (uint32_t i = 0; i < others_count; i++) { new_count += others[i].Count(); - D_ASSERT(properties.HasZ() == others[i].properties.HasZ()); - D_ASSERT(properties.HasM() == others[i].properties.HasM()); + // The other geometries has to be single part + D_ASSERT(GeometryTypes::IsSinglePart(others[i].type)); + // And have the same z and m properties + D_ASSERT(geom.properties.HasZ() == others[i].properties.HasZ()); + D_ASSERT(geom.properties.HasM() == others[i].properties.HasM()); } - Resize(alloc, new_count); - auto vertex_size = properties.VertexSize(); + Resize(geom, alloc, new_count); + + auto vertex_size = geom.properties.VertexSize(); for (uint32_t i = 0; i < others_count; i++) { auto other = others[i]; - memcpy(data.vertex_data + old_count * vertex_size, other.data.vertex_data, vertex_size * other.data_count); + memcpy(geom.data_ptr + old_count * vertex_size, other.data_ptr, vertex_size * other.data_count); old_count += other.data_count; } - data_count = new_count; + geom.data_count = new_count; } -void SinglePartGeometry::SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, +void SinglePartGeometry::SetVertexType(Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { - if (properties.HasZ() == has_z && properties.HasM() == has_m) { + if (geom.properties.HasZ() == has_z && geom.properties.HasM() == has_m) { return; } - if (IsReadOnly()) { - MakeMutable(alloc); + if (geom.is_readonly) { + MakeMutable(geom, alloc); } - auto used_to_have_z = properties.HasZ(); - auto used_to_have_m = properties.HasM(); - auto old_vertex_size = properties.VertexSize(); + auto used_to_have_z = geom.properties.HasZ(); + auto used_to_have_m = geom.properties.HasM(); + auto old_vertex_size = geom.properties.VertexSize(); - properties.SetZ(has_z); - properties.SetM(has_m); + geom.properties.SetZ(has_z); + geom.properties.SetM(has_m); - auto new_vertex_size = properties.VertexSize(); + auto new_vertex_size = geom.properties.VertexSize(); // Case 1: The new vertex size is larger than the old vertex size if (new_vertex_size > old_vertex_size) { - data.vertex_data = - alloc.ReallocateAligned(data.vertex_data, data_count * old_vertex_size, data_count * new_vertex_size); + geom.data_ptr = + alloc.ReallocateAligned(geom.data_ptr, geom.data_count * old_vertex_size, geom.data_count * new_vertex_size); // There are 5 cases here: if (used_to_have_m && has_m && !used_to_have_z && has_z) { // 1. We go from XYM to XYZM // This is special, because we need to slide the M value to the end of each vertex - for (int64_t i = data_count - 1; i >= 0; i--) { + for (int64_t i = geom.data_count - 1; i >= 0; i--) { auto old_offset = i * old_vertex_size; auto new_offset = i * new_vertex_size; auto old_m_offset = old_offset + sizeof(double) * 2; auto new_z_offset = new_offset + sizeof(double) * 2; auto new_m_offset = new_offset + sizeof(double) * 3; // Move the M value - memcpy(data.vertex_data + new_m_offset, data.vertex_data + old_m_offset, sizeof(double)); + memcpy(geom.data_ptr + new_m_offset, geom.data_ptr + old_m_offset, sizeof(double)); // Set the new Z value - memcpy(data.vertex_data + new_z_offset, &default_z, sizeof(double)); + memcpy(geom.data_ptr + new_z_offset, &default_z, sizeof(double)); // Move the X and Y values - memcpy(data.vertex_data + new_offset, data.vertex_data + old_offset, sizeof(double) * 2); + memcpy(geom.data_ptr + new_offset, geom.data_ptr + old_offset, sizeof(double) * 2); } } else if (!used_to_have_z && has_z && !used_to_have_m && has_m) { // 2. We go from XY to XYZM // This is special, because we need to add both the default Z and M values to the end of each vertex - for (int64_t i = data_count - 1; i >= 0; i--) { + for (int64_t i = geom.data_count - 1; i >= 0; i--) { auto old_offset = i * old_vertex_size; auto new_offset = i * new_vertex_size; - memcpy(data.vertex_data + new_offset, data.vertex_data + old_offset, sizeof(double) * 2); - memcpy(data.vertex_data + new_offset + sizeof(double) * 2, &default_z, sizeof(double)); - memcpy(data.vertex_data + new_offset + sizeof(double) * 3, &default_m, sizeof(double)); + memcpy(geom.data_ptr + new_offset, geom.data_ptr + old_offset, sizeof(double) * 2); + memcpy(geom.data_ptr + new_offset + sizeof(double) * 2, &default_z, sizeof(double)); + memcpy(geom.data_ptr + new_offset + sizeof(double) * 3, &default_m, sizeof(double)); } } else { // Otherwise: @@ -152,11 +160,11 @@ void SinglePartGeometry::SetVertexType(ArenaAllocator &alloc, bool has_z, bool h // 5. We go from XYZ to XYZM // These are all really the same, we just add the default to the end auto default_value = has_m ? default_m : default_z; - for (int64_t i = data_count - 1; i >= 0; i--) { + for (int64_t i = geom.data_count - 1; i >= 0; i--) { auto old_offset = i * old_vertex_size; auto new_offset = i * new_vertex_size; - memmove(data.vertex_data + new_offset, data.vertex_data + old_offset, old_vertex_size); - memcpy(data.vertex_data + new_offset + old_vertex_size, &default_value, sizeof(double)); + memmove(geom.data_ptr + new_offset, geom.data_ptr + old_offset, old_vertex_size); + memcpy(geom.data_ptr + new_offset + old_vertex_size, &default_value, sizeof(double)); } } } @@ -165,89 +173,93 @@ void SinglePartGeometry::SetVertexType(ArenaAllocator &alloc, bool has_z, bool h // This only happens when we go from XYZ -> XYM or XYM -> XYZ // In this case we just need to set the default on the third dimension auto default_value = has_m ? default_m : default_z; - for (uint32_t i = 0; i < data_count; i++) { + for (uint32_t i = 0; i < geom.data_count; i++) { auto offset = i * new_vertex_size + sizeof(double) * 2; - memcpy(data.vertex_data + offset, &default_value, sizeof(double)); + memcpy(geom.data_ptr + offset, &default_value, sizeof(double)); } } // Case 3: The new vertex size is smaller than the old vertex size. // In this case we need to allocate new memory and copy the data over to not lose any data else { - auto new_data = alloc.AllocateAligned(data_count * new_vertex_size); - memset(new_data, 0, data_count * new_vertex_size); + auto new_data = alloc.AllocateAligned(geom.data_count * new_vertex_size); + memset(new_data, 0, geom.data_count * new_vertex_size); // Special case: If we go from XYZM to XYM, we need to slide the M value to the end of each vertex if (used_to_have_z && used_to_have_m && !has_z && has_m) { - for (uint32_t i = 0; i < data_count; i++) { + for (uint32_t i = 0; i < geom.data_count; i++) { auto old_offset = i * old_vertex_size; auto new_offset = i * new_vertex_size; - memcpy(new_data + new_offset, data.vertex_data + old_offset, sizeof(double) * 2); + memcpy(new_data + new_offset, geom.data_ptr + old_offset, sizeof(double) * 2); auto m_offset = old_offset + sizeof(double) * 3; - memcpy(new_data + new_offset + sizeof(double) * 2, data.vertex_data + m_offset, sizeof(double)); + memcpy(new_data + new_offset + sizeof(double) * 2, geom.data_ptr + m_offset, sizeof(double)); } } else { // Otherwise, we just copy the data over - for (uint32_t i = 0; i < data_count; i++) { + for (uint32_t i = 0; i < geom.data_count; i++) { auto old_offset = i * old_vertex_size; auto new_offset = i * new_vertex_size; - memcpy(new_data + new_offset, data.vertex_data + old_offset, new_vertex_size); + memcpy(new_data + new_offset, geom.data_ptr + old_offset, new_vertex_size); } } - data.vertex_data = new_data; + geom.data_ptr = new_data; } } -void SinglePartGeometry::MakeMutable(ArenaAllocator &alloc) { - if (!IsReadOnly()) { +void SinglePartGeometry::MakeMutable(Geometry &geom, ArenaAllocator &alloc) { + if (!geom.is_readonly) { return; } - if (data_count == 0) { - data.vertex_data = nullptr; - is_readonly = false; + + if (geom.data_count == 0) { + geom.data_ptr = nullptr; + geom.is_readonly = false; return; } - auto new_data = alloc.AllocateAligned(ByteSize()); - memcpy(new_data, data.vertex_data, ByteSize()); - data.vertex_data = new_data; - is_readonly = false; + auto data_size = ByteSize(geom); + auto new_data = alloc.AllocateAligned(data_size); + memcpy(new_data, geom.data_ptr, data_size); + geom.data_ptr = new_data; + geom.is_readonly = false; } -bool SinglePartGeometry::IsClosed() const { - switch (Count()) { +bool SinglePartGeometry::IsClosed(const Geometry &geom) { + switch (geom.Count()) { case 0: return false; case 1: return true; default: - auto first = Get(0); - auto last = Get(Count() - 1); + VertexXY first = GetVertex(geom, 0); + VertexXY last = GetVertex(geom, geom.Count() - 1); // TODO: Approximate comparison? return first.x == last.x && first.y == last.y; } } -double SinglePartGeometry::Length() const { +double SinglePartGeometry::Length(const Geometry& geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); double length = 0; - for (uint32_t i = 1; i < Count(); i++) { - auto p1 = Get(i - 1); - auto p2 = Get(i); + for (uint32_t i = 1; i < geom.data_count; i++) { + auto p1 = GetVertex(geom, i - 1); + auto p2 = GetVertex(geom, i); length += sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)); } return length; } -string SinglePartGeometry::ToString(uint32_t start, uint32_t count) const { - auto has_z = properties.HasZ(); - auto has_m = properties.HasM(); +string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32_t count) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); + auto has_z = geom.properties.HasZ(); + auto has_m = geom.properties.HasM(); - D_ASSERT(type == GeometryType::POINT || type == GeometryType::LINESTRING); - auto type_name = type == GeometryType::POINT ? "POINT" : "LINESTRING"; + D_ASSERT(geom.type == GeometryType::POINT || geom.type == GeometryType::LINESTRING); + auto type_name = geom.type == GeometryType::POINT ? "POINT" : "LINESTRING"; if (has_z && has_m) { - string result = StringUtil::Format("%s XYZM ([%d-%d]/%d) [", type_name, start, start + count, data_count); + string result = StringUtil::Format("%s XYZM ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { - auto vertex = GetExact(i); + auto vertex = GetVertex(geom, i); result += StringUtil::Format("(%f, %f, %f, %f)", vertex.x, vertex.y, vertex.z, vertex.m); if (i < count - 1) { result += ", "; @@ -256,9 +268,9 @@ string SinglePartGeometry::ToString(uint32_t start, uint32_t count) const { result += "]"; return result; } else if (has_z) { - string result = StringUtil::Format("%s XYZ ([%d-%d]/%d) [", type_name, start, start + count, data_count); + string result = StringUtil::Format("%s XYZ ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { - auto vertex = GetExact(i); + auto vertex = GetVertex(geom, i); result += StringUtil::Format("(%f, %f, %f)", vertex.x, vertex.y, vertex.z); if (i < count - 1) { result += ", "; @@ -267,9 +279,9 @@ string SinglePartGeometry::ToString(uint32_t start, uint32_t count) const { result += "]"; return result; } else if (has_m) { - string result = StringUtil::Format("%s XYM ([%d-%d]/%d) [", type_name, start, start + count, data_count); + string result = StringUtil::Format("%s XYM ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { - auto vertex = GetExact(i); + auto vertex = GetVertex(geom, i); result += StringUtil::Format("(%f, %f, %f)", vertex.x, vertex.y, vertex.m); if (i < count - 1) { result += ", "; @@ -278,9 +290,9 @@ string SinglePartGeometry::ToString(uint32_t start, uint32_t count) const { result += "]"; return result; } else { - string result = StringUtil::Format("%s XY ([%d-%d]/%d) [", type_name, start, start + count, data_count); + string result = StringUtil::Format("%s XY ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { - auto vertex = GetExact(i); + auto vertex = GetVertex(geom, i); result += StringUtil::Format("(%f, %f)", vertex.x, vertex.y); if (i < count - 1) { result += ", "; @@ -291,22 +303,110 @@ string SinglePartGeometry::ToString(uint32_t start, uint32_t count) const { } } +bool Geometry::IsEmpty(const Geometry &geom) { + struct op { + static bool Case (Geometry::Tags::SinglePartGeometry, const Geometry &geom) { + return geom.data_count == 0; + } + static bool Case (Geometry::Tags::MultiPartGeometry, const Geometry &geom) { + for(const auto &p : MultiPartGeometry::Parts(geom)) { + if (!Geometry::IsEmpty(p)) { + return false; + } + } + return true; + } + }; + return Geometry::Visit(geom); +} + +uint32_t Geometry::GetDimension(const Geometry &geom, bool ignore_empty) { + if (ignore_empty && Geometry::IsEmpty(geom)) { + return 0; + } + struct op { + static uint32_t Case(Geometry::Tags::Point, const Geometry&, bool) { + return 0; + } + static uint32_t Case(Geometry::Tags::LineString, const Geometry&, bool) { + return 1; + } + static uint32_t Case(Geometry::Tags::Polygon, const Geometry&, bool) { + return 2; + } + static uint32_t Case(Geometry::Tags::MultiPoint, const Geometry&, bool) { + return 0; + } + static uint32_t Case(Geometry::Tags::MultiLineString, const Geometry&, bool) { + return 1; + } + static uint32_t Case(Geometry::Tags::MultiPolygon, const Geometry&, bool) { + return 2; + } + static uint32_t Case(Geometry::Tags::GeometryCollection, const Geometry& geom, bool ignore_empty) { + uint32_t max_dimension = 0; + for(const auto &p : GeometryCollection::Parts(geom)) { + max_dimension = std::max(max_dimension, Geometry::GetDimension(p, ignore_empty)); + } + return max_dimension; + } + }; + return Geometry::Visit(geom, ignore_empty); +} + +void Geometry::SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { + struct op { + static void Case(Geometry::Tags::SinglePartGeometry, Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { + SinglePartGeometry::SetVertexType(geom, alloc, has_z, has_m, default_z, default_m); + } + static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { + geom.properties.SetZ(has_z); + geom.properties.SetM(has_m); + for(auto &p : MultiPartGeometry::Parts(geom)) { + p.SetVertexType(alloc, has_z, has_m, default_z, default_m); + } + } + }; + Geometry::Visit(*this, alloc, has_z, has_m, default_z, default_m); +} + //------------------------------------------------------------------------------ // Multi Part Geometry //------------------------------------------------------------------------------ - -void MultiPartGeometry::Resize(ArenaAllocator &alloc, uint32_t new_count) { - if (new_count == data_count) { +/* +void MultiPartGeometry::Resize(Geometry& geom, ArenaAllocator &alloc, uint32_t new_count) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.type)); + if (new_count == geom.data_count) { return; } - if (data.part_data == nullptr) { - data.part_data = reinterpret_cast(alloc.AllocateAligned(sizeof(Geometry) * new_count)); - } else { - data.part_data = reinterpret_cast(alloc.ReallocateAligned( - data_ptr_cast(data.part_data), data_count * sizeof(Geometry), new_count * sizeof(Geometry))); + if (geom.data_ptr == nullptr) { + geom.data_ptr = alloc.AllocateAligned(sizeof(Geometry) * new_count); + // Need to create a new Geometry for each entry + for (uint32_t i = 0; i < new_count; i++) { + new (geom.data_ptr + i * sizeof(Geometry)) Geometry(); + } + } + else if(geom.IsReadOnly()) { + auto new_data = alloc.AllocateAligned(sizeof(Geometry) * new_count); + for(uint32_t i = 0; i < geom.data_count; i++) { + new (new_data + i * sizeof(Geometry)) Geometry(); + new_data[i] = geom.data_ptr[i]; + } + + + geom.data_ptr = new_data; + } + else { + geom.data_ptr = alloc.ReallocateAligned( + geom.data_ptr, geom.data_count * sizeof(Geometry), new_count * sizeof(Geometry)); + // If we added new entries, we need to create a new Geometry for each entry + for (uint32_t i = geom.data_count; i < new_count; i++) { + new (geom.data_ptr + i * sizeof(Geometry)) Geometry(); + } } - data_count = new_count; + geom.data_count = new_count; } + */ /* string Point::ToString() const { @@ -484,52 +584,6 @@ string GeometryCollection::ToString() const { } */ -//------------------------------------------------------------------------------ -// Util -//------------------------------------------------------------------------------ -// We've got this exposed upstream, we just need to wait for the next release -extern "C" int geos_d2sfixed_buffered_n(double f, uint32_t precision, char *result); - -string Utils::format_coord(double d) { - char buf[25]; - auto len = geos_d2sfixed_buffered_n(d, 15, buf); - buf[len] = '\0'; - return string {buf}; -} - -string Utils::format_coord(double x, double y) { - char buf[51]; - auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); - buf[res_x++] = ' '; - auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); - buf[res_x + res_y] = '\0'; - return string {buf}; -} - -string Utils::format_coord(double x, double y, double zm) { - char buf[76]; - auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); - buf[res_x++] = ' '; - auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); - buf[res_x + res_y++] = ' '; - auto res_zm = geos_d2sfixed_buffered_n(zm, 15, buf + res_x + res_y); - buf[res_x + res_y + res_zm] = '\0'; - return string {buf}; -} - -string Utils::format_coord(double x, double y, double z, double m) { - char buf[101]; - auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); - buf[res_x++] = ' '; - auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); - buf[res_x + res_y++] = ' '; - auto res_z = geos_d2sfixed_buffered_n(z, 15, buf + res_x + res_y); - buf[res_x + res_y + res_z++] = ' '; - auto res_m = geos_d2sfixed_buffered_n(m, 15, buf + res_x + res_y + res_z); - buf[res_x + res_y + res_z + res_m] = '\0'; - return string {buf}; -} - } // namespace core } // namespace spatial diff --git a/spatial/src/spatial/core/geometry/geometry_serialization.cpp b/spatial/src/spatial/core/geometry/geometry_serialization.cpp index dd93a047..02999866 100644 --- a/spatial/src/spatial/core/geometry/geometry_serialization.cpp +++ b/spatial/src/spatial/core/geometry/geometry_serialization.cpp @@ -1,7 +1,8 @@ #include "spatial/common.hpp" -#include "spatial/core/geometry/cursor.hpp" +#include "spatial/core/util/cursor.hpp" #include "spatial/core/geometry/geometry.hpp" #include "spatial/core/geometry/geometry_processor.hpp" +#include "spatial/core/util/math.hpp" namespace spatial { @@ -37,14 +38,14 @@ namespace core { template struct GetRequiredSizeOp { - static uint32_t Apply(const SinglePartGeometry &geom) { + static uint32_t Case(Geometry::Tags::SinglePartGeometry, const Geometry &geom) { // 4 bytes for the type // 4 bytes for the length // sizeof(vertex) * count return 4 + 4 + (geom.Count() * sizeof(VERTEX)); } - static uint32_t Apply(const Polygon &polygon) { + static uint32_t Case(Geometry::Tags::Polygon, const Geometry &polygon) { // Polygons are special because they may pad between the rings and the ring data // 4 bytes for the type // 4 bytes for the number of rings @@ -52,36 +53,25 @@ struct GetRequiredSizeOp { // - sizeof(vertex) * count for each ring // (+ 4 bytes for padding if num_rings is odd) uint32_t size = 4 + 4; - for (const auto &ring : polygon) { - size += 4; - size += ring.Count() * sizeof(VERTEX); - } + for(uint32_t i = 0; i < Polygon::PartCount(polygon); i++) { + size += 4; + size += Polygon::Part(polygon, i).Count() * sizeof(VERTEX); + } if (polygon.Count() % 2 == 1) { size += 4; } return size; } - template - static uint32_t Apply(const TypedCollectionGeometry &collection) { + static uint32_t Case(Geometry::Tags::CollectionGeometry, const Geometry &collection) { // 4 bytes for the type // 4 bytes for the number of items // recursive call for each item uint32_t size = 4 + 4; - for (const auto &item : collection) { - size += Apply(item); - } - return size; - } - - static uint32_t Apply(const GeometryCollection &collection) { - // 4 bytes for the type - // 4 bytes for the number of geometries - // sizeof(geometry) * count - uint32_t size = 4 + 4; - for (const auto &geom : collection) { - size += geom.Visit>(); - } + for(uint32_t i = 0; i < CollectionGeometry::PartCount(collection); i++) { + auto &part = CollectionGeometry::Part(collection, i); + size += Geometry::Visit>(part); + } return size; } }; @@ -90,23 +80,23 @@ template struct SerializeOp { static constexpr uint32_t MAX_DEPTH = 256; - static void SerializeVertices(const SinglePartGeometry &verts, Cursor &cursor, BoundingBox &bbox, + static void SerializeVertices(const Geometry &verts, Cursor &cursor, BoundingBox &bbox, bool update_bounds) { // Write the vertex data - auto byte_size = verts.ByteSize(); + auto byte_size = SinglePartGeometry::ByteSize(verts); memcpy(cursor.GetPtr(), verts.GetData(), byte_size); // Move the cursor forward cursor.Skip(byte_size); // Also update the bounds real quick if (update_bounds) { for (uint32_t i = 0; i < verts.Count(); i++) { - auto vertex = verts.GetExact(i); + auto vertex = SinglePartGeometry::GetVertex(verts, i); bbox.Stretch(vertex); } } } - static void Apply(const Point &point, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::Point, const Geometry &point, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { D_ASSERT(point.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(point.GetProperties().HasM() == VERTEX::HAS_M); @@ -121,7 +111,7 @@ struct SerializeOp { SerializeVertices(point, cursor, bbox, depth != 0); } - static void Apply(const LineString &linestring, Cursor &cursor, BoundingBox &bbox, uint32_t) { + static void Case(Geometry::Tags::LineString, const Geometry &linestring, Cursor &cursor, BoundingBox &bbox, uint32_t) { D_ASSERT(linestring.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(linestring.GetProperties().HasM() == VERTEX::HAS_M); @@ -135,7 +125,7 @@ struct SerializeOp { SerializeVertices(linestring, cursor, bbox, true); } - static void Apply(const Polygon &polygon, Cursor &cursor, BoundingBox &bbox, uint32_t) { + static void Case(Geometry::Tags::Polygon, const Geometry &polygon, Cursor &cursor, BoundingBox &bbox, uint32_t) { D_ASSERT(polygon.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(polygon.GetProperties().HasM() == VERTEX::HAS_M); @@ -146,9 +136,9 @@ struct SerializeOp { cursor.Write(polygon.Count()); // Write ring lengths - for (const auto &ring : polygon) { - cursor.Write(ring.Count()); - } + for(uint32_t i = 0; i < Polygon::PartCount(polygon); i++) { + cursor.Write(Polygon::Part(polygon, i).Count()); + } if (polygon.Count() % 2 == 1) { // Write padding (4 bytes) @@ -159,11 +149,11 @@ struct SerializeOp { for (uint32_t i = 0; i < polygon.Count(); i++) { // The first ring is always the shell, and must be the only ring contributing to the bounding box // or the geometry is invalid. - SerializeVertices(polygon[i], cursor, bbox, i == 0); + SerializeVertices(Polygon::Part(polygon, i), cursor, bbox, i == 0); } } - static void Apply(const MultiPoint &multipoint, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::MultiPoint, const Geometry &multipoint, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { D_ASSERT(multipoint.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(multipoint.GetProperties().HasM() == VERTEX::HAS_M); @@ -174,12 +164,12 @@ struct SerializeOp { cursor.Write(multipoint.Count()); // Write point data - for (const auto &point : multipoint) { - Apply(point, cursor, bbox, depth + 1); - } + for (uint32_t i = 0; i < MultiPoint::PartCount(multipoint); i++) { + Case(Geometry::Tags::Point{}, MultiPoint::Part(multipoint, i), cursor, bbox, depth + 1); + } } - static void Apply(const MultiLineString &multilinestring, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::MultiLineString, const Geometry &multilinestring, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { D_ASSERT(multilinestring.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(multilinestring.GetProperties().HasM() == VERTEX::HAS_M); @@ -190,12 +180,12 @@ struct SerializeOp { cursor.Write(multilinestring.Count()); // Write linestring data - for (const auto &linestring : multilinestring) { - Apply(linestring, cursor, bbox, depth + 1); - } + for(uint32_t i = 0; i < MultiLineString::PartCount(multilinestring); i++) { + Case(Geometry::Tags::LineString{}, MultiLineString::Part(multilinestring, i), cursor, bbox, depth + 1); + } } - static void Apply(const MultiPolygon &multipolygon, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::MultiPolygon, const Geometry &multipolygon, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { D_ASSERT(multipolygon.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(multipolygon.GetProperties().HasM() == VERTEX::HAS_M); @@ -206,12 +196,12 @@ struct SerializeOp { cursor.Write(multipolygon.Count()); // Write polygon data - for (const auto &polygon : multipolygon) { - Apply(polygon, cursor, bbox, depth + 1); - } + for(uint32_t i = 0; i < MultiPolygon::PartCount(multipolygon); i++) { + Case(Geometry::Tags::Polygon{}, MultiPolygon::Part(multipolygon, i), cursor, bbox, depth + 1); + } } - static void Apply(const GeometryCollection &collection, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::GeometryCollection, const Geometry &collection, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { D_ASSERT(collection.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(collection.GetProperties().HasM() == VERTEX::HAS_M); @@ -227,30 +217,31 @@ struct SerializeOp { cursor.Write(collection.Count()); // write geometry data - for (const auto &geom : collection) { - geom.Visit>(cursor, bbox, depth + 1); - } + for(uint32_t i = 0; i < GeometryCollection::PartCount(collection); i++) { + auto &geom = GeometryCollection::Part(collection, i); + Geometry::Visit>(geom, cursor, bbox, depth + 1); + } } }; -geometry_t Geometry::Serialize(Vector &result) { - auto type = GetType(); - bool has_bbox = type != GeometryType::POINT && !IsEmpty(); +geometry_t Geometry::Serialize(const Geometry &geom, Vector &result) { + auto type = geom.GetType(); + bool has_bbox = type != GeometryType::POINT && !Geometry::IsEmpty(geom); - auto properties = GetProperties(); + auto properties = geom.GetProperties(); auto has_z = properties.HasZ(); auto has_m = properties.HasM(); properties.SetBBox(has_bbox); uint32_t geom_size = 0; if (has_z && has_m) { - geom_size = Visit>(); + geom_size = Geometry::Visit>(geom); } else if (has_z) { - geom_size = Visit>(); + geom_size = Geometry::Visit>(geom); } else if (has_m) { - geom_size = Visit>(); + geom_size = Geometry::Visit>(geom); } else { - geom_size = Visit>(); + geom_size = Geometry::Visit>(geom); } auto header_size = 4; @@ -277,13 +268,13 @@ geometry_t Geometry::Serialize(Vector &result) { cursor.Skip(bbox_size); if (has_z && has_m) { - Visit>(cursor, bbox, 0); + Geometry::Visit>(geom, cursor, bbox, 0); } else if (has_z) { - Visit>(cursor, bbox, 0); + Geometry::Visit>(geom, cursor, bbox, 0); } else if (has_m) { - Visit>(cursor, bbox, 0); + Geometry::Visit>(geom, cursor, bbox, 0); } else { - Visit>(cursor, bbox, 0); + Geometry::Visit>(geom, cursor, bbox, 0); } // Now write the bounding box @@ -291,17 +282,17 @@ geometry_t Geometry::Serialize(Vector &result) { cursor.SetPtr(bbox_ptr); // We serialize the bounding box as floats to save space, but ensure that the bounding box is // still large enough to contain the original double values by rounding up and down - cursor.Write(Utils::DoubleToFloatDown(bbox.minx)); - cursor.Write(Utils::DoubleToFloatDown(bbox.miny)); - cursor.Write(Utils::DoubleToFloatUp(bbox.maxx)); - cursor.Write(Utils::DoubleToFloatUp(bbox.maxy)); + cursor.Write(MathUtil::DoubleToFloatDown(bbox.minx)); + cursor.Write(MathUtil::DoubleToFloatDown(bbox.miny)); + cursor.Write(MathUtil::DoubleToFloatUp(bbox.maxx)); + cursor.Write(MathUtil::DoubleToFloatUp(bbox.maxy)); if (has_z) { - cursor.Write(Utils::DoubleToFloatDown(bbox.minz)); - cursor.Write(Utils::DoubleToFloatUp(bbox.maxz)); + cursor.Write(MathUtil::DoubleToFloatDown(bbox.minz)); + cursor.Write(MathUtil::DoubleToFloatUp(bbox.maxz)); } if (has_m) { - cursor.Write(Utils::DoubleToFloatDown(bbox.minm)); - cursor.Write(Utils::DoubleToFloatUp(bbox.maxm)); + cursor.Write(MathUtil::DoubleToFloatDown(bbox.minm)); + cursor.Write(MathUtil::DoubleToFloatUp(bbox.maxm)); } } blob.Finalize(); @@ -315,9 +306,9 @@ class GeometryDeserializer final : GeometryProcessor { ArenaAllocator &allocator; Geometry ProcessPoint(const VertexData &vertices) override { - auto point = Point::Empty(HasZ(), HasM()); + auto point = Point::CreateEmpty(HasZ(), HasM()); if (!vertices.IsEmpty()) { - point.ReferenceData(vertices.data[0], vertices.count); + Point::ReferenceData(point, vertices.data[0], vertices.count); } return point; } @@ -325,7 +316,7 @@ class GeometryDeserializer final : GeometryProcessor { Geometry ProcessLineString(const VertexData &vertices) override { auto line_string = LineString::Create(allocator, vertices.count, HasZ(), HasM()); if (!vertices.IsEmpty()) { - line_string.ReferenceData(vertices.data[0], vertices.count); + LineString::ReferenceData(line_string, vertices.data[0], vertices.count); } return line_string; } @@ -335,7 +326,8 @@ class GeometryDeserializer final : GeometryProcessor { for (auto i = 0; i < state.RingCount(); i++) { auto vertices = state.Next(); if (!vertices.IsEmpty()) { - polygon[i].ReferenceData(vertices.data[0], vertices.count); + auto &part = Polygon::Part(polygon, i); + LineString::ReferenceData(part, vertices.data[0], vertices.count); } } return polygon; @@ -346,28 +338,28 @@ class GeometryDeserializer final : GeometryProcessor { case GeometryType::MULTIPOINT: { auto multi_point = MultiPoint::Create(allocator, state.ItemCount(), HasZ(), HasM()); for (auto i = 0; i < state.ItemCount(); i++) { - multi_point[i] = state.Next().As(); + MultiPoint::Part(multi_point, i) = state.Next(); } return multi_point; } case GeometryType::MULTILINESTRING: { auto multi_line_string = MultiLineString::Create(allocator, state.ItemCount(), HasZ(), HasM()); for (auto i = 0; i < state.ItemCount(); i++) { - multi_line_string[i] = state.Next().As(); + MultiLineString::Part(multi_line_string, i) = state.Next(); } return multi_line_string; } case GeometryType::MULTIPOLYGON: { auto multi_polygon = MultiPolygon::Create(allocator, state.ItemCount(), HasZ(), HasM()); for (auto i = 0; i < state.ItemCount(); i++) { - multi_polygon[i] = state.Next().As(); + MultiPolygon::Part(multi_polygon, i) = state.Next(); } return multi_polygon; } case GeometryType::GEOMETRYCOLLECTION: { auto collection = GeometryCollection::Create(allocator, state.ItemCount(), HasZ(), HasM()); for (auto i = 0; i < state.ItemCount(); i++) { - collection[i] = state.Next(); + GeometryCollection::Part(collection, i) = state.Next(); } return collection; } diff --git a/spatial/src/spatial/core/geometry/wkb_reader.cpp b/spatial/src/spatial/core/geometry/wkb_reader.cpp index b7aea485..6109b722 100644 --- a/spatial/src/spatial/core/geometry/wkb_reader.cpp +++ b/spatial/src/spatial/core/geometry/wkb_reader.cpp @@ -19,7 +19,7 @@ Geometry WKBReader::Deserialize(const_data_ptr_t wkb, uint32_t size) { auto geom = ReadGeometry(cursor); // Make sure the geometry has unified vertex type, in case we got some funky nested WKB with mixed dimensions - geom.SetVertexType(arena, has_any_z, has_any_m); + geom.SetVertexType(arena, has_any_z, has_any_m); return geom; } @@ -68,7 +68,7 @@ WKBReader::WKBType WKBReader::ReadType(Cursor &cursor, bool little_endian) { return {geometry_type, has_z, has_m}; } -Point WKBReader::ReadPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { +Geometry WKBReader::ReadPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { uint32_t dims = 2 + has_z + has_m; bool all_nan = true; double coords[4]; @@ -79,94 +79,94 @@ Point WKBReader::ReadPoint(Cursor &cursor, bool little_endian, bool has_z, bool } } if (all_nan) { - return Point::Empty(has_z, has_m); + return Point::CreateEmpty(has_z, has_m); } else { - return Point::CopyFromData(arena, data_ptr_cast(coords), 1, has_z, has_m); + return Point::CreateFromCopy(arena, data_ptr_cast(coords), 1, has_z, has_m); } } -void WKBReader::ReadVertices(Cursor &cursor, bool little_endian, bool has_z, bool has_m, SinglePartGeometry &geometry) { +void WKBReader::ReadVertices(Cursor &cursor, bool little_endian, bool has_z, bool has_m, Geometry &geometry) { for (uint32_t i = 0; i < geometry.Count(); i++) { if (has_z && has_m) { auto x = ReadDouble(cursor, little_endian); auto y = ReadDouble(cursor, little_endian); auto z = ReadDouble(cursor, little_endian); auto m = ReadDouble(cursor, little_endian); - geometry.SetExact(i, VertexXYZM {x, y, z, m}); + SinglePartGeometry::SetVertex(geometry, i, VertexXYZM {x, y, z, m}); } else if (has_z) { auto x = ReadDouble(cursor, little_endian); auto y = ReadDouble(cursor, little_endian); auto z = ReadDouble(cursor, little_endian); - geometry.SetExact(i, VertexXYZ {x, y, z}); + SinglePartGeometry::SetVertex(geometry, i, VertexXYZ {x, y, z}); } else if (has_m) { auto x = ReadDouble(cursor, little_endian); auto y = ReadDouble(cursor, little_endian); auto m = ReadDouble(cursor, little_endian); - geometry.SetExact(i, VertexXYM {x, y, m}); + SinglePartGeometry::SetVertex(geometry, i, VertexXYM {x, y, m}); } else { auto x = ReadDouble(cursor, little_endian); auto y = ReadDouble(cursor, little_endian); - geometry.SetExact(i, VertexXY {x, y}); + SinglePartGeometry::SetVertex(geometry, i, VertexXY {x, y}); } } } -LineString WKBReader::ReadLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { +Geometry WKBReader::ReadLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { auto count = ReadInt(cursor, little_endian); auto vertices = LineString::Create(arena, count, has_z, has_m); ReadVertices(cursor, little_endian, has_z, has_m, vertices); return vertices; } -Polygon WKBReader::ReadPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { +Geometry WKBReader::ReadPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { auto ring_count = ReadInt(cursor, little_endian); auto polygon = Polygon::Create(arena, ring_count, has_z, has_m); for (uint32_t i = 0; i < ring_count; i++) { auto point_count = ReadInt(cursor, little_endian); - polygon[i].Resize(arena, point_count); - ReadVertices(cursor, little_endian, has_z, has_m, polygon[i]); + Polygon::Part(polygon, i) = LineString::Create(arena, point_count, has_z, has_m); + ReadVertices(cursor, little_endian, has_z, has_m, Polygon::Part(polygon, i)); } return polygon; } -MultiPoint WKBReader::ReadMultiPoint(Cursor &cursor, bool little_endian) { +Geometry WKBReader::ReadMultiPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { uint32_t count = ReadInt(cursor, little_endian); - auto multi_point = MultiPoint::Create(arena, count, false, false); + auto multi_point = MultiPoint::Create(arena, count, has_z, has_m); for (uint32_t i = 0; i < count; i++) { bool point_order = cursor.Read(); auto point_type = ReadType(cursor, point_order); - multi_point[i] = ReadPoint(cursor, point_order, point_type.has_z, point_type.has_m); + MultiPoint::Part(multi_point, i) = ReadPoint(cursor, point_order, point_type.has_z, point_type.has_m); } return multi_point; } -MultiLineString WKBReader::ReadMultiLineString(Cursor &cursor, bool little_endian) { +Geometry WKBReader::ReadMultiLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { uint32_t count = ReadInt(cursor, little_endian); - auto multi_line_string = MultiLineString::Create(arena, count, false, false); + auto multi_line_string = MultiLineString::Create(arena, count, has_z, has_m); for (uint32_t i = 0; i < count; i++) { bool line_order = cursor.Read(); auto line_type = ReadType(cursor, line_order); - multi_line_string[i] = ReadLineString(cursor, line_order, line_type.has_z, line_type.has_m); + MultiLineString::Part(multi_line_string, i) = ReadLineString(cursor, line_order, line_type.has_z, line_type.has_m); } return multi_line_string; } -MultiPolygon WKBReader::ReadMultiPolygon(Cursor &cursor, bool little_endian) { +Geometry WKBReader::ReadMultiPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { uint32_t count = ReadInt(cursor, little_endian); - auto multi_polygon = MultiPolygon::Create(arena, count, false, false); + auto multi_polygon = MultiPolygon::Create(arena, count, has_z, has_m); for (uint32_t i = 0; i < count; i++) { bool polygon_order = cursor.Read(); auto polygon_type = ReadType(cursor, polygon_order); - multi_polygon[i] = ReadPolygon(cursor, polygon_order, polygon_type.has_z, polygon_type.has_m); + MultiPolygon::Part(multi_polygon, i) = ReadPolygon(cursor, polygon_order, polygon_type.has_z, polygon_type.has_m); } return multi_polygon; } -GeometryCollection WKBReader::ReadGeometryCollection(Cursor &cursor, bool byte_order) { - uint32_t count = ReadInt(cursor, byte_order); - auto geometry_collection = GeometryCollection::Create(arena, count, false, false); +Geometry WKBReader::ReadGeometryCollection(Cursor &cursor, bool little_endian, bool has_z, bool has_m) { + uint32_t count = ReadInt(cursor, little_endian); + auto geometry_collection = GeometryCollection::Create(arena, count, has_z, has_m); for (uint32_t i = 0; i < count; i++) { - geometry_collection[i] = ReadGeometry(cursor); + GeometryCollection::Part(geometry_collection, i) = ReadGeometry(cursor); } return geometry_collection; } @@ -182,13 +182,13 @@ Geometry WKBReader::ReadGeometry(Cursor &cursor) { case GeometryType::POLYGON: return ReadPolygon(cursor, little_endian, type.has_z, type.has_m); case GeometryType::MULTIPOINT: - return ReadMultiPoint(cursor, little_endian); + return ReadMultiPoint(cursor, little_endian, type.has_z, type.has_m); case GeometryType::MULTILINESTRING: - return ReadMultiLineString(cursor, little_endian); + return ReadMultiLineString(cursor, little_endian, type.has_z, type.has_m); case GeometryType::MULTIPOLYGON: - return ReadMultiPolygon(cursor, little_endian); + return ReadMultiPolygon(cursor, little_endian, type.has_z, type.has_m); case GeometryType::GEOMETRYCOLLECTION: - return ReadGeometryCollection(cursor, little_endian); + return ReadGeometryCollection(cursor, little_endian, type.has_z, type.has_m); default: throw NotImplementedException("WKB Reader: Geometry type %u not supported", type.type); } diff --git a/spatial/src/spatial/core/geometry/wkt_reader.cpp b/spatial/src/spatial/core/geometry/wkt_reader.cpp index 99740969..21287c2d 100644 --- a/spatial/src/spatial/core/geometry/wkt_reader.cpp +++ b/spatial/src/spatial/core/geometry/wkt_reader.cpp @@ -125,25 +125,25 @@ pair> WKTReader::ParseVertices() { return {count, coords}; } -Point WKTReader::ParsePoint() { +Geometry WKTReader::ParsePoint() { if (MatchCI("EMPTY")) { - return Point::Empty(has_z, has_m); + return Point::CreateEmpty(has_z, has_m); } Expect('('); vector coords; ParseVertex(coords); Expect(')'); - return Point::CopyFromData(arena, data_ptr_cast(coords.data()), 1, has_z, has_m); + return Point::CreateFromCopy(arena, data_ptr_cast(coords.data()), 1, has_z, has_m); } -LineString WKTReader::ParseLineString() { +Geometry WKTReader::ParseLineString() { auto verts = ParseVertices(); - return LineString::CopyFromData(arena, data_ptr_cast(verts.second.data()), verts.first, has_z, has_m); + return LineString::CreateFromCopy(arena, data_ptr_cast(verts.second.data()), verts.first, has_z, has_m); } -Polygon WKTReader::ParsePolygon() { +Geometry WKTReader::ParsePolygon() { if (MatchCI("EMPTY")) { - return Polygon::Empty(has_z, has_m); + return Polygon::CreateEmpty(has_z, has_m); } Expect('('); vector>> rings; @@ -154,19 +154,20 @@ Polygon WKTReader::ParsePolygon() { Expect(')'); auto result = Polygon::Create(arena, rings.size(), has_z, has_m); for (uint32_t i = 0; i < rings.size(); i++) { - result[i].CopyData(arena, data_ptr_cast(rings[i].second.data()), rings[i].first); + auto &ring = Polygon::Part(result, i); + LineString::CopyData(ring, arena, data_ptr_cast(rings[i].second.data()), rings[i].first); } return result; } -MultiPoint WKTReader::ParseMultiPoint() { +Geometry WKTReader::ParseMultiPoint() { if (MatchCI("EMPTY")) { - return MultiPoint::Empty(has_z, has_m); + return MultiPoint::CreateEmpty(has_z, has_m); } // Multipoints are special in that parens around each point is optional. Expect('('); vector coords; - vector points; + vector points; bool optional_paren = false; if (Match('(')) { @@ -177,7 +178,7 @@ MultiPoint WKTReader::ParseMultiPoint() { Expect(')'); optional_paren = false; } - points.push_back(Point::CopyFromData(arena, data_ptr_cast(coords.data()), 1, has_z, has_m)); + points.push_back(Point::CreateFromCopy(arena, data_ptr_cast(coords.data()), 1, has_z, has_m)); coords.clear(); while (Match(',')) { if (Match('(')) { @@ -188,23 +189,23 @@ MultiPoint WKTReader::ParseMultiPoint() { Expect(')'); optional_paren = false; } - points.push_back(Point::CopyFromData(arena, data_ptr_cast(coords.data()), 1, has_z, has_m)); + points.push_back(Point::CreateFromCopy(arena, data_ptr_cast(coords.data()), 1, has_z, has_m)); coords.clear(); } Expect(')'); auto result = MultiPoint::Create(arena, points.size(), has_z, has_m); for (uint32_t i = 0; i < points.size(); i++) { - result[i] = points[i]; + MultiPoint::Part(result, i) = points[i]; } return result; } -MultiLineString WKTReader::ParseMultiLineString() { +Geometry WKTReader::ParseMultiLineString() { if (MatchCI("EMPTY")) { - return MultiLineString::Empty(has_z, has_m); + return MultiLineString::CreateEmpty(has_z, has_m); } Expect('('); - vector lines; + vector lines; lines.push_back(ParseLineString()); while (Match(',')) { lines.push_back(ParseLineString()); @@ -212,17 +213,17 @@ MultiLineString WKTReader::ParseMultiLineString() { Expect(')'); auto result = MultiLineString::Create(arena, lines.size(), has_z, has_m); for (uint32_t i = 0; i < lines.size(); i++) { - result[i] = lines[i]; + MultiLineString::Part(result, i) = lines[i]; } return result; } -MultiPolygon WKTReader::ParseMultiPolygon() { +Geometry WKTReader::ParseMultiPolygon() { if (MatchCI("EMPTY")) { - return MultiPolygon::Empty(has_z, has_m); + return MultiPolygon::CreateEmpty(has_z, has_m); } Expect('('); - vector polygons; + vector polygons; polygons.push_back(ParsePolygon()); while (Match(',')) { polygons.push_back(ParsePolygon()); @@ -230,14 +231,14 @@ MultiPolygon WKTReader::ParseMultiPolygon() { Expect(')'); auto result = MultiPolygon::Create(arena, polygons.size(), has_z, has_m); for (uint32_t i = 0; i < polygons.size(); i++) { - result[i] = polygons[i]; + MultiPolygon::Part(result, i) = polygons[i]; } return result; } -GeometryCollection WKTReader::ParseGeometryCollection() { +Geometry WKTReader::ParseGeometryCollection() { if (MatchCI("EMPTY")) { - return GeometryCollection::Empty(has_z, has_m); + return GeometryCollection::CreateEmpty(has_z, has_m); } Expect('('); vector geometries; @@ -248,7 +249,7 @@ GeometryCollection WKTReader::ParseGeometryCollection() { Expect(')'); auto result = GeometryCollection::Create(arena, geometries.size(), has_z, has_m); for (uint32_t i = 0; i < geometries.size(); i++) { - result[i] = geometries[i]; + GeometryCollection::Part(result, i) = geometries[i]; } return result; } diff --git a/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp b/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp index fcc182ea..d635b4c3 100644 --- a/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp +++ b/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp @@ -208,7 +208,7 @@ static unique_ptr InitGlobal(ClientContext &context, T struct ConvertPoint { static Geometry Convert(SHPObjectPtr &shape, ArenaAllocator &arena) { - return Point::FromVertex(arena, VertexXY {shape->padfX[0], shape->padfY[0]}); + return Point::CreateFromVertex(arena, VertexXY {shape->padfX[0], shape->padfY[0]}); } }; @@ -218,7 +218,7 @@ struct ConvertLineString { // Single LineString auto line = LineString::Create(arena, shape->nVertices, false, false); for (int i = 0; i < shape->nVertices; i++) { - line.SetExact(i, VertexXY {shape->padfX[i], shape->padfY[i]}); + LineString::SetVertex(line, i, {shape->padfX[i], shape->padfY[i]}); } return line; } else { @@ -228,11 +228,11 @@ struct ConvertLineString { for (int i = 0; i < shape->nParts; i++) { auto end = i == shape->nParts - 1 ? shape->nVertices : shape->panPartStart[i + 1]; auto line_size = end - start; - auto &line = multi_line_string[i]; - line.Resize(arena, line_size); + auto &line = MultiLineString::Part(multi_line_string, i); + LineString::Resize(line, arena, line_size); for (int j = 0; j < line_size; j++) { auto offset = start + j; - line.SetExact(j, VertexXY {shape->padfX[offset], shape->padfY[offset]}); + LineString::SetVertex(line, j, {shape->padfX[offset], shape->padfY[offset]}); } start = end; } @@ -266,12 +266,12 @@ struct ConvertPolygon { auto start = shape->panPartStart[0]; for (int i = 0; i < shape->nParts; i++) { auto end = i == shape->nParts - 1 ? shape->nVertices : shape->panPartStart[i + 1]; - auto &ring = polygon[i]; + auto &ring = Polygon::Part(polygon, i); auto ring_size = end - start; - ring.Resize(arena, ring_size); + LineString::Resize(ring, arena, ring_size); for (int j = 0; j < ring_size; j++) { auto offset = start + j; - ring.Set(j, shape->padfX[offset], shape->padfY[offset]); + LineString::SetVertex(ring, j, {shape->padfX[offset], shape->padfY[offset]}); } start = end; } @@ -289,15 +289,15 @@ struct ConvertPolygon { for (auto ring_idx = part_start; ring_idx < part_end; ring_idx++) { auto start = shape->panPartStart[ring_idx]; auto end = ring_idx == shape->nParts - 1 ? shape->nVertices : shape->panPartStart[ring_idx + 1]; - auto &ring = polygon[ring_idx - part_start]; + auto &ring = Polygon::Part(polygon, ring_idx - part_start); auto ring_size = end - start; - ring.Resize(arena, ring_size); + LineString::Resize(ring, arena, ring_size); for (int j = 0; j < ring_size; j++) { auto offset = start + j; - ring.SetExact(j, VertexXY {shape->padfX[offset], shape->padfY[offset]}); + LineString::SetVertex(ring, j, {shape->padfX[offset], shape->padfY[offset]}); } } - multi_polygon[polygon_idx] = polygon; + MultiPolygon::Part(multi_polygon, polygon_idx) = std::move(polygon); } return multi_polygon; } @@ -308,7 +308,8 @@ struct ConvertMultiPoint { static Geometry Convert(SHPObjectPtr &shape, ArenaAllocator &arena) { auto multi_point = MultiPoint::Create(arena, shape->nVertices, false, false); for (int i = 0; i < shape->nVertices; i++) { - multi_point[i] = Point::FromVertex(arena, VertexXY {shape->padfX[i], shape->padfY[i]}); + auto point = Point::CreateFromVertex(arena, VertexXY {shape->padfX[i], shape->padfY[i]}); + MultiPoint::Part(multi_point, i) = std::move(point); } return multi_point; } @@ -323,7 +324,7 @@ static void ConvertGeomLoop(Vector &result, int record_start, idx_t count, SHPHa FlatVector::SetNull(result, result_idx, true); } else { // TODO: Handle Z and M - FlatVector::GetData(result)[result_idx] = Geometry(OP::Convert(shape, arena)).Serialize(result); + FlatVector::GetData(result)[result_idx] = Geometry::Serialize(OP::Convert(shape, arena), result); } } } diff --git a/spatial/src/spatial/core/io/shapefile/shapefile_common.cpp b/spatial/src/spatial/core/io/shapefile/shapefile_common.cpp index 00db00bc..2d3a99f1 100644 --- a/spatial/src/spatial/core/io/shapefile/shapefile_common.cpp +++ b/spatial/src/spatial/core/io/shapefile/shapefile_common.cpp @@ -18,7 +18,7 @@ namespace core { static SAFile DuckDBShapefileOpen(void *userData, const char *filename, const char *access_mode) { try { auto &fs = *reinterpret_cast(userData); - auto file_handle = fs.OpenFile(filename, FileFlags::FILE_FLAGS_READ); + auto file_handle = fs.OpenFile(filename, FileFlags::FILE_FLAGS_READ | FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS); if (!file_handle) { return nullptr; } @@ -87,7 +87,10 @@ static int DuckDBShapefileClose(SAFile file) { static int DuckDBShapefileRemove(void *userData, const char *filename) { try { auto &fs = *reinterpret_cast(userData); - auto file = fs.OpenFile(filename, FileFlags::FILE_FLAGS_WRITE); + auto file = fs.OpenFile(filename, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS); + if (!file) { + return -1; + } auto file_type = fs.GetFileType(*file); if (file_type == FileType::FILE_TYPE_DIR) { fs.RemoveDirectory(filename); diff --git a/spatial/src/spatial/core/util/math.cpp b/spatial/src/spatial/core/util/math.cpp new file mode 100644 index 00000000..b0f4952a --- /dev/null +++ b/spatial/src/spatial/core/util/math.cpp @@ -0,0 +1,52 @@ +#include "spatial/core/util/math.hpp" + +namespace spatial { + +namespace core { + +// We've got this exposed upstream, we just need to wait for the next release +extern "C" int geos_d2sfixed_buffered_n(double f, uint32_t precision, char *result); + +string MathUtil::format_coord(double d) { + char buf[25]; + auto len = geos_d2sfixed_buffered_n(d, 15, buf); + buf[len] = '\0'; + return string{buf}; +} + +string MathUtil::format_coord(double x, double y) { + char buf[51]; + auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); + buf[res_x++] = ' '; + auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); + buf[res_x + res_y] = '\0'; + return string{buf}; +} + +string MathUtil::format_coord(double x, double y, double zm) { + char buf[76]; + auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); + buf[res_x++] = ' '; + auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); + buf[res_x + res_y++] = ' '; + auto res_zm = geos_d2sfixed_buffered_n(zm, 15, buf + res_x + res_y); + buf[res_x + res_y + res_zm] = '\0'; + return string{buf}; +} + +string MathUtil::format_coord(double x, double y, double z, double m) { + char buf[101]; + auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); + buf[res_x++] = ' '; + auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); + buf[res_x + res_y++] = ' '; + auto res_z = geos_d2sfixed_buffered_n(z, 15, buf + res_x + res_y); + buf[res_x + res_y + res_z++] = ' '; + auto res_m = geos_d2sfixed_buffered_n(m, 15, buf + res_x + res_y + res_z); + buf[res_x + res_y + res_z + res_m] = '\0'; + return string{buf}; +} + +} // namespace core + +} // namespace spatial \ No newline at end of file diff --git a/spatial/src/spatial/gdal/file_handler.cpp b/spatial/src/spatial/gdal/file_handler.cpp index faa87029..022c6d4d 100644 --- a/spatial/src/spatial/gdal/file_handler.cpp +++ b/spatial/src/spatial/gdal/file_handler.cpp @@ -238,11 +238,10 @@ class DuckDBFileSystemHandler : public VSIFilesystemHandler { unique_ptr file; try { - file = fs.OpenFile(file_name, FileFlags::FILE_FLAGS_READ | FileCompressionType::AUTO_DETECT); + file = fs.OpenFile(file_name, FileFlags::FILE_FLAGS_READ | FileCompressionType::AUTO_DETECT | FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS); } catch (std::exception &ex) { return -1; } - if (!file) { return -1; } diff --git a/spatial/src/spatial/gdal/functions/st_read.cpp b/spatial/src/spatial/gdal/functions/st_read.cpp index 9edf968a..cc2a13d6 100644 --- a/spatial/src/spatial/gdal/functions/st_read.cpp +++ b/spatial/src/spatial/gdal/functions/st_read.cpp @@ -570,7 +570,8 @@ void GdalTableFunction::Scan(ClientContext &context, TableFunctionInput &input, validity.SetInvalid(out_idx); return core::geometry_t {}; } - return state.wkb_reader.Deserialize(input).Serialize(geom_vec); + auto geom = state.wkb_reader.Deserialize(input); + return core::Geometry::Serialize(geom, geom_vec); }); output.data[col_idx].ReferenceAndSetType(geom_vec); } diff --git a/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp index f82a0b09..2e0c425e 100644 --- a/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp @@ -80,14 +80,14 @@ static void GeodesicPolygon2DFunction(DataChunk &args, ExpressionState &state, V //------------------------------------------------------------------------------ // GEOMETRY //------------------------------------------------------------------------------ -static double PolygonArea(const Polygon &poly, GeographicLib::PolygonArea &comp) { +static double PolygonArea(const Geometry &poly, GeographicLib::PolygonArea &comp) { double total_area = 0; for (uint32_t ring_idx = 0; ring_idx < poly.Count(); ring_idx++) { comp.Clear(); - auto &ring = poly[ring_idx]; + auto &ring = Polygon::Part(poly, ring_idx); // Note: the last point is the same as the first point, but geographiclib doesn't know that, for (uint32_t coord_idx = 0; coord_idx < ring.Count() - 1; coord_idx++) { - auto coord = ring.Get(coord_idx); + auto coord = LineString::GetVertex(ring, coord_idx); comp.AddPoint(coord.x, coord.y); } double ring_area; @@ -115,34 +115,15 @@ static void GeodesicGeometryFunction(DataChunk &args, ExpressionState &state, Ve const GeographicLib::Geodesic &geod = GeographicLib::Geodesic::WGS84(); auto comp = GeographicLib::PolygonArea(geod, false); - struct op { - static double Apply(const Polygon &poly, GeographicLib::PolygonArea &comp) { - return PolygonArea(poly, comp); - } - - static double Apply(const MultiPolygon &mpoly, GeographicLib::PolygonArea &comp) { - double total_area = 0; - for (auto &poly : mpoly) { - total_area += PolygonArea(poly, comp); - } - return total_area; - } - - static double Apply(const GeometryCollection &coll, GeographicLib::PolygonArea &comp) { - double total_area = 0; - for (auto &item : coll) { - total_area += item.Visit(comp); - } - return total_area; - } - - static double Apply(const BaseGeometry &, GeographicLib::PolygonArea &) { - return 0.0; - } - }; - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { return Geometry::Deserialize(arena, input).Visit(comp); }); + input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + double area = 0; + Geometry::ExtractPolygons(geom, [&](const Geometry &geom) { + area += PolygonArea(geom, comp); + }); + return area; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp index 20f0b59e..238920fc 100644 --- a/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp @@ -58,12 +58,11 @@ static void GeodesicLineString2DFunction(DataChunk &args, ExpressionState &state //------------------------------------------------------------------------------ // GEOMETRY //------------------------------------------------------------------------------ -static double LineLength(const LineString &line, GeographicLib::PolygonArea &comp) { +static double LineLength(const Geometry &line, GeographicLib::PolygonArea &comp) { comp.Clear(); - for (uint32_t i = 0; i < line.Count(); i++) { - auto vert = line.Get(i); - comp.AddPoint(vert.x, vert.y); - } + for(VertexXY vert : LineString::Vertices(line)) { + comp.AddPoint(vert.x, vert.y); + } double _area; double linestring_length; comp.Compute(false, true, linestring_length, _area); @@ -80,34 +79,15 @@ static void GeodesicGeometryFunction(DataChunk &args, ExpressionState &state, Ve const GeographicLib::Geodesic &geod = GeographicLib::Geodesic::WGS84(); auto comp = GeographicLib::PolygonArea(geod, true); - struct op { - static double Apply(const LineString &line, GeographicLib::PolygonArea &comp) { - return LineLength(line, comp); - } - - static double Apply(const MultiLineString &mline, GeographicLib::PolygonArea &comp) { - double sum = 0.0; - for (const auto &line : mline) { - sum += LineLength(line, comp); - } - return sum; - } - - static double Apply(const GeometryCollection &collection, GeographicLib::PolygonArea &comp) { - double sum = 0.0; - for (const auto &geom : collection) { - sum += geom.Visit(comp); - } - return sum; - } - - static double Apply(const BaseGeometry &, GeographicLib::PolygonArea &) { - return 0.0; - } - }; - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { return Geometry::Deserialize(arena, input).Visit(comp); }); + input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + double length = 0.0; + Geometry::ExtractLines(geom, [&](const Geometry &line) { + length += LineLength(line, comp); + }); + return length; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp index 8c3bb52b..85d42ddd 100644 --- a/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp @@ -70,15 +70,15 @@ static void GeodesicPolygon2DFunction(DataChunk &args, ExpressionState &state, V //------------------------------------------------------------------------------ // GEOMETRY //------------------------------------------------------------------------------ -static double PolygonPerimeter(const Polygon &poly, GeographicLib::PolygonArea &comp) { +static double PolygonPerimeter(const Geometry &poly, GeographicLib::PolygonArea &comp) { double total_perimeter = 0; - for (auto &ring : poly) { + for (auto &ring : Polygon::Parts(poly)) { comp.Clear(); // Note: the last point is the same as the first point, but geographiclib doesn't know that, // so skip it. for (uint32_t coord_idx = 0; coord_idx < ring.Count() - 1; coord_idx++) { - auto coord = ring.Get(coord_idx); + auto coord = LineString::GetVertex(ring, coord_idx); comp.AddPoint(coord.x, coord.y); } double _ring_area; @@ -99,34 +99,15 @@ static void GeodesicGeometryFunction(DataChunk &args, ExpressionState &state, Ve const GeographicLib::Geodesic &geod = GeographicLib::Geodesic::WGS84(); auto comp = GeographicLib::PolygonArea(geod, false); - struct op { - static double Apply(const Polygon &poly, GeographicLib::PolygonArea &comp) { - return PolygonPerimeter(poly, comp); - } - - static double Apply(const MultiPolygon &mpoly, GeographicLib::PolygonArea &comp) { - double total_perimeter = 0; - for (auto &poly : mpoly) { - total_perimeter += PolygonPerimeter(poly, comp); - } - return total_perimeter; - } - - static double Apply(const GeometryCollection &coll, GeographicLib::PolygonArea &comp) { - double total_perimeter = 0; - for (auto &item : coll) { - total_perimeter += item.Visit(comp); - } - return total_perimeter; - } - - static double Apply(const BaseGeometry &, GeographicLib::PolygonArea &) { - return 0.0; - } - }; - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { return Geometry::Deserialize(arena, input).Visit(comp); }); + input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + auto length = 0.0; + Geometry::ExtractPolygons(geom, [&](const Geometry &poly) { + length += PolygonPerimeter(poly, comp); + }); + return length; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/geos/functions/scalar/st_is_valid.cpp b/spatial/src/spatial/geos/functions/scalar/st_is_valid.cpp index d21018dd..ae746ed3 100644 --- a/spatial/src/spatial/geos/functions/scalar/st_is_valid.cpp +++ b/spatial/src/spatial/geos/functions/scalar/st_is_valid.cpp @@ -18,42 +18,46 @@ static bool IsValidForGeos(Geometry &geometry) { switch (geometry.GetType()) { case GeometryType::LINESTRING: // Every linestring needs 0 or at least 2 points - return geometry.As().Count() != 1; + return LineString::VertexCount(geometry) != 1; case GeometryType::POLYGON: { // Every ring needs 0 or at least 4 points - auto &polygon = geometry.As(); - for (const auto &ring : polygon) { - if (ring.Count() > 0 && ring.Count() < 4) { - return false; - } - } + for(uint32_t i = 0; i < Polygon::PartCount(geometry); i++) { + auto &ring = Polygon::Part(geometry, i); + if (LineString::VertexCount(ring) < 4) { + return false; + } + } return true; } case GeometryType::MULTILINESTRING: { - for (const auto &linestring : geometry.As()) { - if (linestring.Count() == 1) { - return false; - } - } + for(uint32_t i = 0; i < MultiLineString::PartCount(geometry); i++) { + auto &linestring = MultiLineString::Part(geometry, i); + if (LineString::VertexCount(linestring) == 1) { + return false; + } + } return true; } case GeometryType::MULTIPOLYGON: { - for (const auto &polygon : geometry.As()) { - for (const auto &ring : polygon) { - if (ring.Count() > 0 && ring.Count() < 4) { - return false; - } - } - } + for(uint32_t i = 0; i < MultiPolygon::PartCount(geometry); i++) { + auto &polygon = MultiPolygon::Part(geometry, i); + for(uint32_t j = 0; j < Polygon::PartCount(polygon); j++) { + auto &ring = Polygon::Part(polygon, j); + if (LineString::VertexCount(ring) < 4) { + return false; + } + } + } return true; } case GeometryType::GEOMETRYCOLLECTION: { - for (auto &geom : geometry.As()) { - if (!IsValidForGeos(geom)) { - return false; - } - } + for(uint32_t i = 0; i < GeometryCollection::PartCount(geometry); i++) { + auto &geom = GeometryCollection::Part(geometry, i); + if (!IsValidForGeos(geom)) { + return false; + } + } return true; } default: diff --git a/spatial/src/spatial/geos/geos_wrappers.cpp b/spatial/src/spatial/geos/geos_wrappers.cpp index 2cd233f3..2605a261 100644 --- a/spatial/src/spatial/geos/geos_wrappers.cpp +++ b/spatial/src/spatial/geos/geos_wrappers.cpp @@ -1,7 +1,8 @@ #include "spatial/common.hpp" #include "spatial/geos/geos_wrappers.hpp" #include "spatial/core/geometry/geometry.hpp" -#include "spatial/core/geometry/cursor.hpp" +#include "spatial/core/util/cursor.hpp" +#include "spatial/core/util/math.hpp" #include "spatial/core/geometry/geometry_processor.hpp" namespace spatial { @@ -556,22 +557,22 @@ geometry_t SerializeGEOSGeometry(Vector &result, const GEOSGeometry *geom, GEOSC if (has_bbox) { double minx, maxx, miny, maxy; GEOSGeom_getExtent_r(ctx, geom, &minx, &miny, &maxx, &maxy); - writer.Write(Utils::DoubleToFloatDown(minx)); - writer.Write(Utils::DoubleToFloatDown(miny)); - writer.Write(Utils::DoubleToFloatUp(maxx)); - writer.Write(Utils::DoubleToFloatUp(maxy)); + writer.Write(MathUtil::DoubleToFloatDown(minx)); + writer.Write(MathUtil::DoubleToFloatDown(miny)); + writer.Write(MathUtil::DoubleToFloatUp(maxx)); + writer.Write(MathUtil::DoubleToFloatUp(maxy)); // well, this sucks. GEOS doesnt have a native way to get the Z and M value extents. if (has_z || has_m) { double minz, maxz, minm, maxm; GetExtendedExtent(geom, &minz, &maxz, &minm, &maxm, ctx); if (has_z) { - writer.Write(Utils::DoubleToFloatDown(minz)); - writer.Write(Utils::DoubleToFloatUp(maxz)); + writer.Write(MathUtil::DoubleToFloatDown(minz)); + writer.Write(MathUtil::DoubleToFloatUp(maxz)); } if (has_m) { - writer.Write(Utils::DoubleToFloatDown(minm)); - writer.Write(Utils::DoubleToFloatUp(maxm)); + writer.Write(MathUtil::DoubleToFloatDown(minm)); + writer.Write(MathUtil::DoubleToFloatUp(maxm)); } } } diff --git a/spatial/src/spatial/proj/functions.cpp b/spatial/src/spatial/proj/functions.cpp index f7e7cf84..c5263f61 100644 --- a/spatial/src/spatial/proj/functions.cpp +++ b/spatial/src/spatial/proj/functions.cpp @@ -233,18 +233,18 @@ static void Point2DTransformFunction(DataChunk &args, ExpressionState &state, Ve } struct TransformOp { - static void Apply(SinglePartGeometry &geom, PJ *crs, ArenaAllocator &arena) { - geom.MakeMutable(arena); + static void Case(Geometry::Tags::SinglePartGeometry, Geometry &geom, PJ *crs, ArenaAllocator &arena) { + SinglePartGeometry::MakeMutable(geom, arena); for (uint32_t i = 0; i < geom.Count(); i++) { - auto vertex = geom.Get(i); + auto vertex = SinglePartGeometry::GetVertex(geom, i); auto transformed = proj_trans(crs, PJ_FWD, proj_coord(vertex.x, vertex.y, 0, 0)).xy; // we own the array, so we can use SetUnsafe - geom.Set(i, transformed.x, transformed.y); + SinglePartGeometry::SetVertex(geom, i, { transformed.x, transformed.y }); } } - static void Apply(MultiPartGeometry &geom, PJ *crs, ArenaAllocator &arena) { - for (auto &part : geom) { - part.Visit(crs, arena); + static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, PJ *crs, ArenaAllocator &arena) { + for (auto &part : MultiPartGeometry::Parts(geom)) { + Geometry::Visit(part, crs, arena); } } }; @@ -296,8 +296,8 @@ static void GeometryTransformFunction(DataChunk &args, ExpressionState &state, V UnaryExecutor::Execute(geom_vec, result, count, [&](geometry_t input_geom) { auto geom = Geometry::Deserialize(arena, input_geom); - geom.Visit(crs.get(), arena); - return geom.Serialize(result); + Geometry::Visit(geom, crs.get(), arena); + return Geometry::Serialize(geom, result); }); } else { // General case: projections are not constant @@ -322,8 +322,8 @@ static void GeometryTransformFunction(DataChunk &args, ExpressionState &state, V } auto geom = Geometry::Deserialize(arena, input_geom); - geom.Visit(crs.get(), arena); - return geom.Serialize(result); + Geometry::Visit(geom, crs.get(), arena); + return Geometry::Serialize(geom, result); }); } } From 9c7bf1cb54394bdd5de38e91b7c04ba04725766b Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 24 Apr 2024 11:27:45 +0200 Subject: [PATCH 02/13] cleanup, move st_is_closed to core --- .../include/spatial/core/functions/scalar.hpp | 4 + .../spatial/core/geometry/geometry.hpp | 337 +++++++++++------- .../include/spatial/geos/functions/scalar.hpp | 2 - .../core/functions/scalar/CMakeLists.txt | 1 + .../core/functions/scalar/st_asgeojson.cpp | 4 +- .../functions/scalar/st_collectionextract.cpp | 158 +++----- .../core/functions/scalar/st_exteriorring.cpp | 4 +- .../functions/scalar/st_flipcoordinates.cpp | 6 +- .../functions/scalar/st_is_closed.cpp | 37 +- .../core/functions/scalar/st_makepolygon.cpp | 7 +- .../core/functions/scalar/st_ngeometries.cpp | 2 +- .../core/functions/scalar/st_npoints.cpp | 4 +- .../src/spatial/core/geometry/CMakeLists.txt | 1 - .../src/spatial/core/geometry/geometry.cpp | 101 +----- .../core/geometry/geometry_serialization.cpp | 22 +- .../geos/functions/scalar/CMakeLists.txt | 1 - spatial/src/spatial/proj/functions.cpp | 6 +- 17 files changed, 312 insertions(+), 385 deletions(-) rename spatial/src/spatial/{geos => core}/functions/scalar/st_is_closed.cpp (54%) diff --git a/spatial/include/spatial/core/functions/scalar.hpp b/spatial/include/spatial/core/functions/scalar.hpp index f65322f3..1ff6fed8 100644 --- a/spatial/include/spatial/core/functions/scalar.hpp +++ b/spatial/include/spatial/core/functions/scalar.hpp @@ -33,6 +33,7 @@ struct CoreScalarFunctions { RegisterStHilbert(db); RegisterStIntersects(db); RegisterStIntersectsExtent(db); + RegisterStIsClosed(db); RegisterStIsEmpty(db); RegisterStLength(db); RegisterStMakeEnvelope(db); @@ -137,6 +138,9 @@ struct CoreScalarFunctions { // ST_IntersectsExtent (&&) static void RegisterStIntersectsExtent(DatabaseInstance &db); + // ST_IsClosed + static void RegisterStIsClosed(DatabaseInstance &db); + // ST_IsEmpty static void RegisterStIsEmpty(DatabaseInstance &db); diff --git a/spatial/include/spatial/core/geometry/geometry.hpp b/spatial/include/spatial/core/geometry/geometry.hpp index ba69d492..5936047b 100644 --- a/spatial/include/spatial/core/geometry/geometry.hpp +++ b/spatial/include/spatial/core/geometry/geometry.hpp @@ -17,18 +17,6 @@ class Geometry; // Geometry //------------------------------------------------------------------------------ -// Forward declare all accessors -struct AnyGeometry; -struct SinglePartGeometry; -struct MultiPartGeometry; -struct CollectionGeometry; -struct Point; -struct LineString; -struct Polygon; -struct MultiPoint; -struct MultiLineString; -struct MultiPolygon; -struct GeometryCollection; class Geometry { friend struct SinglePartGeometry; @@ -44,6 +32,22 @@ class Geometry { Geometry(GeometryType type, GeometryProperties props, bool is_readonly, data_ptr_t data, uint32_t count) : type(type), properties(props), is_readonly(is_readonly), data_count(count), data_ptr(data) {} + + // TODO: Maybe make these public... + Geometry& operator[](uint32_t index) { + D_ASSERT(index < data_count); + return reinterpret_cast(data_ptr)[index]; + } + Geometry* begin() { return reinterpret_cast(data_ptr); } + Geometry* end() { return reinterpret_cast(data_ptr) + data_count; } + + const Geometry& operator[](uint32_t index) const { + D_ASSERT(index < data_count); + return reinterpret_cast(data_ptr)[index]; + } + const Geometry* begin() const { return reinterpret_cast(data_ptr); } + const Geometry* end() const { return reinterpret_cast(data_ptr) + data_count; } + public: // By default, create a read-only empty point Geometry() : type(GeometryType::POINT), properties(false, false), is_readonly(true), data_count(0), data_ptr(nullptr) {} @@ -102,49 +106,26 @@ class Geometry { bool IsCollection() const { return GeometryTypes::IsCollection(type); } bool IsMultiPart() const { return GeometryTypes::IsMultiPart(type); } bool IsSinglePart() const { return GeometryTypes::IsSinglePart(type); } - public: // Used for tag dispatching struct Tags { // Base types - struct AnyGeometry { - using ACCESS = ::spatial::core::AnyGeometry; - }; - struct SinglePartGeometry : public AnyGeometry { - using ACCESS = ::spatial::core::SinglePartGeometry; - }; - struct MultiPartGeometry : public AnyGeometry { - using ACCESS = ::spatial::core::MultiPartGeometry; - }; - struct CollectionGeometry : public MultiPartGeometry { - using ACCESS = ::spatial::core::CollectionGeometry; - }; + struct AnyGeometry {}; + struct SinglePartGeometry : public AnyGeometry {}; + struct MultiPartGeometry : public AnyGeometry {}; + struct CollectionGeometry : public MultiPartGeometry {}; // Concrete types - struct Point : public SinglePartGeometry { - using ACCESS = ::spatial::core::Point; - }; - struct LineString : public SinglePartGeometry { - using ACCESS = ::spatial::core::LineString; - }; - struct Polygon : public MultiPartGeometry { - using ACCESS = ::spatial::core::Polygon; - }; - struct MultiPoint : public CollectionGeometry { - using ACCESS = ::spatial::core::MultiPoint; - }; - struct MultiLineString : public CollectionGeometry { - using ACCESS = ::spatial::core::MultiLineString; - }; - struct MultiPolygon : public CollectionGeometry { - using ACCESS = ::spatial::core::MultiPolygon; - }; - struct GeometryCollection : public CollectionGeometry { - using ACCESS = ::spatial::core::GeometryCollection; - }; + struct Point : public SinglePartGeometry {}; + struct LineString : public SinglePartGeometry {}; + struct Polygon : public MultiPartGeometry {}; + struct MultiPoint : public CollectionGeometry {}; + struct MultiLineString : public CollectionGeometry {}; + struct MultiPolygon : public CollectionGeometry {}; + struct GeometryCollection : public CollectionGeometry {}; }; template - static auto Visit(Geometry& geom, ARGS&&... args) -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { + static auto Match(Geometry& geom, ARGS&&... args) -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { switch(geom.type) { case GeometryType::POINT: return T::Case(Tags::Point{}, geom, std::forward(args)...); case GeometryType::LINESTRING: return T::Case(Tags::LineString{}, geom, std::forward(args)...); @@ -158,7 +139,7 @@ class Geometry { } template - static auto Visit(const Geometry &geom, ARGS&&... args) -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { + static auto Match(const Geometry &geom, ARGS&&... args) -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { switch(geom.type) { case GeometryType::POINT: return T::Case(Tags::Point{}, geom, std::forward(args)...); case GeometryType::LINESTRING: return T::Case(Tags::LineString{}, geom, std::forward(args)...); @@ -209,6 +190,124 @@ inline Geometry Geometry::CreateEmpty(GeometryType type, bool has_z, bool has_m) return Geometry(type, props, false, nullptr, 0); } +//------------------------------------------------------------------------------ +// Inlined Geometry Functions +//------------------------------------------------------------------------------ +template +inline void Geometry::ExtractPoints(const Geometry &geom, FUNC &&func) { + struct op { + static void Case(Geometry::Tags::Point, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiPoint, const Geometry &geom, FUNC &&func) { + for(auto &part : geom) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + for(auto &part : geom) { + Match(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } + }; + Match(geom, std::forward(func)); +} + +template +inline void Geometry::ExtractLines(const Geometry &geom, FUNC &&func) { + struct op { + static void Case(Geometry::Tags::LineString, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiLineString, const Geometry &geom, FUNC &&func) { + for(auto &part : geom) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + for(auto &part : geom) { + Match(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } + }; + Match(geom, std::forward(func)); +} + +template +inline void Geometry::ExtractPolygons(const Geometry &geom, FUNC &&func){ + struct op { + static void Case(Geometry::Tags::Polygon, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiPolygon, const Geometry &geom, FUNC &&func) { + for(auto &part : geom) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + for(auto &part : geom) { + Match(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } + }; + Match(geom, std::forward(func)); +} + + +inline bool Geometry::IsEmpty(const Geometry &geom) { + struct op { + static bool Case (Geometry::Tags::SinglePartGeometry, const Geometry &geom) { + return geom.data_count == 0; + } + static bool Case (Geometry::Tags::MultiPartGeometry, const Geometry &geom) { + for(const auto &part : geom) { + if(!Geometry::Match(part)) { + return false; + } + } + return true; + } + }; + return Geometry::Match(geom); +} + +inline uint32_t Geometry::GetDimension(const Geometry &geom, bool ignore_empty) { + if (ignore_empty && Geometry::IsEmpty(geom)) { + return 0; + } + struct op { + static uint32_t Case(Geometry::Tags::Point, const Geometry&, bool) { + return 0; + } + static uint32_t Case(Geometry::Tags::LineString, const Geometry&, bool) { + return 1; + } + static uint32_t Case(Geometry::Tags::Polygon, const Geometry&, bool) { + return 2; + } + static uint32_t Case(Geometry::Tags::MultiPoint, const Geometry&, bool) { + return 0; + } + static uint32_t Case(Geometry::Tags::MultiLineString, const Geometry&, bool) { + return 1; + } + static uint32_t Case(Geometry::Tags::MultiPolygon, const Geometry&, bool) { + return 2; + } + static uint32_t Case(Geometry::Tags::GeometryCollection, const Geometry& geom, bool ignore_empty) { + uint32_t max_dimension = 0; + for(const auto &p : geom) { + max_dimension = std::max(max_dimension, Geometry::GetDimension(p, ignore_empty)); + } + return max_dimension; + } + }; + return Geometry::Match(geom, ignore_empty); +} + //------------------------------------------------------------------------------ // Iterators //------------------------------------------------------------------------------ @@ -306,11 +405,6 @@ class ConstTypedVertexView { // Accessors //------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -// AnyGeometry -//------------------------------------------------------------------------------ -struct AnyGeometry {}; - //------------------------------------------------------------------------------ // SinglePartGeometry //------------------------------------------------------------------------------ @@ -605,7 +699,6 @@ struct Point : public SinglePartGeometry { // Constants static const constexpr GeometryType TYPE = GeometryType::POINT; - static const constexpr int SURFACE_DIMENSION = 0; }; inline Geometry Point::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { @@ -673,7 +766,6 @@ struct LineString : public SinglePartGeometry { // Constants static const constexpr GeometryType TYPE = GeometryType::LINESTRING; - static const constexpr int SURFACE_DIMENSION = 1; }; inline Geometry LineString::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { @@ -684,6 +776,38 @@ inline Geometry LineString::CreateEmpty(bool has_z, bool has_m) { return Geometry::CreateEmpty(TYPE, has_z, has_m); } +//------------------------------------------------------------------------------ +// LinearRing (special case of LineString) +//------------------------------------------------------------------------------ +struct LinearRing : public LineString { + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + + // Methods + static bool IsClosed(const Geometry &geom); + + // Constants + // TODO: We dont have a LinearRing type, so we use LineString for now + static const constexpr GeometryType TYPE = GeometryType::LINESTRING; +}; + +inline Geometry LinearRing::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { + return LineString::Create(alloc, count, has_z, has_m); +} + +inline Geometry LinearRing::CreateEmpty(bool has_z, bool has_m) { + return LineString::CreateEmpty(has_z, has_m); +} + +inline bool LinearRing::IsClosed(const Geometry &geom) { + D_ASSERT(geom.GetType() == TYPE); + // The difference between LineString is that a empty LinearRing is considered closed + if(LinearRing::IsEmpty(geom)) { + return true; + } + return LineString::IsClosed(geom); +} + //------------------------------------------------------------------------------ // Polygon //------------------------------------------------------------------------------ @@ -694,10 +818,11 @@ struct Polygon : public MultiPartGeometry { static Geometry CreateFromBox(ArenaAllocator &alloc, double minx, double miny, double maxx, double maxy); // Methods + static const Geometry& ExteriorRing(const Geometry &geom); + static Geometry& ExteriorRing(Geometry &geom); // Constants static const constexpr GeometryType TYPE = GeometryType::POLYGON; - static const constexpr int SURFACE_DIMENSION = 2; }; inline Geometry Polygon::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { @@ -725,6 +850,18 @@ inline Geometry Polygon::CreateFromBox(ArenaAllocator &alloc, double minx, doubl return polygon; } +inline Geometry& Polygon::ExteriorRing(Geometry &geom) { + D_ASSERT(geom.GetType() == TYPE); + D_ASSERT(Polygon::PartCount(geom) > 0); + return Polygon::Part(geom, 0); +} + +inline const Geometry& Polygon::ExteriorRing(const Geometry &geom) { + D_ASSERT(geom.GetType() == TYPE); + D_ASSERT(Polygon::PartCount(geom) > 0); + return Polygon::Part(geom, 0); +} + //------------------------------------------------------------------------------ // MultiPoint //------------------------------------------------------------------------------ @@ -735,7 +872,6 @@ struct MultiPoint : public CollectionGeometry { // Constants static const constexpr GeometryType TYPE = GeometryType::MULTIPOINT; - static const constexpr int SURFACE_DIMENSION = 0; }; inline Geometry MultiPoint::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { @@ -763,10 +899,10 @@ struct MultiLineString : public CollectionGeometry { static Geometry CreateEmpty(bool has_z, bool has_m); static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + static bool IsClosed(const Geometry &geom); // Constants static const constexpr GeometryType TYPE = GeometryType::MULTILINESTRING; - static const constexpr int SURFACE_DIMENSION = 1; }; inline Geometry MultiLineString::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { @@ -786,6 +922,18 @@ inline Geometry MultiLineString::Create(ArenaAllocator &alloc, vector return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } +inline bool MultiLineString::IsClosed(const Geometry &geom) { + if(MultiLineString::PartCount(geom) == 0) { + return false; + } + for(auto &part : MultiLineString::Parts(geom)) { + if(!LineString::IsClosed(part)) { + return false; + } + } + return true; +} + //------------------------------------------------------------------------------ // MultiPolygon //------------------------------------------------------------------------------ @@ -796,7 +944,6 @@ struct MultiPolygon : public CollectionGeometry { // Constants static const constexpr GeometryType TYPE = GeometryType::MULTIPOLYGON; - static const constexpr int SURFACE_DIMENSION = 2; }; inline Geometry MultiPolygon::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { @@ -845,78 +992,6 @@ inline Geometry GeometryCollection::Create(ArenaAllocator &alloc, vector -inline void Geometry::ExtractPoints(const Geometry &geom, FUNC &&func) { - struct op { - static void Case(Geometry::Tags::Point, const Geometry &geom, FUNC &&func) { - func(geom); - } - static void Case(Geometry::Tags::MultiPoint, const Geometry &geom, FUNC &&func) { - auto parts = MultiPoint::Parts(geom); - for(auto &part : parts) { - func(part); - } - } - static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { - auto parts = GeometryCollection::Parts(geom); - for(auto &part : parts) { - Visit(part, std::forward(func)); - } - } - static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } - }; - Visit(geom, std::forward(func)); -} - -template -inline void Geometry::ExtractLines(const Geometry &geom, FUNC &&func) { - struct op { - static void Case(Geometry::Tags::LineString, const Geometry &geom, FUNC &&func) { - func(geom); - } - static void Case(Geometry::Tags::MultiLineString, const Geometry &geom, FUNC &&func) { - auto parts = MultiLineString::Parts(geom); - for(auto &part : parts) { - func(part); - } - } - static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { - auto parts = GeometryCollection::Parts(geom); - for(auto &part : parts) { - Visit(part, std::forward(func)); - } - } - static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } - }; - Visit(geom, std::forward(func)); -} - -template -inline void Geometry::ExtractPolygons(const Geometry &geom, FUNC &&func){ - struct op { - static void Case(Geometry::Tags::Polygon, const Geometry &geom, FUNC &&func) { - func(geom); - } - static void Case(Geometry::Tags::MultiPolygon, const Geometry &geom, FUNC &&func) { - auto parts = MultiPolygon::Parts(geom); - for(auto &part : parts) { - func(part); - } - } - static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { - auto parts = GeometryCollection::Parts(geom); - for(auto &part : parts) { - Visit(part, std::forward(func)); - } - } - static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } - }; - Visit(geom, std::forward(func)); -} - //------------------------------------------------------------------------------ // Assertions //------------------------------------------------------------------------------ diff --git a/spatial/include/spatial/geos/functions/scalar.hpp b/spatial/include/spatial/geos/functions/scalar.hpp index 0f2d2826..62aff4eb 100644 --- a/spatial/include/spatial/geos/functions/scalar.hpp +++ b/spatial/include/spatial/geos/functions/scalar.hpp @@ -25,7 +25,6 @@ struct GEOSScalarFunctions { RegisterStEnvelope(db); RegisterStIntersection(db); RegisterStIntersects(db); - RegisterStIsClosed(db); RegisterStIsRing(db); RegisterStIsSimple(db); RegisterStIsValid(db); @@ -63,7 +62,6 @@ struct GEOSScalarFunctions { static void RegisterStEnvelope(DatabaseInstance &db); static void RegisterStIntersection(DatabaseInstance &db); static void RegisterStIntersects(DatabaseInstance &db); - static void RegisterStIsClosed(DatabaseInstance &db); static void RegisterStIsRing(DatabaseInstance &db); static void RegisterStIsSimple(DatabaseInstance &db); static void RegisterStIsValid(DatabaseInstance &db); diff --git a/spatial/src/spatial/core/functions/scalar/CMakeLists.txt b/spatial/src/spatial/core/functions/scalar/CMakeLists.txt index c8da1209..150ca0e9 100644 --- a/spatial/src/spatial/core/functions/scalar/CMakeLists.txt +++ b/spatial/src/spatial/core/functions/scalar/CMakeLists.txt @@ -25,6 +25,7 @@ set(EXTENSION_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/st_hilbert.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_intersects.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_intersects_extent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/st_is_closed.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_length.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_makeenvelope.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_makeline.cpp diff --git a/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp b/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp index d01b4684..3aa1bab4 100644 --- a/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp @@ -213,7 +213,7 @@ struct ToGeoJSONFunctor { for(uint32_t i = 0; i < GeometryCollection::PartCount(collection); i++) { auto &geom = GeometryCollection::Part(collection, i); auto geom_obj = yyjson_mut_obj(doc); - Geometry::Visit(geom, doc, geom_obj); + Geometry::Match(geom, doc, geom_obj); yyjson_mut_arr_append(arr, geom_obj); } } @@ -235,7 +235,7 @@ static void GeometryToGeoJSONFragmentFunction(DataChunk &args, ExpressionState & auto obj = yyjson_mut_obj(doc); yyjson_mut_doc_set_root(doc, obj); - Geometry::Visit(geom, doc, obj); + Geometry::Match(geom, doc, obj); size_t json_size = 0; // TODO: YYJSON_WRITE_PRETTY diff --git a/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp b/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp index 7ded923e..d621537d 100644 --- a/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp @@ -15,75 +15,6 @@ namespace core { //------------------------------------------------------------------------------ // GEOMETRY //------------------------------------------------------------------------------ -static void CollectPoints(const Geometry &geom, vector &points) { - switch (geom.GetType()) { - case GeometryType::POINT: { - points.push_back(geom); - break; - } - case GeometryType::MULTIPOINT: { - for(uint32_t i = 0; i < MultiPoint::PartCount(geom); i++) { - points.push_back(MultiPoint::Part(geom, i)); - } - break; - } - case GeometryType::GEOMETRYCOLLECTION: { - for (uint32_t i = 0; i < GeometryCollection::PartCount(geom); i++) { - CollectPoints(GeometryCollection::Part(geom, i), points); - } - } - default: { - break; - } - } -} - -static void CollectLines(Geometry &geom, vector &lines) { - switch (geom.GetType()) { - case GeometryType::LINESTRING: { - lines.push_back(geom); - break; - } - case GeometryType::MULTILINESTRING: { - for(uint32_t i = 0; i < MultiLineString::PartCount(geom); i++) { - lines.push_back(MultiLineString::Part(geom, i)); - } - break; - } - case GeometryType::GEOMETRYCOLLECTION: { - for (uint32_t i = 0; i < GeometryCollection::PartCount(geom); i++) { - CollectLines(GeometryCollection::Part(geom, i), lines); - } - } - default: { - break; - } - } -} - -static void CollectPolygons(Geometry &geom, vector &polys) { - switch (geom.GetType()) { - case GeometryType::POLYGON: { - polys.push_back(geom); - break; - } - case GeometryType::MULTIPOLYGON: { - for(uint32_t i = 0; i < MultiPolygon::PartCount(geom); i++) { - polys.push_back(MultiPolygon::Part(geom, i)); - } - break; - } - case GeometryType::GEOMETRYCOLLECTION: { - for (uint32_t i = 0; i < GeometryCollection::PartCount(geom); i++) { - CollectPolygons(GeometryCollection::Part(geom, i), polys); - } - } - default: { - break; - } - } -} - // Collection extract with a specific dimension static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &state, Vector &result) { auto &lstate = GeometryFunctionLocalState::ResetAndGet(state); @@ -92,10 +23,19 @@ static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &stat auto &input = args.data[0]; auto &dim = args.data[1]; - BinaryExecutor::Execute( + // Items vector + vector items; + + BinaryExecutor::Execute( input, dim, result, count, [&](geometry_t input, int32_t requested_type) { + // Reset the items vector + items.clear(); + + // Deserialize the input geometry auto props = input.GetProperties(); auto geometry = Geometry::Deserialize(arena, input); + + // Switch on the requested type switch (requested_type) { case 1: { if (geometry.GetType() == GeometryType::MULTIPOINT || geometry.GetType() == GeometryType::POINT) { @@ -103,15 +43,11 @@ static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &stat } else if (geometry.IsCollection()) { // if it is a geometry collection, we need to collect all points if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { - vector points; - CollectPoints(geometry, points); - uint32_t size = points.size(); - - auto mpoint = MultiPoint::Create(arena, size, props.HasZ(), props.HasM()); - for (uint32_t i = 0; i < size; i++) { - MultiPoint::Part(mpoint, i) = points[i]; - } - return Geometry::Serialize(mpoint, result); + Geometry::ExtractPoints(geometry, [&](const Geometry &point) { + items.push_back(point); + }); + auto mpoint = MultiPoint::Create(arena, items, props.HasZ(), props.HasM()); + return Geometry::Serialize(mpoint, result); } // otherwise, we return an empty multipoint auto empty = MultiPoint::CreateEmpty(props.HasZ(), props.HasM()); @@ -129,14 +65,10 @@ static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &stat } else if (geometry.IsCollection()) { // if it is a geometry collection, we need to collect all lines if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { - vector lines; - CollectLines(geometry, lines); - uint32_t size = lines.size(); - - auto mline = MultiLineString::Create(arena, size, props.HasZ(), props.HasM()); - for (uint32_t i = 0; i < size; i++) { - MultiLineString::Part(mline, i) = lines[i]; - } + Geometry::ExtractLines(geometry, [&](const Geometry &line) { + items.push_back(line); + }); + auto mline = MultiLineString::Create(arena, items, props.HasZ(), props.HasM()); return Geometry::Serialize(mline, result); } // otherwise, we return an empty multilinestring @@ -154,14 +86,10 @@ static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &stat } else if (geometry.IsCollection()) { // if it is a geometry collection, we need to collect all polygons if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { - vector polys; - CollectPolygons(geometry, polys); - uint32_t size = polys.size(); - - auto mpoly = MultiPolygon::Create(arena, size, props.HasZ(), props.HasM()); - for (uint32_t i = 0; i < size; i++) { - MultiPolygon::Part(mpoly, i) = polys[i]; - } + Geometry::ExtractPolygons(geometry, [&](const Geometry &poly) { + items.push_back(poly); + }); + auto mpoly = MultiPolygon::Create(arena, items, props.HasZ(), props.HasM()); return Geometry::Serialize(mpoly, result); } // otherwise, we return an empty multipolygon @@ -188,9 +116,14 @@ static void CollectionExtractAutoFunction(DataChunk &args, ExpressionState &stat auto count = args.size(); auto &input = args.data[0]; + vector items; + UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { if (input.GetType() == GeometryType::GEOMETRYCOLLECTION) { - auto props = input.GetProperties(); + // Reset the items vector + items.clear(); + + auto props = input.GetProperties(); auto collection = Geometry::Deserialize(arena, input); if (GeometryCollection::IsEmpty(collection)) { return input; @@ -202,35 +135,26 @@ static void CollectionExtractAutoFunction(DataChunk &args, ExpressionState &stat switch (dim) { // Point case case 0: { - vector points; - CollectPoints(collection, points); - uint32_t size = points.size(); - auto mpoint = MultiPoint::Create(arena, size, props.HasZ(), props.HasM()); - for (uint32_t i = 0; i < size; i++) { - MultiPoint::Part(mpoint, i) = points[i]; - } + Geometry::ExtractPoints(collection, [&](const Geometry &point) { + items.push_back(point); + }); + auto mpoint = MultiPoint::Create(arena, items, props.HasZ(), props.HasM()); return Geometry::Serialize(mpoint, result); } // LineString case case 1: { - vector lines; - CollectLines(collection, lines); - uint32_t size = lines.size(); - auto mline = MultiLineString::Create(arena, size, props.HasZ(), props.HasM()); - for (uint32_t i = 0; i < size; i++) { - MultiLineString::Part(mline, i) = lines[i]; - } + Geometry::ExtractLines(collection, [&](const Geometry &line) { + items.push_back(line); + }); + auto mline = MultiLineString::Create(arena, items, props.HasZ(), props.HasM()); return Geometry::Serialize(mline, result); } // Polygon case case 2: { - vector polys; - CollectPolygons(collection, polys); - uint32_t size = polys.size(); - auto mpoly = MultiPolygon::Create(arena, size, props.HasZ(), props.HasM()); - for (uint32_t i = 0; i < size; i++) { - MultiPolygon::Part(mpoly, i) = polys[i]; - } + Geometry::ExtractPolygons(collection, [&](const Geometry &poly) { + items.push_back(poly); + }); + auto mpoly = MultiPolygon::Create(arena, items, props.HasZ(), props.HasM()); return Geometry::Serialize(mpoly, result); } default: { diff --git a/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp b/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp index 29a7780b..9bf5d933 100644 --- a/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp @@ -102,14 +102,12 @@ static void GeometryExteriorRingFunction(DataChunk &args, ExpressionState &state validity.SetInvalid(idx); return geometry_t {}; } - auto polygon = Geometry::Deserialize(arena, input); if (Polygon::IsEmpty(polygon)) { auto empty = LineString::CreateEmpty(polygon.GetProperties().HasZ(), polygon.GetProperties().HasM()); return Geometry::Serialize(empty, result); } - - auto &shell = Polygon::Part(polygon, 0); + auto &shell = Polygon::ExteriorRing(polygon); return Geometry::Serialize(shell, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp b/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp index 81836450..ad91fbbe 100644 --- a/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp @@ -161,7 +161,7 @@ static void GeometryFlipCoordinatesFunction(DataChunk &args, ExpressionState &st auto input = args.data[0]; auto count = args.size(); - struct FlipOp { + struct op { static void Case(Geometry::Tags::SinglePartGeometry, Geometry &geom, ArenaAllocator &arena) { SinglePartGeometry::MakeMutable(geom, arena); for (idx_t i = 0; i < SinglePartGeometry::VertexCount(geom); i++) { @@ -173,14 +173,14 @@ static void GeometryFlipCoordinatesFunction(DataChunk &args, ExpressionState &st static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, ArenaAllocator &arena) { for (uint32_t i = 0; i < MultiPartGeometry::PartCount(geom); i++) { auto &part = MultiPartGeometry::Part(geom, i); - Geometry::Visit(part, arena); + Geometry::Match(part, arena); } } }; UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { auto geom = Geometry::Deserialize(lstate.arena, input); - Geometry::Visit(geom, lstate.arena); + Geometry::Match(geom, lstate.arena); return Geometry::Serialize(geom, result); }); } diff --git a/spatial/src/spatial/geos/functions/scalar/st_is_closed.cpp b/spatial/src/spatial/core/functions/scalar/st_is_closed.cpp similarity index 54% rename from spatial/src/spatial/geos/functions/scalar/st_is_closed.cpp rename to spatial/src/spatial/core/functions/scalar/st_is_closed.cpp index c41749d0..30caca5a 100644 --- a/spatial/src/spatial/geos/functions/scalar/st_is_closed.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_is_closed.cpp @@ -1,8 +1,8 @@ #include "spatial/common.hpp" #include "spatial/core/types.hpp" -#include "spatial/geos/functions/scalar.hpp" -#include "spatial/geos/functions/common.hpp" -#include "spatial/geos/geos_wrappers.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" @@ -10,16 +10,27 @@ namespace spatial { -namespace geos { - -using namespace spatial::core; +namespace core { static void IsClosedFunction(DataChunk &args, ExpressionState &state, Vector &result) { - auto &lstate = GEOSFunctionLocalState::ResetAndGet(state); - auto &ctx = lstate.ctx.GetCtx(); + auto &lstate = GeometryFunctionLocalState::ResetAndGet(state); + auto &arena = lstate.arena; + + // TODO: We should support more than just LINESTRING and MULTILINESTRING (like PostGIS does) UnaryExecutor::Execute(args.data[0], result, args.size(), [&](geometry_t input) { - auto geom = lstate.ctx.Deserialize(input); - return GEOSisClosed_r(ctx, geom.get()); + struct op { + static bool Case(Geometry::Tags::LineString, const Geometry &geom) { + return LineString::IsClosed(geom); + } + static bool Case(Geometry::Tags::MultiLineString, const Geometry &geom) { + return MultiLineString::IsClosed(geom); + } + static bool Case(Geometry::Tags::AnyGeometry, const Geometry &) { + throw InvalidInputException("ST_IsClosed only accepts LINESTRING and MULTILINESTRING geometries"); + } + }; + auto geom = Geometry::Deserialize(arena, input); + return Geometry::Match(geom); }); } @@ -39,17 +50,17 @@ static constexpr DocTag DOC_TAGS[] = {{"ext", "spatial"}, {"category", "property //------------------------------------------------------------------------------ // Register Functions //------------------------------------------------------------------------------ -void GEOSScalarFunctions::RegisterStIsClosed(DatabaseInstance &db) { +void CoreScalarFunctions::RegisterStIsClosed(DatabaseInstance &db) { ScalarFunctionSet set("ST_IsClosed"); set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY()}, LogicalType::BOOLEAN, IsClosedFunction, nullptr, nullptr, - nullptr, GEOSFunctionLocalState::Init)); + nullptr, GeometryFunctionLocalState::Init)); ExtensionUtil::RegisterFunction(db, set); DocUtil::AddDocumentation(db, "ST_IsClosed", DOC_DESCRIPTION, DOC_EXAMPLE, DOC_TAGS); } -} // namespace geos +} // namespace core } // namespace spatial diff --git a/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp b/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp index eb253b03..2f9bef08 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp @@ -62,12 +62,11 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state if (geometry_blob.GetProperties().HasZ() || geometry_blob.GetProperties().HasM()) { throw InvalidInputException("ST_MakePolygon does not support Z or M geometries"); } - - auto hole = Geometry::Deserialize(arena, geometry_blob); - if (hole.GetType() != GeometryType::LINESTRING) { + if (geometry_blob.GetType() != GeometryType::LINESTRING) { throw InvalidInputException( StringUtil::Format("ST_MakePolygon hole #%lu is not a LINESTRING geometry", hole_idx + 1)); } + auto hole = Geometry::Deserialize(arena, geometry_blob); if (LineString::VertexCount(hole) < 4) { throw InvalidInputException( StringUtil::Format("ST_MakePolygon hole #%lu requires at least 4 vertices", hole_idx + 1)); @@ -80,7 +79,7 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state rings.push_back(hole); } - + // TODO: Add constructor that takes a vector of rings auto polygon = Polygon::Create(arena, rings.size(), false, false); for (idx_t ring_idx = 0; ring_idx < rings.size(); ring_idx++) { Polygon::Part(polygon, ring_idx) = std::move(rings[ring_idx]); diff --git a/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp b/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp index 76153da6..5c1e3c54 100644 --- a/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp @@ -33,7 +33,7 @@ static void GeometryNGeometriesFunction(DataChunk &args, ExpressionState &state, } }; auto geom = Geometry::Deserialize(ctx.arena, input); - return Geometry::Visit(geom); + return Geometry::Match(geom); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_npoints.cpp b/spatial/src/spatial/core/functions/scalar/st_npoints.cpp index 3009df67..22c748b2 100644 --- a/spatial/src/spatial/core/functions/scalar/st_npoints.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_npoints.cpp @@ -83,7 +83,7 @@ static void GeometryNumPointsFunction(DataChunk &args, ExpressionState &state, V uint32_t count = 0; for(uint32_t i = 0; i < MultiPartGeometry::PartCount(geom); i++) { auto part = MultiPartGeometry::Part(geom, i); - count += Geometry::Visit(part); + count += Geometry::Match(part); } return count; } @@ -92,7 +92,7 @@ static void GeometryNumPointsFunction(DataChunk &args, ExpressionState &state, V UnaryExecutor::Execute( input, result, count, [&](geometry_t input) { auto geom = Geometry::Deserialize(arena, input); - return Geometry::Visit(geom); + return Geometry::Match(geom); }); } diff --git a/spatial/src/spatial/core/geometry/CMakeLists.txt b/spatial/src/spatial/core/geometry/CMakeLists.txt index e119e0d8..0367fdb7 100644 --- a/spatial/src/spatial/core/geometry/CMakeLists.txt +++ b/spatial/src/spatial/core/geometry/CMakeLists.txt @@ -6,6 +6,5 @@ set(EXTENSION_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/wkb_reader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wkb_writer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wkt_reader.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/geometry2.cpp PARENT_SCOPE ) \ No newline at end of file diff --git a/spatial/src/spatial/core/geometry/geometry.cpp b/spatial/src/spatial/core/geometry/geometry.cpp index e9d99867..ec8d82ba 100644 --- a/spatial/src/spatial/core/geometry/geometry.cpp +++ b/spatial/src/spatial/core/geometry/geometry.cpp @@ -1,5 +1,6 @@ #include "spatial/common.hpp" #include "spatial/core/geometry/geometry.hpp" +#include "spatial/core/util/math.hpp" namespace spatial { @@ -8,41 +9,6 @@ namespace core { //------------------------------------------------------------------------------ // Single Part Geometry //------------------------------------------------------------------------------ -/* -void SinglePartGeometry::Reference(const SinglePartGeometry &other, uint32_t offset, uint32_t count) { - properties = other.properties; - data.vertex_data = other.data.vertex_data + offset * properties.VertexSize(); - data_count = count; - is_readonly = true; -} - -void SinglePartGeometry::ReferenceData(const_data_ptr_t data_ptr, uint32_t count, bool has_z, bool has_m) { - properties.SetZ(has_z); - properties.SetM(has_m); - data.vertex_data = const_cast(data_ptr); - data_count = count; - is_readonly = true; -} - -void SinglePartGeometry::Copy(ArenaAllocator &alloc, const SinglePartGeometry &other, uint32_t offset, uint32_t count) { - properties = other.properties; - data_count = count; - data.vertex_data = alloc.AllocateAligned(properties.VertexSize() * count); - memcpy(data.vertex_data, other.data.vertex_data + offset * properties.VertexSize(), - properties.VertexSize() * count); - is_readonly = false; -} - -void SinglePartGeometry::CopyData(ArenaAllocator &alloc, const_data_ptr_t data_ptr, uint32_t count, bool has_z, - bool has_m) { - properties.SetZ(has_z); - properties.SetM(has_m); - data_count = count; - data.vertex_data = alloc.AllocateAligned(properties.VertexSize() * count); - memcpy(data.vertex_data, data_ptr, properties.VertexSize() * count); - is_readonly = false; -} -*/ void SinglePartGeometry::Resize(Geometry& geom, ArenaAllocator &alloc, uint32_t new_count) { D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); @@ -59,7 +25,7 @@ void SinglePartGeometry::Resize(Geometry& geom, ArenaAllocator &alloc, uint32_t } if (!geom.is_readonly) { - geom.data_ptr = alloc.Reallocate(geom.data_ptr, geom.data_count * vertex_size, vertex_size * new_count); + geom.data_ptr = alloc.ReallocateAligned(geom.data_ptr, geom.data_count * vertex_size, vertex_size * new_count); geom.data_count = new_count; } else { auto new_data = alloc.AllocateAligned(vertex_size * new_count); @@ -260,7 +226,7 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 string result = StringUtil::Format("%s XYZM ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { auto vertex = GetVertex(geom, i); - result += StringUtil::Format("(%f, %f, %f, %f)", vertex.x, vertex.y, vertex.z, vertex.m); + result += "(" + MathUtil::format_coord(vertex.x, vertex.y, vertex.z, vertex.m) + ")"; if (i < count - 1) { result += ", "; } @@ -271,7 +237,7 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 string result = StringUtil::Format("%s XYZ ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { auto vertex = GetVertex(geom, i); - result += StringUtil::Format("(%f, %f, %f)", vertex.x, vertex.y, vertex.z); + result += "(" + MathUtil::format_coord(vertex.x, vertex.y, vertex.z) + ")"; if (i < count - 1) { result += ", "; } @@ -282,7 +248,7 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 string result = StringUtil::Format("%s XYM ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { auto vertex = GetVertex(geom, i); - result += StringUtil::Format("(%f, %f, %f)", vertex.x, vertex.y, vertex.m); + result += "(" + MathUtil::format_coord(vertex.x, vertex.y, vertex.m) + ")"; if (i < count - 1) { result += ", "; } @@ -293,7 +259,7 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 string result = StringUtil::Format("%s XY ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { auto vertex = GetVertex(geom, i); - result += StringUtil::Format("(%f, %f)", vertex.x, vertex.y); + result += "(" + MathUtil::format_coord(vertex.x, vertex.y) + ")"; if (i < count - 1) { result += ", "; } @@ -303,57 +269,10 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 } } -bool Geometry::IsEmpty(const Geometry &geom) { - struct op { - static bool Case (Geometry::Tags::SinglePartGeometry, const Geometry &geom) { - return geom.data_count == 0; - } - static bool Case (Geometry::Tags::MultiPartGeometry, const Geometry &geom) { - for(const auto &p : MultiPartGeometry::Parts(geom)) { - if (!Geometry::IsEmpty(p)) { - return false; - } - } - return true; - } - }; - return Geometry::Visit(geom); -} - -uint32_t Geometry::GetDimension(const Geometry &geom, bool ignore_empty) { - if (ignore_empty && Geometry::IsEmpty(geom)) { - return 0; - } - struct op { - static uint32_t Case(Geometry::Tags::Point, const Geometry&, bool) { - return 0; - } - static uint32_t Case(Geometry::Tags::LineString, const Geometry&, bool) { - return 1; - } - static uint32_t Case(Geometry::Tags::Polygon, const Geometry&, bool) { - return 2; - } - static uint32_t Case(Geometry::Tags::MultiPoint, const Geometry&, bool) { - return 0; - } - static uint32_t Case(Geometry::Tags::MultiLineString, const Geometry&, bool) { - return 1; - } - static uint32_t Case(Geometry::Tags::MultiPolygon, const Geometry&, bool) { - return 2; - } - static uint32_t Case(Geometry::Tags::GeometryCollection, const Geometry& geom, bool ignore_empty) { - uint32_t max_dimension = 0; - for(const auto &p : GeometryCollection::Parts(geom)) { - max_dimension = std::max(max_dimension, Geometry::GetDimension(p, ignore_empty)); - } - return max_dimension; - } - }; - return Geometry::Visit(geom, ignore_empty); -} +//------------------------------------------------------------------------------ +// Geometry +//------------------------------------------------------------------------------ void Geometry::SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { struct op { static void Case(Geometry::Tags::SinglePartGeometry, Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { @@ -367,7 +286,7 @@ void Geometry::SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, doub } } }; - Geometry::Visit(*this, alloc, has_z, has_m, default_z, default_m); + Geometry::Match(*this, alloc, has_z, has_m, default_z, default_m); } //------------------------------------------------------------------------------ diff --git a/spatial/src/spatial/core/geometry/geometry_serialization.cpp b/spatial/src/spatial/core/geometry/geometry_serialization.cpp index 02999866..73652a6b 100644 --- a/spatial/src/spatial/core/geometry/geometry_serialization.cpp +++ b/spatial/src/spatial/core/geometry/geometry_serialization.cpp @@ -57,7 +57,7 @@ struct GetRequiredSizeOp { size += 4; size += Polygon::Part(polygon, i).Count() * sizeof(VERTEX); } - if (polygon.Count() % 2 == 1) { + if (Polygon::PartCount(polygon) % 2 == 1) { size += 4; } return size; @@ -70,7 +70,7 @@ struct GetRequiredSizeOp { uint32_t size = 4 + 4; for(uint32_t i = 0; i < CollectionGeometry::PartCount(collection); i++) { auto &part = CollectionGeometry::Part(collection, i); - size += Geometry::Visit>(part); + size += Geometry::Match>(part); } return size; } @@ -219,7 +219,7 @@ struct SerializeOp { // write geometry data for(uint32_t i = 0; i < GeometryCollection::PartCount(collection); i++) { auto &geom = GeometryCollection::Part(collection, i); - Geometry::Visit>(geom, cursor, bbox, depth + 1); + Geometry::Match>(geom, cursor, bbox, depth + 1); } } }; @@ -235,13 +235,13 @@ geometry_t Geometry::Serialize(const Geometry &geom, Vector &result) { uint32_t geom_size = 0; if (has_z && has_m) { - geom_size = Geometry::Visit>(geom); + geom_size = Geometry::Match>(geom); } else if (has_z) { - geom_size = Geometry::Visit>(geom); + geom_size = Geometry::Match>(geom); } else if (has_m) { - geom_size = Geometry::Visit>(geom); + geom_size = Geometry::Match>(geom); } else { - geom_size = Geometry::Visit>(geom); + geom_size = Geometry::Match>(geom); } auto header_size = 4; @@ -268,13 +268,13 @@ geometry_t Geometry::Serialize(const Geometry &geom, Vector &result) { cursor.Skip(bbox_size); if (has_z && has_m) { - Geometry::Visit>(geom, cursor, bbox, 0); + Geometry::Match>(geom, cursor, bbox, 0); } else if (has_z) { - Geometry::Visit>(geom, cursor, bbox, 0); + Geometry::Match>(geom, cursor, bbox, 0); } else if (has_m) { - Geometry::Visit>(geom, cursor, bbox, 0); + Geometry::Match>(geom, cursor, bbox, 0); } else { - Geometry::Visit>(geom, cursor, bbox, 0); + Geometry::Match>(geom, cursor, bbox, 0); } // Now write the bounding box diff --git a/spatial/src/spatial/geos/functions/scalar/CMakeLists.txt b/spatial/src/spatial/geos/functions/scalar/CMakeLists.txt index 91c507c5..dabb15e3 100644 --- a/spatial/src/spatial/geos/functions/scalar/CMakeLists.txt +++ b/spatial/src/spatial/geos/functions/scalar/CMakeLists.txt @@ -17,7 +17,6 @@ set(EXTENSION_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/st_equals.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_intersection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_intersects.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/st_is_closed.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_is_ring.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_is_simple.cpp ${CMAKE_CURRENT_SOURCE_DIR}/st_is_valid.cpp diff --git a/spatial/src/spatial/proj/functions.cpp b/spatial/src/spatial/proj/functions.cpp index c5263f61..c3cc45d3 100644 --- a/spatial/src/spatial/proj/functions.cpp +++ b/spatial/src/spatial/proj/functions.cpp @@ -244,7 +244,7 @@ struct TransformOp { } static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, PJ *crs, ArenaAllocator &arena) { for (auto &part : MultiPartGeometry::Parts(geom)) { - Geometry::Visit(part, crs, arena); + Geometry::Match(part, crs, arena); } } }; @@ -296,7 +296,7 @@ static void GeometryTransformFunction(DataChunk &args, ExpressionState &state, V UnaryExecutor::Execute(geom_vec, result, count, [&](geometry_t input_geom) { auto geom = Geometry::Deserialize(arena, input_geom); - Geometry::Visit(geom, crs.get(), arena); + Geometry::Match(geom, crs.get(), arena); return Geometry::Serialize(geom, result); }); } else { @@ -322,7 +322,7 @@ static void GeometryTransformFunction(DataChunk &args, ExpressionState &state, V } auto geom = Geometry::Deserialize(arena, input_geom); - Geometry::Visit(geom, crs.get(), arena); + Geometry::Match(geom, crs.get(), arena); return Geometry::Serialize(geom, result); }); } From 83801cb74e19bce4114c8563d8eb0e10581cab3f Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 24 Apr 2024 11:28:20 +0200 Subject: [PATCH 03/13] format --- .../include/spatial/core/functions/scalar.hpp | 6 +- .../spatial/core/geometry/geometry.hpp | 1491 +++++++++-------- .../spatial/core/geometry/geometry_type.hpp | 70 +- .../spatial/core/geometry/wkb_reader.hpp | 12 +- .../spatial/core/geometry/wkt_reader.hpp | 14 +- spatial/include/spatial/core/util/math.hpp | 68 +- .../spatial/core/util/misaligned_ptr.hpp | 184 +- .../spatial/core/util/misaligned_ref.hpp | 47 +- .../core/functions/cast/geometry_cast.cpp | 8 +- .../core/functions/cast/varchar_cast.cpp | 6 +- .../spatial/core/functions/cast/wkb_cast.cpp | 4 +- .../core/functions/scalar/st_asgeojson.cpp | 103 +- .../core/functions/scalar/st_collect.cpp | 22 +- .../functions/scalar/st_collectionextract.cpp | 185 +- .../spatial/core/functions/scalar/st_dump.cpp | 12 +- .../core/functions/scalar/st_endpoint.cpp | 4 +- .../core/functions/scalar/st_exteriorring.cpp | 8 +- .../functions/scalar/st_flipcoordinates.cpp | 16 +- .../core/functions/scalar/st_force.cpp | 18 +- .../functions/scalar/st_geomfromhexwkb.cpp | 4 +- .../core/functions/scalar/st_geomfromtext.cpp | 2 +- .../core/functions/scalar/st_geomfromwkb.cpp | 9 +- .../core/functions/scalar/st_is_closed.cpp | 30 +- .../core/functions/scalar/st_isempty.cpp | 9 +- .../core/functions/scalar/st_length.cpp | 15 +- .../core/functions/scalar/st_makeenvelope.cpp | 2 +- .../core/functions/scalar/st_makeline.cpp | 19 +- .../core/functions/scalar/st_makepolygon.cpp | 10 +- .../core/functions/scalar/st_ngeometries.cpp | 6 +- .../functions/scalar/st_ninteriorrings.cpp | 4 +- .../core/functions/scalar/st_npoints.cpp | 13 +- .../core/functions/scalar/st_perimeter.cpp | 21 +- .../core/functions/scalar/st_point.cpp | 2 +- .../core/functions/scalar/st_pointn.cpp | 2 +- .../core/functions/scalar/st_quadkey.cpp | 8 +- .../core/functions/scalar/st_startpoint.cpp | 30 +- .../src/spatial/core/geometry/geometry.cpp | 99 +- .../core/geometry/geometry_serialization.cpp | 86 +- .../src/spatial/core/geometry/wkb_reader.cpp | 12 +- .../src/spatial/core/geometry/wkt_reader.cpp | 6 +- .../core/io/shapefile/read_shapefile.cpp | 10 +- .../core/io/shapefile/shapefile_common.cpp | 6 +- spatial/src/spatial/core/util/math.cpp | 56 +- spatial/src/spatial/gdal/file_handler.cpp | 16 +- .../src/spatial/gdal/functions/st_read.cpp | 4 +- .../functions/st_area_spheroid.cpp | 15 +- .../functions/st_length_spheroid.cpp | 21 +- .../functions/st_perimeter_spheroid.cpp | 15 +- .../geos/functions/scalar/st_is_valid.cpp | 54 +- spatial/src/spatial/proj/functions.cpp | 8 +- 50 files changed, 1509 insertions(+), 1363 deletions(-) diff --git a/spatial/include/spatial/core/functions/scalar.hpp b/spatial/include/spatial/core/functions/scalar.hpp index 1ff6fed8..2c446e95 100644 --- a/spatial/include/spatial/core/functions/scalar.hpp +++ b/spatial/include/spatial/core/functions/scalar.hpp @@ -33,7 +33,7 @@ struct CoreScalarFunctions { RegisterStHilbert(db); RegisterStIntersects(db); RegisterStIntersectsExtent(db); - RegisterStIsClosed(db); + RegisterStIsClosed(db); RegisterStIsEmpty(db); RegisterStLength(db); RegisterStMakeEnvelope(db); @@ -138,8 +138,8 @@ struct CoreScalarFunctions { // ST_IntersectsExtent (&&) static void RegisterStIntersectsExtent(DatabaseInstance &db); - // ST_IsClosed - static void RegisterStIsClosed(DatabaseInstance &db); + // ST_IsClosed + static void RegisterStIsClosed(DatabaseInstance &db); // ST_IsEmpty static void RegisterStIsEmpty(DatabaseInstance &db); diff --git a/spatial/include/spatial/core/geometry/geometry.hpp b/spatial/include/spatial/core/geometry/geometry.hpp index 5936047b..7f9ab0a6 100644 --- a/spatial/include/spatial/core/geometry/geometry.hpp +++ b/spatial/include/spatial/core/geometry/geometry.hpp @@ -17,295 +17,348 @@ class Geometry; // Geometry //------------------------------------------------------------------------------ - class Geometry { - friend struct SinglePartGeometry; - friend struct MultiPartGeometry; - friend struct CollectionGeometry; + friend struct SinglePartGeometry; + friend struct MultiPartGeometry; + friend struct CollectionGeometry; + private: - GeometryType type; - GeometryProperties properties; - bool is_readonly; - uint32_t data_count; - data_ptr_t data_ptr; - - Geometry(GeometryType type, GeometryProperties props, bool is_readonly, data_ptr_t data, uint32_t count) - : type(type), properties(props), is_readonly(is_readonly), data_count(count), data_ptr(data) {} - - - // TODO: Maybe make these public... - Geometry& operator[](uint32_t index) { - D_ASSERT(index < data_count); - return reinterpret_cast(data_ptr)[index]; - } - Geometry* begin() { return reinterpret_cast(data_ptr); } - Geometry* end() { return reinterpret_cast(data_ptr) + data_count; } - - const Geometry& operator[](uint32_t index) const { - D_ASSERT(index < data_count); - return reinterpret_cast(data_ptr)[index]; - } - const Geometry* begin() const { return reinterpret_cast(data_ptr); } - const Geometry* end() const { return reinterpret_cast(data_ptr) + data_count; } + GeometryType type; + GeometryProperties properties; + bool is_readonly; + uint32_t data_count; + data_ptr_t data_ptr; + + Geometry(GeometryType type, GeometryProperties props, bool is_readonly, data_ptr_t data, uint32_t count) + : type(type), properties(props), is_readonly(is_readonly), data_count(count), data_ptr(data) { + } + + // TODO: Maybe make these public... + Geometry &operator[](uint32_t index) { + D_ASSERT(index < data_count); + return reinterpret_cast(data_ptr)[index]; + } + Geometry *begin() { + return reinterpret_cast(data_ptr); + } + Geometry *end() { + return reinterpret_cast(data_ptr) + data_count; + } + + const Geometry &operator[](uint32_t index) const { + D_ASSERT(index < data_count); + return reinterpret_cast(data_ptr)[index]; + } + const Geometry *begin() const { + return reinterpret_cast(data_ptr); + } + const Geometry *end() const { + return reinterpret_cast(data_ptr) + data_count; + } public: - // By default, create a read-only empty point - Geometry() : type(GeometryType::POINT), properties(false, false), is_readonly(true), data_count(0), data_ptr(nullptr) {} - - Geometry(GeometryType type, bool has_z, bool has_m) - : type(type), properties(has_z, has_m), is_readonly(true), data_count(0), data_ptr(nullptr) {} - - // Copy Constructor - Geometry(const Geometry &other) - : type(other.type), properties(other.properties), is_readonly(true), data_count(other.data_count), - data_ptr(other.data_ptr) { } - - // Copy Assignment - Geometry &operator=(const Geometry &other) { - type = other.type; - properties = other.properties; - is_readonly = true; - data_count = other.data_count; - data_ptr = other.data_ptr; - return *this; - } - - // Move Constructor - Geometry(Geometry &&other) noexcept - : type(other.type), properties(other.properties), is_readonly(other.is_readonly), data_count(other.data_count), - data_ptr(other.data_ptr) { - if (!other.is_readonly) { - // Take ownership of the data, and make the other object read-only - other.is_readonly = true; - } - } - - // Move Assignment - Geometry &operator=(Geometry &&other) noexcept { - type = other.type; - properties = other.properties; - is_readonly = other.is_readonly; - data_count = other.data_count; - data_ptr = other.data_ptr; - if (!other.is_readonly) { - // Take ownership of the data, and make the other object read-only - other.is_readonly = true; - } - return *this; - } + // By default, create a read-only empty point + Geometry() + : type(GeometryType::POINT), properties(false, false), is_readonly(true), data_count(0), data_ptr(nullptr) { + } + + Geometry(GeometryType type, bool has_z, bool has_m) + : type(type), properties(has_z, has_m), is_readonly(true), data_count(0), data_ptr(nullptr) { + } + + // Copy Constructor + Geometry(const Geometry &other) + : type(other.type), properties(other.properties), is_readonly(true), data_count(other.data_count), + data_ptr(other.data_ptr) { + } + + // Copy Assignment + Geometry &operator=(const Geometry &other) { + type = other.type; + properties = other.properties; + is_readonly = true; + data_count = other.data_count; + data_ptr = other.data_ptr; + return *this; + } + + // Move Constructor + Geometry(Geometry &&other) noexcept + : type(other.type), properties(other.properties), is_readonly(other.is_readonly), data_count(other.data_count), + data_ptr(other.data_ptr) { + if (!other.is_readonly) { + // Take ownership of the data, and make the other object read-only + other.is_readonly = true; + } + } + + // Move Assignment + Geometry &operator=(Geometry &&other) noexcept { + type = other.type; + properties = other.properties; + is_readonly = other.is_readonly; + data_count = other.data_count; + data_ptr = other.data_ptr; + if (!other.is_readonly) { + // Take ownership of the data, and make the other object read-only + other.is_readonly = true; + } + return *this; + } public: - GeometryType GetType() const { return type; } - GeometryProperties &GetProperties() { return properties; } - const GeometryProperties &GetProperties() const { return properties; } - const_data_ptr_t GetData() const { return data_ptr; } - data_ptr_t GetData() { return data_ptr; } - bool IsReadOnly() const { return is_readonly; } - uint32_t Count() const { return data_count; } - - bool IsCollection() const { return GeometryTypes::IsCollection(type); } - bool IsMultiPart() const { return GeometryTypes::IsMultiPart(type); } - bool IsSinglePart() const { return GeometryTypes::IsSinglePart(type); } + GeometryType GetType() const { + return type; + } + GeometryProperties &GetProperties() { + return properties; + } + const GeometryProperties &GetProperties() const { + return properties; + } + const_data_ptr_t GetData() const { + return data_ptr; + } + data_ptr_t GetData() { + return data_ptr; + } + bool IsReadOnly() const { + return is_readonly; + } + uint32_t Count() const { + return data_count; + } + + bool IsCollection() const { + return GeometryTypes::IsCollection(type); + } + bool IsMultiPart() const { + return GeometryTypes::IsMultiPart(type); + } + bool IsSinglePart() const { + return GeometryTypes::IsSinglePart(type); + } + public: - // Used for tag dispatching - struct Tags { - // Base types - struct AnyGeometry {}; - struct SinglePartGeometry : public AnyGeometry {}; - struct MultiPartGeometry : public AnyGeometry {}; - struct CollectionGeometry : public MultiPartGeometry {}; - // Concrete types - struct Point : public SinglePartGeometry {}; - struct LineString : public SinglePartGeometry {}; - struct Polygon : public MultiPartGeometry {}; - struct MultiPoint : public CollectionGeometry {}; - struct MultiLineString : public CollectionGeometry {}; - struct MultiPolygon : public CollectionGeometry {}; - struct GeometryCollection : public CollectionGeometry {}; - }; - - template - static auto Match(Geometry& geom, ARGS&&... args) -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { - switch(geom.type) { - case GeometryType::POINT: return T::Case(Tags::Point{}, geom, std::forward(args)...); - case GeometryType::LINESTRING: return T::Case(Tags::LineString{}, geom, std::forward(args)...); - case GeometryType::POLYGON: return T::Case(Tags::Polygon{}, geom, std::forward(args)...); - case GeometryType::MULTIPOINT: return T::Case(Tags::MultiPoint{}, geom, std::forward(args)...); - case GeometryType::MULTILINESTRING: return T::Case(Tags::MultiLineString{}, geom, std::forward(args)...); - case GeometryType::MULTIPOLYGON: return T::Case(Tags::MultiPolygon{}, geom, std::forward(args)...); - case GeometryType::GEOMETRYCOLLECTION: return T::Case(Tags::GeometryCollection{}, geom, std::forward(args)...); - default: throw NotImplementedException("Geometry::Match"); - } - } - - template - static auto Match(const Geometry &geom, ARGS&&... args) -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { - switch(geom.type) { - case GeometryType::POINT: return T::Case(Tags::Point{}, geom, std::forward(args)...); - case GeometryType::LINESTRING: return T::Case(Tags::LineString{}, geom, std::forward(args)...); - case GeometryType::POLYGON: return T::Case(Tags::Polygon{}, geom, std::forward(args)...); - case GeometryType::MULTIPOINT: return T::Case(Tags::MultiPoint{}, geom, std::forward(args)...); - case GeometryType::MULTILINESTRING: return T::Case(Tags::MultiLineString{}, geom, std::forward(args)...); - case GeometryType::MULTIPOLYGON: return T::Case(Tags::MultiPolygon{}, geom, std::forward(args)...); - case GeometryType::GEOMETRYCOLLECTION: return T::Case(Tags::GeometryCollection{}, geom, std::forward(args)...); - default: throw NotImplementedException("Geometry::Match"); - } - } - - // TODO: Swap this to only have two create methods, - // and use mutating methods for Reference/Copy - static Geometry Create(ArenaAllocator &alloc, GeometryType type, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(GeometryType type, bool has_z, bool has_m); - - static geometry_t Serialize(const Geometry &geom, Vector &result); - static Geometry Deserialize(ArenaAllocator &arena, const geometry_t &data); - - static bool IsEmpty(const Geometry &geom); - static uint32_t GetDimension(const Geometry &geom, bool recurse); - void SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, double default_z = 0, double default_m = 0); - - // Iterate over all points in the geometry, recursing into collections - template - static void ExtractPoints(const Geometry &geom, FUNC &&func); - - // Iterate over all lines in the geometry, recursing into collections - template - static void ExtractLines(const Geometry &geom, FUNC &&func); - - // Iterate over all polygons in the geometry, recursing into collections - template - static void ExtractPolygons(const Geometry &geom, FUNC &&func); + // Used for tag dispatching + struct Tags { + // Base types + struct AnyGeometry {}; + struct SinglePartGeometry : public AnyGeometry {}; + struct MultiPartGeometry : public AnyGeometry {}; + struct CollectionGeometry : public MultiPartGeometry {}; + // Concrete types + struct Point : public SinglePartGeometry {}; + struct LineString : public SinglePartGeometry {}; + struct Polygon : public MultiPartGeometry {}; + struct MultiPoint : public CollectionGeometry {}; + struct MultiLineString : public CollectionGeometry {}; + struct MultiPolygon : public CollectionGeometry {}; + struct GeometryCollection : public CollectionGeometry {}; + }; + + template + static auto Match(Geometry &geom, ARGS &&...args) + -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { + switch (geom.type) { + case GeometryType::POINT: + return T::Case(Tags::Point {}, geom, std::forward(args)...); + case GeometryType::LINESTRING: + return T::Case(Tags::LineString {}, geom, std::forward(args)...); + case GeometryType::POLYGON: + return T::Case(Tags::Polygon {}, geom, std::forward(args)...); + case GeometryType::MULTIPOINT: + return T::Case(Tags::MultiPoint {}, geom, std::forward(args)...); + case GeometryType::MULTILINESTRING: + return T::Case(Tags::MultiLineString {}, geom, std::forward(args)...); + case GeometryType::MULTIPOLYGON: + return T::Case(Tags::MultiPolygon {}, geom, std::forward(args)...); + case GeometryType::GEOMETRYCOLLECTION: + return T::Case(Tags::GeometryCollection {}, geom, std::forward(args)...); + default: + throw NotImplementedException("Geometry::Match"); + } + } + + template + static auto Match(const Geometry &geom, ARGS &&...args) + -> decltype(T::Case(std::declval(), std::declval(), std::declval()...)) { + switch (geom.type) { + case GeometryType::POINT: + return T::Case(Tags::Point {}, geom, std::forward(args)...); + case GeometryType::LINESTRING: + return T::Case(Tags::LineString {}, geom, std::forward(args)...); + case GeometryType::POLYGON: + return T::Case(Tags::Polygon {}, geom, std::forward(args)...); + case GeometryType::MULTIPOINT: + return T::Case(Tags::MultiPoint {}, geom, std::forward(args)...); + case GeometryType::MULTILINESTRING: + return T::Case(Tags::MultiLineString {}, geom, std::forward(args)...); + case GeometryType::MULTIPOLYGON: + return T::Case(Tags::MultiPolygon {}, geom, std::forward(args)...); + case GeometryType::GEOMETRYCOLLECTION: + return T::Case(Tags::GeometryCollection {}, geom, std::forward(args)...); + default: + throw NotImplementedException("Geometry::Match"); + } + } + + // TODO: Swap this to only have two create methods, + // and use mutating methods for Reference/Copy + static Geometry Create(ArenaAllocator &alloc, GeometryType type, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(GeometryType type, bool has_z, bool has_m); + + static geometry_t Serialize(const Geometry &geom, Vector &result); + static Geometry Deserialize(ArenaAllocator &arena, const geometry_t &data); + + static bool IsEmpty(const Geometry &geom); + static uint32_t GetDimension(const Geometry &geom, bool recurse); + void SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, double default_z = 0, double default_m = 0); + + // Iterate over all points in the geometry, recursing into collections + template + static void ExtractPoints(const Geometry &geom, FUNC &&func); + + // Iterate over all lines in the geometry, recursing into collections + template + static void ExtractLines(const Geometry &geom, FUNC &&func); + + // Iterate over all polygons in the geometry, recursing into collections + template + static void ExtractPolygons(const Geometry &geom, FUNC &&func); }; inline Geometry Geometry::Create(ArenaAllocator &alloc, GeometryType type, uint32_t count, bool has_z, bool has_m) { - GeometryProperties props(has_z, has_m); - auto single_part = GeometryTypes::IsSinglePart(type); - auto elem_size = single_part ? props.VertexSize() : sizeof(Geometry); - auto geom = Geometry(type, props, false, alloc.AllocateAligned(count * elem_size), count); - return geom; + GeometryProperties props(has_z, has_m); + auto single_part = GeometryTypes::IsSinglePart(type); + auto elem_size = single_part ? props.VertexSize() : sizeof(Geometry); + auto geom = Geometry(type, props, false, alloc.AllocateAligned(count * elem_size), count); + return geom; } inline Geometry Geometry::CreateEmpty(GeometryType type, bool has_z, bool has_m) { - GeometryProperties props(has_z, has_m); - return Geometry(type, props, false, nullptr, 0); + GeometryProperties props(has_z, has_m); + return Geometry(type, props, false, nullptr, 0); } //------------------------------------------------------------------------------ // Inlined Geometry Functions //------------------------------------------------------------------------------ -template +template inline void Geometry::ExtractPoints(const Geometry &geom, FUNC &&func) { - struct op { - static void Case(Geometry::Tags::Point, const Geometry &geom, FUNC &&func) { - func(geom); - } - static void Case(Geometry::Tags::MultiPoint, const Geometry &geom, FUNC &&func) { - for(auto &part : geom) { - func(part); - } - } - static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { - for(auto &part : geom) { - Match(part, std::forward(func)); - } - } - static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } - }; - Match(geom, std::forward(func)); -} - -template + struct op { + static void Case(Geometry::Tags::Point, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiPoint, const Geometry &geom, FUNC &&func) { + for (auto &part : geom) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + for (auto &part : geom) { + Match(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry &, FUNC &&) { + } + }; + Match(geom, std::forward(func)); +} + +template inline void Geometry::ExtractLines(const Geometry &geom, FUNC &&func) { - struct op { - static void Case(Geometry::Tags::LineString, const Geometry &geom, FUNC &&func) { - func(geom); - } - static void Case(Geometry::Tags::MultiLineString, const Geometry &geom, FUNC &&func) { - for(auto &part : geom) { - func(part); - } - } - static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { - for(auto &part : geom) { - Match(part, std::forward(func)); - } - } - static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } - }; - Match(geom, std::forward(func)); -} - -template -inline void Geometry::ExtractPolygons(const Geometry &geom, FUNC &&func){ - struct op { - static void Case(Geometry::Tags::Polygon, const Geometry &geom, FUNC &&func) { - func(geom); - } - static void Case(Geometry::Tags::MultiPolygon, const Geometry &geom, FUNC &&func) { - for(auto &part : geom) { - func(part); - } - } - static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { - for(auto &part : geom) { - Match(part, std::forward(func)); - } - } - static void Case(Geometry::Tags::AnyGeometry, const Geometry&, FUNC &&) { } - }; - Match(geom, std::forward(func)); + struct op { + static void Case(Geometry::Tags::LineString, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiLineString, const Geometry &geom, FUNC &&func) { + for (auto &part : geom) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + for (auto &part : geom) { + Match(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry &, FUNC &&) { + } + }; + Match(geom, std::forward(func)); } +template +inline void Geometry::ExtractPolygons(const Geometry &geom, FUNC &&func) { + struct op { + static void Case(Geometry::Tags::Polygon, const Geometry &geom, FUNC &&func) { + func(geom); + } + static void Case(Geometry::Tags::MultiPolygon, const Geometry &geom, FUNC &&func) { + for (auto &part : geom) { + func(part); + } + } + static void Case(Geometry::Tags::GeometryCollection, const Geometry &geom, FUNC &&func) { + for (auto &part : geom) { + Match(part, std::forward(func)); + } + } + static void Case(Geometry::Tags::AnyGeometry, const Geometry &, FUNC &&) { + } + }; + Match(geom, std::forward(func)); +} inline bool Geometry::IsEmpty(const Geometry &geom) { - struct op { - static bool Case (Geometry::Tags::SinglePartGeometry, const Geometry &geom) { - return geom.data_count == 0; - } - static bool Case (Geometry::Tags::MultiPartGeometry, const Geometry &geom) { - for(const auto &part : geom) { - if(!Geometry::Match(part)) { - return false; - } - } - return true; - } - }; - return Geometry::Match(geom); + struct op { + static bool Case(Geometry::Tags::SinglePartGeometry, const Geometry &geom) { + return geom.data_count == 0; + } + static bool Case(Geometry::Tags::MultiPartGeometry, const Geometry &geom) { + for (const auto &part : geom) { + if (!Geometry::Match(part)) { + return false; + } + } + return true; + } + }; + return Geometry::Match(geom); } inline uint32_t Geometry::GetDimension(const Geometry &geom, bool ignore_empty) { - if (ignore_empty && Geometry::IsEmpty(geom)) { - return 0; - } - struct op { - static uint32_t Case(Geometry::Tags::Point, const Geometry&, bool) { - return 0; - } - static uint32_t Case(Geometry::Tags::LineString, const Geometry&, bool) { - return 1; - } - static uint32_t Case(Geometry::Tags::Polygon, const Geometry&, bool) { - return 2; - } - static uint32_t Case(Geometry::Tags::MultiPoint, const Geometry&, bool) { - return 0; - } - static uint32_t Case(Geometry::Tags::MultiLineString, const Geometry&, bool) { - return 1; - } - static uint32_t Case(Geometry::Tags::MultiPolygon, const Geometry&, bool) { - return 2; - } - static uint32_t Case(Geometry::Tags::GeometryCollection, const Geometry& geom, bool ignore_empty) { - uint32_t max_dimension = 0; - for(const auto &p : geom) { - max_dimension = std::max(max_dimension, Geometry::GetDimension(p, ignore_empty)); - } - return max_dimension; - } - }; - return Geometry::Match(geom, ignore_empty); + if (ignore_empty && Geometry::IsEmpty(geom)) { + return 0; + } + struct op { + static uint32_t Case(Geometry::Tags::Point, const Geometry &, bool) { + return 0; + } + static uint32_t Case(Geometry::Tags::LineString, const Geometry &, bool) { + return 1; + } + static uint32_t Case(Geometry::Tags::Polygon, const Geometry &, bool) { + return 2; + } + static uint32_t Case(Geometry::Tags::MultiPoint, const Geometry &, bool) { + return 0; + } + static uint32_t Case(Geometry::Tags::MultiLineString, const Geometry &, bool) { + return 1; + } + static uint32_t Case(Geometry::Tags::MultiPolygon, const Geometry &, bool) { + return 2; + } + static uint32_t Case(Geometry::Tags::GeometryCollection, const Geometry &geom, bool ignore_empty) { + uint32_t max_dimension = 0; + for (const auto &p : geom) { + max_dimension = std::max(max_dimension, Geometry::GetDimension(p, ignore_empty)); + } + return max_dimension; + } + }; + return Geometry::Match(geom, ignore_empty); } //------------------------------------------------------------------------------ @@ -313,92 +366,130 @@ inline uint32_t Geometry::GetDimension(const Geometry &geom, bool ignore_empty) //------------------------------------------------------------------------------ class PartView { private: - Geometry* beg_ptr; - Geometry* end_ptr; + Geometry *beg_ptr; + Geometry *end_ptr; + public: - PartView(Geometry *beg, Geometry *end) : beg_ptr(beg), end_ptr(end) {} - Geometry* begin() { return beg_ptr; } - Geometry* end() { return end_ptr; } - Geometry& operator[](uint32_t index) { return beg_ptr[index]; } + PartView(Geometry *beg, Geometry *end) : beg_ptr(beg), end_ptr(end) { + } + Geometry *begin() { + return beg_ptr; + } + Geometry *end() { + return end_ptr; + } + Geometry &operator[](uint32_t index) { + return beg_ptr[index]; + } }; class ConstPartView { private: - const Geometry* beg_ptr; - const Geometry* end_ptr; + const Geometry *beg_ptr; + const Geometry *end_ptr; + public: - ConstPartView(const Geometry *beg, const Geometry *end) : beg_ptr(beg), end_ptr(end) {} - const Geometry* begin() { return beg_ptr; } - const Geometry* end() { return end_ptr; } - const Geometry& operator[](uint32_t index) { return beg_ptr[index]; } + ConstPartView(const Geometry *beg, const Geometry *end) : beg_ptr(beg), end_ptr(end) { + } + const Geometry *begin() { + return beg_ptr; + } + const Geometry *end() { + return end_ptr; + } + const Geometry &operator[](uint32_t index) { + return beg_ptr[index]; + } }; class VertexView { private: - data_ptr_t beg_ptr; - data_ptr_t end_ptr; - uint32_t vertex_size; + data_ptr_t beg_ptr; + data_ptr_t end_ptr; + uint32_t vertex_size; + public: - VertexView(data_ptr_t beg, data_ptr_t end, uint32_t vertex_size) - : beg_ptr(beg), end_ptr(end), vertex_size(vertex_size) {} - - StridedPtr begin() { return { beg_ptr, vertex_size }; } - StridedPtr end() { return { end_ptr, vertex_size }; } - MisalignedRef operator[](uint32_t index) { - D_ASSERT(beg_ptr + index * vertex_size < end_ptr); - return { beg_ptr + index * vertex_size }; - } + VertexView(data_ptr_t beg, data_ptr_t end, uint32_t vertex_size) + : beg_ptr(beg), end_ptr(end), vertex_size(vertex_size) { + } + + StridedPtr begin() { + return {beg_ptr, vertex_size}; + } + StridedPtr end() { + return {end_ptr, vertex_size}; + } + MisalignedRef operator[](uint32_t index) { + D_ASSERT(beg_ptr + index * vertex_size < end_ptr); + return {beg_ptr + index * vertex_size}; + } }; class ConstVertexView { private: - const_data_ptr_t beg_ptr; - const_data_ptr_t end_ptr; - uint32_t vertex_size; + const_data_ptr_t beg_ptr; + const_data_ptr_t end_ptr; + uint32_t vertex_size; + public: - ConstVertexView(const_data_ptr_t beg, const_data_ptr_t end, uint32_t vertex_size) - : beg_ptr(beg), end_ptr(end), vertex_size(vertex_size) {} - - ConstStridedPtr begin() const { return { beg_ptr, vertex_size }; } - ConstStridedPtr end() const { return { end_ptr, vertex_size }; } - ConstMisalignedRef operator[](uint32_t index) { - D_ASSERT(beg_ptr + index * vertex_size < end_ptr); - return { beg_ptr + index * vertex_size }; - } + ConstVertexView(const_data_ptr_t beg, const_data_ptr_t end, uint32_t vertex_size) + : beg_ptr(beg), end_ptr(end), vertex_size(vertex_size) { + } + + ConstStridedPtr begin() const { + return {beg_ptr, vertex_size}; + } + ConstStridedPtr end() const { + return {end_ptr, vertex_size}; + } + ConstMisalignedRef operator[](uint32_t index) { + D_ASSERT(beg_ptr + index * vertex_size < end_ptr); + return {beg_ptr + index * vertex_size}; + } }; -template +template class TypedVertexView { - static_assert(V::IS_VERTEX, "V must be a vertex type"); - data_ptr_t beg_ptr; - data_ptr_t end_ptr; + static_assert(V::IS_VERTEX, "V must be a vertex type"); + data_ptr_t beg_ptr; + data_ptr_t end_ptr; + public: - TypedVertexView(data_ptr_t beg, data_ptr_t end) - : beg_ptr(beg), end_ptr(end) {} - - MisalignedPtr begin() { return { beg_ptr }; } - MisalignedPtr end() { return { end_ptr }; } - MisalignedRef operator[](uint32_t index) { - D_ASSERT(beg_ptr + index * sizeof(V) < end_ptr); - return { beg_ptr + index * sizeof(V) }; - } + TypedVertexView(data_ptr_t beg, data_ptr_t end) : beg_ptr(beg), end_ptr(end) { + } + + MisalignedPtr begin() { + return {beg_ptr}; + } + MisalignedPtr end() { + return {end_ptr}; + } + MisalignedRef operator[](uint32_t index) { + D_ASSERT(beg_ptr + index * sizeof(V) < end_ptr); + return {beg_ptr + index * sizeof(V)}; + } }; -template +template class ConstTypedVertexView { - static_assert(V::IS_VERTEX, "V must be a vertex type"); - const_data_ptr_t beg_ptr; - const_data_ptr_t end_ptr; + static_assert(V::IS_VERTEX, "V must be a vertex type"); + const_data_ptr_t beg_ptr; + const_data_ptr_t end_ptr; + public: - ConstTypedVertexView(const_data_ptr_t beg, const_data_ptr_t end) - : beg_ptr(beg), end_ptr(end) {} - - ConstMisalignedPtr begin() { return { beg_ptr }; } - ConstMisalignedPtr end() { return { end_ptr }; } - ConstMisalignedRef operator[](uint32_t index) { - D_ASSERT(beg_ptr + index * sizeof(V) < end_ptr); - return { beg_ptr + index * sizeof(V) }; - } + ConstTypedVertexView(const_data_ptr_t beg, const_data_ptr_t end) : beg_ptr(beg), end_ptr(end) { + } + + ConstMisalignedPtr begin() { + return {beg_ptr}; + } + ConstMisalignedPtr end() { + return {end_ptr}; + } + ConstMisalignedRef operator[](uint32_t index) { + D_ASSERT(beg_ptr + index * sizeof(V) < end_ptr); + return {beg_ptr + index * sizeof(V)}; + } }; //------------------------------------------------------------------------------ @@ -410,200 +501,204 @@ class ConstTypedVertexView { //------------------------------------------------------------------------------ struct SinglePartGeometry { - // Turn this geometry into a read-only reference to raw data - static void ReferenceData(Geometry &geom, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { - geom.data_count = count; - geom.data_ptr = const_cast(data); - geom.is_readonly = true; - geom.properties.SetZ(has_z); - geom.properties.SetM(has_m); - } - - // Turn this geometry into a read-only reference to another geometry, starting at the specified index - static void ReferenceData(Geometry &geom, const Geometry &other, uint32_t offset, uint32_t count) { - D_ASSERT(GeometryTypes::IsSinglePart(other.GetType())); - D_ASSERT(offset + count <= other.data_count); - auto vertex_size = other.properties.VertexSize(); - auto has_z = other.properties.HasZ(); - auto has_m = other.properties.HasM(); - ReferenceData(geom, other.data_ptr + offset * vertex_size, count, has_z, has_m); - } - - static void ReferenceData(Geometry &geom, const_data_ptr_t data, uint32_t count) { - ReferenceData(geom, data, count, geom.properties.HasZ(), geom.properties.HasM()); - } - - // Turn this geometry into a owning copy of raw data - static void CopyData(Geometry &geom, ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { - auto old_vertex_size = geom.properties.VertexSize(); - geom.properties.SetZ(has_z); - geom.properties.SetM(has_m); - auto new_vertex_size = geom.properties.VertexSize(); - if(geom.is_readonly) { - geom.data_ptr = alloc.AllocateAligned(count * new_vertex_size); - } else if(geom.data_count != count) { - geom.data_ptr = alloc.ReallocateAligned(geom.data_ptr, geom.data_count * old_vertex_size, count * new_vertex_size); - } - memcpy(geom.data_ptr, data, count * new_vertex_size); - geom.data_count = count; - geom.is_readonly = false; - } - - // Turn this geometry into a owning copy of another geometry, starting at the specified index - static void CopyData(Geometry &geom, ArenaAllocator &alloc, const Geometry &other, uint32_t offset, uint32_t count) { - D_ASSERT(GeometryTypes::IsSinglePart(other.GetType())); - D_ASSERT(offset + count <= other.data_count); - auto vertex_size = geom.properties.VertexSize(); - auto has_z = other.properties.HasZ(); - auto has_m = other.properties.HasM(); - CopyData(geom, alloc, other.data_ptr + offset * vertex_size, count, has_z, has_m); - } - - static void CopyData(Geometry &geom, ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count) { - CopyData(geom, alloc, data, count, geom.properties.HasZ(), geom.properties.HasM()); - } - - // Resize the geometry, truncating or extending with zeroed vertices as needed - static void Resize(Geometry &geom, ArenaAllocator &alloc, uint32_t new_count); - - // Append the data from another geometry - static void Append(Geometry &geom, ArenaAllocator &alloc, const Geometry &other); - - // Append the data from multiple other geometries - static void Append(Geometry &geom, ArenaAllocator &alloc, const Geometry *others, uint32_t others_count); - - // Force the geometry to have a specific vertex type, resizing or shrinking the data as needed - static void SetVertexType(Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z = 0, double default_m = 0); - - // If this geometry is read-only, make it mutable by copying the data - static void MakeMutable(Geometry &geom, ArenaAllocator &alloc); - - // Print this geometry as a string, starting at the specified index and printing the specified number of vertices - // (useful for debugging) - static string ToString(const Geometry &geom, uint32_t start = 0, uint32_t count = 0); - - // Check if the geometry is closed (first and last vertex are the same) - // A geometry with 1 vertex is considered closed, 0 vertices are considered open - static bool IsClosed(const Geometry &geom); - static bool IsEmpty(const Geometry &geom); - - // Return the planar length of the geometry - static double Length(const Geometry &geom); - - static VertexXY GetVertex(const Geometry &geom, uint32_t index); - static void SetVertex(Geometry &geom, uint32_t index, const VertexXY &vertex); - - template - static V GetVertex(const Geometry &geom, uint32_t index); - - template - static void SetVertex(Geometry &geom, uint32_t index, const V &vertex); - - template - static MisalignedRef Vertex(Geometry &geom, uint32_t index); - static MisalignedRef Vertex(Geometry &geom, uint32_t index); - - template - static ConstMisalignedRef Vertex(const Geometry &geom, uint32_t index); - static ConstMisalignedRef Vertex(const Geometry &geom, uint32_t index); - - static uint32_t VertexCount(const Geometry &geom); - static uint32_t VertexSize(const Geometry &geom); - static uint32_t ByteSize(const Geometry &geom); - - static VertexView Vertices(Geometry &geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - auto count = VertexCount(geom); - auto size = VertexSize(geom); - return { geom.GetData(), geom.GetData() + count * size, size }; - } - - static ConstVertexView Vertices(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - auto count = VertexCount(geom); - auto size = VertexSize(geom); - return { geom.GetData(), geom.GetData() + count * size, size }; - } + // Turn this geometry into a read-only reference to raw data + static void ReferenceData(Geometry &geom, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { + geom.data_count = count; + geom.data_ptr = const_cast(data); + geom.is_readonly = true; + geom.properties.SetZ(has_z); + geom.properties.SetM(has_m); + } + + // Turn this geometry into a read-only reference to another geometry, starting at the specified index + static void ReferenceData(Geometry &geom, const Geometry &other, uint32_t offset, uint32_t count) { + D_ASSERT(GeometryTypes::IsSinglePart(other.GetType())); + D_ASSERT(offset + count <= other.data_count); + auto vertex_size = other.properties.VertexSize(); + auto has_z = other.properties.HasZ(); + auto has_m = other.properties.HasM(); + ReferenceData(geom, other.data_ptr + offset * vertex_size, count, has_z, has_m); + } + + static void ReferenceData(Geometry &geom, const_data_ptr_t data, uint32_t count) { + ReferenceData(geom, data, count, geom.properties.HasZ(), geom.properties.HasM()); + } + + // Turn this geometry into a owning copy of raw data + static void CopyData(Geometry &geom, ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, + bool has_m) { + auto old_vertex_size = geom.properties.VertexSize(); + geom.properties.SetZ(has_z); + geom.properties.SetM(has_m); + auto new_vertex_size = geom.properties.VertexSize(); + if (geom.is_readonly) { + geom.data_ptr = alloc.AllocateAligned(count * new_vertex_size); + } else if (geom.data_count != count) { + geom.data_ptr = + alloc.ReallocateAligned(geom.data_ptr, geom.data_count * old_vertex_size, count * new_vertex_size); + } + memcpy(geom.data_ptr, data, count * new_vertex_size); + geom.data_count = count; + geom.is_readonly = false; + } + + // Turn this geometry into a owning copy of another geometry, starting at the specified index + static void CopyData(Geometry &geom, ArenaAllocator &alloc, const Geometry &other, uint32_t offset, + uint32_t count) { + D_ASSERT(GeometryTypes::IsSinglePart(other.GetType())); + D_ASSERT(offset + count <= other.data_count); + auto vertex_size = geom.properties.VertexSize(); + auto has_z = other.properties.HasZ(); + auto has_m = other.properties.HasM(); + CopyData(geom, alloc, other.data_ptr + offset * vertex_size, count, has_z, has_m); + } + + static void CopyData(Geometry &geom, ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count) { + CopyData(geom, alloc, data, count, geom.properties.HasZ(), geom.properties.HasM()); + } + + // Resize the geometry, truncating or extending with zeroed vertices as needed + static void Resize(Geometry &geom, ArenaAllocator &alloc, uint32_t new_count); + + // Append the data from another geometry + static void Append(Geometry &geom, ArenaAllocator &alloc, const Geometry &other); + + // Append the data from multiple other geometries + static void Append(Geometry &geom, ArenaAllocator &alloc, const Geometry *others, uint32_t others_count); + + // Force the geometry to have a specific vertex type, resizing or shrinking the data as needed + static void SetVertexType(Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z = 0, + double default_m = 0); + + // If this geometry is read-only, make it mutable by copying the data + static void MakeMutable(Geometry &geom, ArenaAllocator &alloc); + + // Print this geometry as a string, starting at the specified index and printing the specified number of vertices + // (useful for debugging) + static string ToString(const Geometry &geom, uint32_t start = 0, uint32_t count = 0); + + // Check if the geometry is closed (first and last vertex are the same) + // A geometry with 1 vertex is considered closed, 0 vertices are considered open + static bool IsClosed(const Geometry &geom); + static bool IsEmpty(const Geometry &geom); + + // Return the planar length of the geometry + static double Length(const Geometry &geom); + + static VertexXY GetVertex(const Geometry &geom, uint32_t index); + static void SetVertex(Geometry &geom, uint32_t index, const VertexXY &vertex); + + template + static V GetVertex(const Geometry &geom, uint32_t index); + + template + static void SetVertex(Geometry &geom, uint32_t index, const V &vertex); + + template + static MisalignedRef Vertex(Geometry &geom, uint32_t index); + static MisalignedRef Vertex(Geometry &geom, uint32_t index); + + template + static ConstMisalignedRef Vertex(const Geometry &geom, uint32_t index); + static ConstMisalignedRef Vertex(const Geometry &geom, uint32_t index); + + static uint32_t VertexCount(const Geometry &geom); + static uint32_t VertexSize(const Geometry &geom); + static uint32_t ByteSize(const Geometry &geom); + + static VertexView Vertices(Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + auto count = VertexCount(geom); + auto size = VertexSize(geom); + return {geom.GetData(), geom.GetData() + count * size, size}; + } + + static ConstVertexView Vertices(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + auto count = VertexCount(geom); + auto size = VertexSize(geom); + return {geom.GetData(), geom.GetData() + count * size, size}; + } }; inline VertexXY SinglePartGeometry::GetVertex(const Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(index < geom.data_count); - return Load(geom.GetData() + index * geom.GetProperties().VertexSize()); + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return Load(geom.GetData() + index * geom.GetProperties().VertexSize()); } inline void SinglePartGeometry::SetVertex(Geometry &geom, uint32_t index, const VertexXY &vertex) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(index < geom.data_count); - Store(vertex, geom.GetData() + index * geom.GetProperties().VertexSize()); + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(index < geom.data_count); + Store(vertex, geom.GetData() + index * geom.GetProperties().VertexSize()); } -template +template inline V SinglePartGeometry::GetVertex(const Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); - D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); - D_ASSERT(index < geom.data_count); - return Load(geom.GetData() + index * sizeof(V)); + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); + D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); + D_ASSERT(index < geom.data_count); + return Load(geom.GetData() + index * sizeof(V)); } -template +template inline void SinglePartGeometry::SetVertex(Geometry &geom, uint32_t index, const V &vertex) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); - D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); - D_ASSERT(index < geom.data_count); - Store(vertex, geom.GetData() + index * sizeof(V)); + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); + D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); + D_ASSERT(index < geom.data_count); + Store(vertex, geom.GetData() + index * sizeof(V)); } -template +template inline MisalignedRef SinglePartGeometry::Vertex(Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); - D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); - D_ASSERT(index < geom.data_count); - return { geom.GetData() + index * sizeof(V) }; + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); + D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); + D_ASSERT(index < geom.data_count); + return {geom.GetData() + index * sizeof(V)}; } inline MisalignedRef SinglePartGeometry::Vertex(Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(index < geom.data_count); - return { geom.GetData() + index * geom.GetProperties().VertexSize() }; + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return {geom.GetData() + index * geom.GetProperties().VertexSize()}; } -template +template inline ConstMisalignedRef SinglePartGeometry::Vertex(const Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); - D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); - D_ASSERT(index < geom.data_count); - return { geom.GetData() + index * sizeof(V) }; + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); + D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); + D_ASSERT(index < geom.data_count); + return {geom.GetData() + index * sizeof(V)}; } inline ConstMisalignedRef SinglePartGeometry::Vertex(const Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(index < geom.data_count); - return { geom.GetData() + index * geom.GetProperties().VertexSize() }; + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return {geom.GetData() + index * geom.GetProperties().VertexSize()}; } inline uint32_t SinglePartGeometry::VertexCount(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - return geom.data_count; + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + return geom.data_count; } inline uint32_t SinglePartGeometry::VertexSize(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - return geom.GetProperties().VertexSize(); + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + return geom.GetProperties().VertexSize(); } inline uint32_t SinglePartGeometry::ByteSize(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - return geom.data_count * geom.GetProperties().VertexSize(); + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + return geom.data_count * geom.GetProperties().VertexSize(); } inline bool SinglePartGeometry::IsEmpty(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - return geom.data_count == 0; + D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); + return geom.data_count == 0; } //------------------------------------------------------------------------------ @@ -611,52 +706,52 @@ inline bool SinglePartGeometry::IsEmpty(const Geometry &geom) { //------------------------------------------------------------------------------ struct MultiPartGeometry { - // static void Resize(Geometry &geom, ArenaAllocator &alloc, uint32_t new_count); - - static uint32_t PartCount(const Geometry &geom); - static Geometry& Part(Geometry &geom, uint32_t index); - static const Geometry& Part(const Geometry &geom, uint32_t index); - static PartView Parts(Geometry &geom); - static ConstPartView Parts(const Geometry &geom); - - static bool IsEmpty(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); - for(uint32_t i = 0; i < geom.data_count; i++) { - if(!Geometry::IsEmpty(Part(geom, i))) { - return false; - } - } - return true; - } + // static void Resize(Geometry &geom, ArenaAllocator &alloc, uint32_t new_count); + + static uint32_t PartCount(const Geometry &geom); + static Geometry &Part(Geometry &geom, uint32_t index); + static const Geometry &Part(const Geometry &geom, uint32_t index); + static PartView Parts(Geometry &geom); + static ConstPartView Parts(const Geometry &geom); + + static bool IsEmpty(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + for (uint32_t i = 0; i < geom.data_count; i++) { + if (!Geometry::IsEmpty(Part(geom, i))) { + return false; + } + } + return true; + } }; inline uint32_t MultiPartGeometry::PartCount(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); - return geom.data_count; + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + return geom.data_count; } -inline Geometry& MultiPartGeometry::Part(Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); - D_ASSERT(index < geom.data_count); - return *reinterpret_cast(geom.GetData() + index * sizeof(Geometry)); +inline Geometry &MultiPartGeometry::Part(Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return *reinterpret_cast(geom.GetData() + index * sizeof(Geometry)); } -inline const Geometry& MultiPartGeometry::Part(const Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); - D_ASSERT(index < geom.data_count); - return *reinterpret_cast(geom.GetData() + index * sizeof(Geometry)); +inline const Geometry &MultiPartGeometry::Part(const Geometry &geom, uint32_t index) { + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + D_ASSERT(index < geom.data_count); + return *reinterpret_cast(geom.GetData() + index * sizeof(Geometry)); } inline PartView MultiPartGeometry::Parts(Geometry &geom) { - D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); - auto ptr = reinterpret_cast(geom.GetData()); - return { ptr, ptr + geom.data_count }; + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + auto ptr = reinterpret_cast(geom.GetData()); + return {ptr, ptr + geom.data_count}; } inline ConstPartView MultiPartGeometry::Parts(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); - auto ptr = reinterpret_cast(geom.GetData()); - return { ptr, ptr + geom.data_count }; + D_ASSERT(GeometryTypes::IsMultiPart(geom.GetType())); + auto ptr = reinterpret_cast(geom.GetData()); + return {ptr, ptr + geom.data_count}; } //------------------------------------------------------------------------------ @@ -664,332 +759,334 @@ inline ConstPartView MultiPartGeometry::Parts(const Geometry &geom) { //------------------------------------------------------------------------------ struct CollectionGeometry : public MultiPartGeometry { protected: - static Geometry Create(ArenaAllocator &alloc, GeometryType type, vector &items, bool has_z, bool has_m) { - D_ASSERT(GeometryTypes::IsCollection(type)); - auto collection = Geometry::Create(alloc, type, items.size(), has_z, has_m); - for(uint32_t i = 0; i < items.size(); i++) { - CollectionGeometry::Part(collection, i) = std::move(items[i]); - } - return collection; - } + static Geometry Create(ArenaAllocator &alloc, GeometryType type, vector &items, bool has_z, bool has_m) { + D_ASSERT(GeometryTypes::IsCollection(type)); + auto collection = Geometry::Create(alloc, type, items.size(), has_z, has_m); + for (uint32_t i = 0; i < items.size(); i++) { + CollectionGeometry::Part(collection, i) = std::move(items[i]); + } + return collection; + } }; //------------------------------------------------------------------------------ // Point //------------------------------------------------------------------------------ struct Point : public SinglePartGeometry { - static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); - template - static Geometry CreateFromVertex(ArenaAllocator &alloc, const V &vertex); + template + static Geometry CreateFromVertex(ArenaAllocator &alloc, const V &vertex); - static Geometry CreateFromCopy(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { - auto point = Point::Create(alloc, 1, has_z, has_m); - SinglePartGeometry::CopyData(point, alloc, data, count, has_z, has_m); - return point; - } + static Geometry CreateFromCopy(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, + bool has_m) { + auto point = Point::Create(alloc, 1, has_z, has_m); + SinglePartGeometry::CopyData(point, alloc, data, count, has_z, has_m); + return point; + } - // Methods - template - static V GetVertex(const Geometry &geom); + // Methods + template + static V GetVertex(const Geometry &geom); - template - static void SetVertex(Geometry &geom, const V &vertex); + template + static void SetVertex(Geometry &geom, const V &vertex); - // Constants - static const constexpr GeometryType TYPE = GeometryType::POINT; + // Constants + static const constexpr GeometryType TYPE = GeometryType::POINT; }; inline Geometry Point::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return Geometry::Create(alloc, TYPE, count, has_z, has_m); + return Geometry::Create(alloc, TYPE, count, has_z, has_m); } inline Geometry Point::CreateEmpty(bool has_z, bool has_m) { - return Geometry::CreateEmpty(TYPE, has_z, has_m); + return Geometry::CreateEmpty(TYPE, has_z, has_m); } -template +template inline Geometry Point::CreateFromVertex(ArenaAllocator &alloc, const V &vertex) { - auto point = Create(alloc, 1, V::HAS_Z, V::HAS_M); - Point::SetVertex(point, vertex); - return point; + auto point = Create(alloc, 1, V::HAS_Z, V::HAS_M); + Point::SetVertex(point, vertex); + return point; } -template +template inline V Point::GetVertex(const Geometry &geom) { - D_ASSERT(geom.GetType() == TYPE); - D_ASSERT(geom.Count() == 1); - D_ASSERT(geom.GetProperties().HasZ() == V::HAS_Z); - D_ASSERT(geom.GetProperties().HasM() == V::HAS_M); - return SinglePartGeometry::GetVertex(geom, 0); + D_ASSERT(geom.GetType() == TYPE); + D_ASSERT(geom.Count() == 1); + D_ASSERT(geom.GetProperties().HasZ() == V::HAS_Z); + D_ASSERT(geom.GetProperties().HasM() == V::HAS_M); + return SinglePartGeometry::GetVertex(geom, 0); } -template +template void Point::SetVertex(Geometry &geom, const V &vertex) { - D_ASSERT(geom.GetType() == TYPE); - D_ASSERT(geom.Count() == 1); - D_ASSERT(geom.GetProperties().HasZ() == V::HAS_Z); - D_ASSERT(geom.GetProperties().HasM() == V::HAS_M); - SinglePartGeometry::SetVertex(geom, 0, vertex); + D_ASSERT(geom.GetType() == TYPE); + D_ASSERT(geom.Count() == 1); + D_ASSERT(geom.GetProperties().HasZ() == V::HAS_Z); + D_ASSERT(geom.GetProperties().HasM() == V::HAS_M); + SinglePartGeometry::SetVertex(geom, 0, vertex); } //------------------------------------------------------------------------------ // LineString //------------------------------------------------------------------------------ struct LineString : public SinglePartGeometry { - static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(bool has_z, bool has_m); - - static Geometry CreateFromCopy(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, bool has_m) { - auto line = LineString::Create(alloc, 1, has_z, has_m); - SinglePartGeometry::CopyData(line, alloc, data, count, has_z, has_m); - return line; - } - - // TODO: Wrap - // Create a new LineString referencing a slice of the this linestring - static Geometry GetSliceAsReference(const Geometry &geom, uint32_t start, uint32_t count) { - auto line = LineString::CreateEmpty(geom.GetProperties().HasZ(), geom.GetProperties().HasM()); - SinglePartGeometry::ReferenceData(line, geom, start, count); - return line; - } - - // TODO: Wrap - // Create a new LineString referencing a single point in the this linestring - static Geometry GetPointAsReference(const Geometry &geom, uint32_t index) { - auto count = index >= geom.Count() ? 0 : 1; - auto point = Point::CreateEmpty(geom.GetProperties().HasZ(), geom.GetProperties().HasM()); - SinglePartGeometry::ReferenceData(point, geom, index, count); - return point; - } - - // Constants - static const constexpr GeometryType TYPE = GeometryType::LINESTRING; + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + + static Geometry CreateFromCopy(ArenaAllocator &alloc, const_data_ptr_t data, uint32_t count, bool has_z, + bool has_m) { + auto line = LineString::Create(alloc, 1, has_z, has_m); + SinglePartGeometry::CopyData(line, alloc, data, count, has_z, has_m); + return line; + } + + // TODO: Wrap + // Create a new LineString referencing a slice of the this linestring + static Geometry GetSliceAsReference(const Geometry &geom, uint32_t start, uint32_t count) { + auto line = LineString::CreateEmpty(geom.GetProperties().HasZ(), geom.GetProperties().HasM()); + SinglePartGeometry::ReferenceData(line, geom, start, count); + return line; + } + + // TODO: Wrap + // Create a new LineString referencing a single point in the this linestring + static Geometry GetPointAsReference(const Geometry &geom, uint32_t index) { + auto count = index >= geom.Count() ? 0 : 1; + auto point = Point::CreateEmpty(geom.GetProperties().HasZ(), geom.GetProperties().HasM()); + SinglePartGeometry::ReferenceData(point, geom, index, count); + return point; + } + + // Constants + static const constexpr GeometryType TYPE = GeometryType::LINESTRING; }; inline Geometry LineString::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return Geometry::Create(alloc, TYPE, count, has_z, has_m); + return Geometry::Create(alloc, TYPE, count, has_z, has_m); } inline Geometry LineString::CreateEmpty(bool has_z, bool has_m) { - return Geometry::CreateEmpty(TYPE, has_z, has_m); + return Geometry::CreateEmpty(TYPE, has_z, has_m); } //------------------------------------------------------------------------------ // LinearRing (special case of LineString) //------------------------------------------------------------------------------ struct LinearRing : public LineString { - static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); - // Methods - static bool IsClosed(const Geometry &geom); + // Methods + static bool IsClosed(const Geometry &geom); - // Constants - // TODO: We dont have a LinearRing type, so we use LineString for now - static const constexpr GeometryType TYPE = GeometryType::LINESTRING; + // Constants + // TODO: We dont have a LinearRing type, so we use LineString for now + static const constexpr GeometryType TYPE = GeometryType::LINESTRING; }; inline Geometry LinearRing::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - return LineString::Create(alloc, count, has_z, has_m); + return LineString::Create(alloc, count, has_z, has_m); } inline Geometry LinearRing::CreateEmpty(bool has_z, bool has_m) { - return LineString::CreateEmpty(has_z, has_m); + return LineString::CreateEmpty(has_z, has_m); } inline bool LinearRing::IsClosed(const Geometry &geom) { - D_ASSERT(geom.GetType() == TYPE); - // The difference between LineString is that a empty LinearRing is considered closed - if(LinearRing::IsEmpty(geom)) { - return true; - } - return LineString::IsClosed(geom); + D_ASSERT(geom.GetType() == TYPE); + // The difference between LineString is that a empty LinearRing is considered closed + if (LinearRing::IsEmpty(geom)) { + return true; + } + return LineString::IsClosed(geom); } //------------------------------------------------------------------------------ // Polygon //------------------------------------------------------------------------------ struct Polygon : public MultiPartGeometry { - // Constructors - static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(bool has_z, bool has_m); - static Geometry CreateFromBox(ArenaAllocator &alloc, double minx, double miny, double maxx, double maxy); + // Constructors + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry CreateFromBox(ArenaAllocator &alloc, double minx, double miny, double maxx, double maxy); - // Methods - static const Geometry& ExteriorRing(const Geometry &geom); - static Geometry& ExteriorRing(Geometry &geom); + // Methods + static const Geometry &ExteriorRing(const Geometry &geom); + static Geometry &ExteriorRing(Geometry &geom); - // Constants - static const constexpr GeometryType TYPE = GeometryType::POLYGON; + // Constants + static const constexpr GeometryType TYPE = GeometryType::POLYGON; }; inline Geometry Polygon::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); - for(uint32_t i = 0; i < count; i++) { - // Placement new - new (&Polygon::Part(geom, i)) Geometry(GeometryType::LINESTRING, has_z, has_m); - } - return geom; + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for (uint32_t i = 0; i < count; i++) { + // Placement new + new (&Polygon::Part(geom, i)) Geometry(GeometryType::LINESTRING, has_z, has_m); + } + return geom; } inline Geometry Polygon::CreateEmpty(bool has_z, bool has_m) { - return Geometry::CreateEmpty(TYPE, has_z, has_m); + return Geometry::CreateEmpty(TYPE, has_z, has_m); } inline Geometry Polygon::CreateFromBox(ArenaAllocator &alloc, double minx, double miny, double maxx, double maxy) { - auto polygon = Polygon::Create(alloc, 1, false, false); - auto &ring = Polygon::Part(polygon, 0); - LineString::Resize(ring, alloc, 5); - LineString::SetVertex(ring, 0, { minx, miny }); - LineString::SetVertex(ring, 1, { miny, maxy }); - LineString::SetVertex(ring, 2, { maxx, maxy }); - LineString::SetVertex(ring, 3, { maxx, miny }); - LineString::SetVertex(ring, 4, { minx, miny }); - return polygon; + auto polygon = Polygon::Create(alloc, 1, false, false); + auto &ring = Polygon::Part(polygon, 0); + LineString::Resize(ring, alloc, 5); + LineString::SetVertex(ring, 0, {minx, miny}); + LineString::SetVertex(ring, 1, {miny, maxy}); + LineString::SetVertex(ring, 2, {maxx, maxy}); + LineString::SetVertex(ring, 3, {maxx, miny}); + LineString::SetVertex(ring, 4, {minx, miny}); + return polygon; } -inline Geometry& Polygon::ExteriorRing(Geometry &geom) { - D_ASSERT(geom.GetType() == TYPE); - D_ASSERT(Polygon::PartCount(geom) > 0); - return Polygon::Part(geom, 0); +inline Geometry &Polygon::ExteriorRing(Geometry &geom) { + D_ASSERT(geom.GetType() == TYPE); + D_ASSERT(Polygon::PartCount(geom) > 0); + return Polygon::Part(geom, 0); } -inline const Geometry& Polygon::ExteriorRing(const Geometry &geom) { - D_ASSERT(geom.GetType() == TYPE); - D_ASSERT(Polygon::PartCount(geom) > 0); - return Polygon::Part(geom, 0); +inline const Geometry &Polygon::ExteriorRing(const Geometry &geom) { + D_ASSERT(geom.GetType() == TYPE); + D_ASSERT(Polygon::PartCount(geom) > 0); + return Polygon::Part(geom, 0); } //------------------------------------------------------------------------------ // MultiPoint //------------------------------------------------------------------------------ struct MultiPoint : public CollectionGeometry { - static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(bool has_z, bool has_m); - static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); - // Constants - static const constexpr GeometryType TYPE = GeometryType::MULTIPOINT; + // Constants + static const constexpr GeometryType TYPE = GeometryType::MULTIPOINT; }; inline Geometry MultiPoint::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); - for(uint32_t i = 0; i < count; i++) { - // Placement new - new (&MultiPoint::Part(geom, i)) Geometry(GeometryType::POINT, has_z, has_m); - } - return geom; + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for (uint32_t i = 0; i < count; i++) { + // Placement new + new (&MultiPoint::Part(geom, i)) Geometry(GeometryType::POINT, has_z, has_m); + } + return geom; } inline Geometry MultiPoint::CreateEmpty(bool has_z, bool has_m) { - return Geometry::CreateEmpty(TYPE, has_z, has_m); + return Geometry::CreateEmpty(TYPE, has_z, has_m); } inline Geometry MultiPoint::Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m) { - return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); + return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } //------------------------------------------------------------------------------ // MultiLineString //------------------------------------------------------------------------------ struct MultiLineString : public CollectionGeometry { - static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(bool has_z, bool has_m); - static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); - static bool IsClosed(const Geometry &geom); + static bool IsClosed(const Geometry &geom); - // Constants - static const constexpr GeometryType TYPE = GeometryType::MULTILINESTRING; + // Constants + static const constexpr GeometryType TYPE = GeometryType::MULTILINESTRING; }; inline Geometry MultiLineString::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); - for(uint32_t i = 0; i < count; i++) { - // Placement new - new (&MultiLineString::Part(geom, i)) Geometry(GeometryType::LINESTRING, has_z, has_m); - } - return geom; + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for (uint32_t i = 0; i < count; i++) { + // Placement new + new (&MultiLineString::Part(geom, i)) Geometry(GeometryType::LINESTRING, has_z, has_m); + } + return geom; } inline Geometry MultiLineString::CreateEmpty(bool has_z, bool has_m) { - return Geometry::CreateEmpty(TYPE, has_z, has_m); + return Geometry::CreateEmpty(TYPE, has_z, has_m); } inline Geometry MultiLineString::Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m) { - return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); + return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } inline bool MultiLineString::IsClosed(const Geometry &geom) { - if(MultiLineString::PartCount(geom) == 0) { - return false; - } - for(auto &part : MultiLineString::Parts(geom)) { - if(!LineString::IsClosed(part)) { - return false; - } - } - return true; + if (MultiLineString::PartCount(geom) == 0) { + return false; + } + for (auto &part : MultiLineString::Parts(geom)) { + if (!LineString::IsClosed(part)) { + return false; + } + } + return true; } //------------------------------------------------------------------------------ // MultiPolygon //------------------------------------------------------------------------------ struct MultiPolygon : public CollectionGeometry { - static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(bool has_z, bool has_m); - static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); - // Constants - static const constexpr GeometryType TYPE = GeometryType::MULTIPOLYGON; + // Constants + static const constexpr GeometryType TYPE = GeometryType::MULTIPOLYGON; }; inline Geometry MultiPolygon::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); - for(uint32_t i = 0; i < count; i++) { - // Placement new - new (&MultiPolygon::Part(geom, i)) Geometry(GeometryType::POLYGON, has_z, has_m); - } - return geom; + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for (uint32_t i = 0; i < count; i++) { + // Placement new + new (&MultiPolygon::Part(geom, i)) Geometry(GeometryType::POLYGON, has_z, has_m); + } + return geom; } inline Geometry MultiPolygon::CreateEmpty(bool has_z, bool has_m) { - return Geometry::CreateEmpty(TYPE, has_z, has_m); + return Geometry::CreateEmpty(TYPE, has_z, has_m); } inline Geometry MultiPolygon::Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m) { - return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); + return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } //------------------------------------------------------------------------------ // GeometryCollection //------------------------------------------------------------------------------ struct GeometryCollection : public CollectionGeometry { - static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); - static Geometry CreateEmpty(bool has_z, bool has_m); - static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m); + static Geometry CreateEmpty(bool has_z, bool has_m); + static Geometry Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m); - // Constants - static const constexpr GeometryType TYPE = GeometryType::GEOMETRYCOLLECTION; + // Constants + static const constexpr GeometryType TYPE = GeometryType::GEOMETRYCOLLECTION; }; inline Geometry GeometryCollection::Create(ArenaAllocator &alloc, uint32_t count, bool has_z, bool has_m) { - auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); - for(uint32_t i = 0; i < count; i++) { - // Placement new - new (&GeometryCollection::Part(geom, i)) Geometry(GeometryType::GEOMETRYCOLLECTION, has_z, has_m); - } - return geom; + auto geom = Geometry::Create(alloc, TYPE, count, has_z, has_m); + for (uint32_t i = 0; i < count; i++) { + // Placement new + new (&GeometryCollection::Part(geom, i)) Geometry(GeometryType::GEOMETRYCOLLECTION, has_z, has_m); + } + return geom; } inline Geometry GeometryCollection::CreateEmpty(bool has_z, bool has_m) { - return Geometry::CreateEmpty(TYPE, has_z, has_m); + return Geometry::CreateEmpty(TYPE, has_z, has_m); } inline Geometry GeometryCollection::Create(ArenaAllocator &alloc, vector &items, bool has_z, bool has_m) { - return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); + return CollectionGeometry::Create(alloc, TYPE, items, has_z, has_m); } //------------------------------------------------------------------------------ diff --git a/spatial/include/spatial/core/geometry/geometry_type.hpp b/spatial/include/spatial/core/geometry/geometry_type.hpp index 2e58648e..855da92d 100644 --- a/spatial/include/spatial/core/geometry/geometry_type.hpp +++ b/spatial/include/spatial/core/geometry/geometry_type.hpp @@ -19,41 +19,41 @@ enum class GeometryType : uint8_t { }; struct GeometryTypes { - static bool IsSinglePart(GeometryType type) { - return type == GeometryType::POINT || type == GeometryType::LINESTRING; - } - - static bool IsMultiPart(GeometryType type) { - return type == GeometryType::POLYGON || - type == GeometryType::MULTIPOINT || type == GeometryType::MULTILINESTRING || - type == GeometryType::MULTIPOLYGON || type == GeometryType::GEOMETRYCOLLECTION; - } - - static bool IsCollection(GeometryType type) { - return type == GeometryType::MULTIPOINT || type == GeometryType::MULTILINESTRING || - type == GeometryType::MULTIPOLYGON || type == GeometryType::GEOMETRYCOLLECTION; - } - - static string ToString(GeometryType type) { - switch (type) { - case GeometryType::POINT: - return "POINT"; - case GeometryType::LINESTRING: - return "LINESTRING"; - case GeometryType::POLYGON: - return "POLYGON"; - case GeometryType::MULTIPOINT: - return "MULTIPOINT"; - case GeometryType::MULTILINESTRING: - return "MULTILINESTRING"; - case GeometryType::MULTIPOLYGON: - return "MULTIPOLYGON"; - case GeometryType::GEOMETRYCOLLECTION: - return "GEOMETRYCOLLECTION"; - default: - return StringUtil::Format("UNKNOWN(%d)", static_cast(type)); - } - } + static bool IsSinglePart(GeometryType type) { + return type == GeometryType::POINT || type == GeometryType::LINESTRING; + } + + static bool IsMultiPart(GeometryType type) { + return type == GeometryType::POLYGON || type == GeometryType::MULTIPOINT || + type == GeometryType::MULTILINESTRING || type == GeometryType::MULTIPOLYGON || + type == GeometryType::GEOMETRYCOLLECTION; + } + + static bool IsCollection(GeometryType type) { + return type == GeometryType::MULTIPOINT || type == GeometryType::MULTILINESTRING || + type == GeometryType::MULTIPOLYGON || type == GeometryType::GEOMETRYCOLLECTION; + } + + static string ToString(GeometryType type) { + switch (type) { + case GeometryType::POINT: + return "POINT"; + case GeometryType::LINESTRING: + return "LINESTRING"; + case GeometryType::POLYGON: + return "POLYGON"; + case GeometryType::MULTIPOINT: + return "MULTIPOINT"; + case GeometryType::MULTILINESTRING: + return "MULTILINESTRING"; + case GeometryType::MULTIPOLYGON: + return "MULTIPOLYGON"; + case GeometryType::GEOMETRYCOLLECTION: + return "GEOMETRYCOLLECTION"; + default: + return StringUtil::Format("UNKNOWN(%d)", static_cast(type)); + } + } }; enum class SerializedGeometryType : uint32_t { diff --git a/spatial/include/spatial/core/geometry/wkb_reader.hpp b/spatial/include/spatial/core/geometry/wkb_reader.hpp index 3f41613d..c50fdfc5 100644 --- a/spatial/include/spatial/core/geometry/wkb_reader.hpp +++ b/spatial/include/spatial/core/geometry/wkb_reader.hpp @@ -26,12 +26,12 @@ class WKBReader { // Geometries Geometry ReadPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - Geometry ReadLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - Geometry ReadPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - Geometry ReadMultiPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - Geometry ReadMultiLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - Geometry ReadMultiPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m); - Geometry ReadGeometryCollection(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadMultiPoint(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadMultiLineString(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadMultiPolygon(Cursor &cursor, bool little_endian, bool has_z, bool has_m); + Geometry ReadGeometryCollection(Cursor &cursor, bool little_endian, bool has_z, bool has_m); Geometry ReadGeometry(Cursor &cursor); public: diff --git a/spatial/include/spatial/core/geometry/wkt_reader.hpp b/spatial/include/spatial/core/geometry/wkt_reader.hpp index 75ac14bb..015ac9b2 100644 --- a/spatial/include/spatial/core/geometry/wkt_reader.hpp +++ b/spatial/include/spatial/core/geometry/wkt_reader.hpp @@ -25,14 +25,14 @@ class WKTReader { void Expect(char c); void ParseVertex(vector &coords); pair> ParseVertices(); - - Geometry ParsePoint(); + + Geometry ParsePoint(); Geometry ParseLineString(); - Geometry ParsePolygon(); - Geometry ParseMultiPoint(); - Geometry ParseMultiLineString(); - Geometry ParseMultiPolygon(); - Geometry ParseGeometryCollection(); + Geometry ParsePolygon(); + Geometry ParseMultiPoint(); + Geometry ParseMultiLineString(); + Geometry ParseMultiPolygon(); + Geometry ParseGeometryCollection(); void CheckZM(); Geometry ParseGeometry(); Geometry ParseWKT(); diff --git a/spatial/include/spatial/core/util/math.hpp b/spatial/include/spatial/core/util/math.hpp index 4743d878..ba916041 100644 --- a/spatial/include/spatial/core/util/math.hpp +++ b/spatial/include/spatial/core/util/math.hpp @@ -5,40 +5,40 @@ namespace spatial { namespace core { struct MathUtil { - static string format_coord(double d); - static string format_coord(double x, double y); - static string format_coord(double x, double y, double z); - static string format_coord(double x, double y, double z, double m); - - static inline float DoubleToFloatDown(double d) { - if (d > static_cast(std::numeric_limits::max())) { - return std::numeric_limits::max(); - } - if (d <= static_cast(std::numeric_limits::lowest())) { - return std::numeric_limits::lowest(); - } - - auto f = static_cast(d); - if (static_cast(f) <= d) { - return f; - } - return std::nextafter(f, std::numeric_limits::lowest()); - } - - static inline float DoubleToFloatUp(double d) { - if (d >= static_cast(std::numeric_limits::max())) { - return std::numeric_limits::max(); - } - if (d < static_cast(std::numeric_limits::lowest())) { - return std::numeric_limits::lowest(); - } - - auto f = static_cast(d); - if (static_cast(f) >= d) { - return f; - } - return std::nextafter(f, std::numeric_limits::max()); - } + static string format_coord(double d); + static string format_coord(double x, double y); + static string format_coord(double x, double y, double z); + static string format_coord(double x, double y, double z, double m); + + static inline float DoubleToFloatDown(double d) { + if (d > static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + if (d <= static_cast(std::numeric_limits::lowest())) { + return std::numeric_limits::lowest(); + } + + auto f = static_cast(d); + if (static_cast(f) <= d) { + return f; + } + return std::nextafter(f, std::numeric_limits::lowest()); + } + + static inline float DoubleToFloatUp(double d) { + if (d >= static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } + if (d < static_cast(std::numeric_limits::lowest())) { + return std::numeric_limits::lowest(); + } + + auto f = static_cast(d); + if (static_cast(f) >= d) { + return f; + } + return std::nextafter(f, std::numeric_limits::max()); + } }; } // namespace core diff --git a/spatial/include/spatial/core/util/misaligned_ptr.hpp b/spatial/include/spatial/core/util/misaligned_ptr.hpp index 1cf2f824..b3bd1df8 100644 --- a/spatial/include/spatial/core/util/misaligned_ptr.hpp +++ b/spatial/include/spatial/core/util/misaligned_ptr.hpp @@ -7,86 +7,150 @@ namespace spatial { namespace core { - template class MisalignedPtrBase { private: - PTR ptr_; + PTR ptr_; + public: - using iterator_category = std::random_access_iterator_tag; - using value_type = TYPE; - using difference_type = std::ptrdiff_t; - - reference operator*() const noexcept { return {ptr_}; } - reference operator[](std::size_t i) noexcept { return reference(ptr_ + i * sizeof(TYPE)); } - value_type operator[](std::size_t i) const noexcept { return Load(ptr_ + i * sizeof(TYPE)); } - - MisalignedPtrBase(PTR ptr) noexcept : ptr_(ptr) {} - MisalignedPtrBase operator++(int) noexcept { return MisalignedPtr(ptr_ + sizeof(TYPE)); } - MisalignedPtrBase operator--(int) noexcept { return MisalignedPtr(ptr_ - sizeof(TYPE)); } - MisalignedPtrBase operator+(difference_type d) noexcept { return MisalignedPtr(ptr_ + d * sizeof(TYPE)); } - MisalignedPtrBase operator-(difference_type d) noexcept { return MisalignedPtr(ptr_ - d * sizeof(TYPE)); } - - MisalignedPtrBase& operator++() noexcept { ptr_ += sizeof(TYPE); return *this; } - MisalignedPtrBase& operator--() noexcept { ptr_ -= sizeof(TYPE); return *this; } - MisalignedPtrBase& operator+=(difference_type d) noexcept { ptr_ += d * sizeof(TYPE); return *this; } - MisalignedPtrBase& operator-=(difference_type d) noexcept { ptr_ -= d * sizeof(TYPE); return *this; } - - bool operator==(MisalignedPtrBase const& other) noexcept { return ptr_ == other.ptr_; } - bool operator!=(MisalignedPtrBase const& other) noexcept { return ptr_ != other.ptr_; } + using iterator_category = std::random_access_iterator_tag; + using value_type = TYPE; + using difference_type = std::ptrdiff_t; + + reference operator*() const noexcept { + return {ptr_}; + } + reference operator[](std::size_t i) noexcept { + return reference(ptr_ + i * sizeof(TYPE)); + } + value_type operator[](std::size_t i) const noexcept { + return Load(ptr_ + i * sizeof(TYPE)); + } + + MisalignedPtrBase(PTR ptr) noexcept : ptr_(ptr) { + } + MisalignedPtrBase operator++(int) noexcept { + return MisalignedPtr(ptr_ + sizeof(TYPE)); + } + MisalignedPtrBase operator--(int) noexcept { + return MisalignedPtr(ptr_ - sizeof(TYPE)); + } + MisalignedPtrBase operator+(difference_type d) noexcept { + return MisalignedPtr(ptr_ + d * sizeof(TYPE)); + } + MisalignedPtrBase operator-(difference_type d) noexcept { + return MisalignedPtr(ptr_ - d * sizeof(TYPE)); + } + + MisalignedPtrBase &operator++() noexcept { + ptr_ += sizeof(TYPE); + return *this; + } + MisalignedPtrBase &operator--() noexcept { + ptr_ -= sizeof(TYPE); + return *this; + } + MisalignedPtrBase &operator+=(difference_type d) noexcept { + ptr_ += d * sizeof(TYPE); + return *this; + } + MisalignedPtrBase &operator-=(difference_type d) noexcept { + ptr_ -= d * sizeof(TYPE); + return *this; + } + + bool operator==(MisalignedPtrBase const &other) noexcept { + return ptr_ == other.ptr_; + } + bool operator!=(MisalignedPtrBase const &other) noexcept { + return ptr_ != other.ptr_; + } }; -template -class MisalignedPtr : public MisalignedPtrBase, MisalignedRef> { }; - -template -class ConstMisalignedPtr : public MisalignedPtrBase, ConstMisalignedRef> { }; +template +class MisalignedPtr : public MisalignedPtrBase, MisalignedRef> {}; +template +class ConstMisalignedPtr : public MisalignedPtrBase, ConstMisalignedRef> { +}; -template +template class StridedPtrBase { private: - PTR ptr; - size_t stride; + PTR ptr; + size_t stride; + public: - using iterator_category = std::random_access_iterator_tag; - using value_type = TYPE; - using difference_type = std::ptrdiff_t; - - reference operator*() const noexcept { return {ptr}; } - reference operator[](std::size_t i) noexcept { return reference(ptr + i * stride); } - value_type operator[](std::size_t i) const noexcept { return Load(ptr + i * stride); } - - StridedPtrBase(PTR ptr, size_t stride) noexcept : ptr(ptr), stride(stride) {} - StridedPtrBase operator++(int) noexcept { return StridedPtrBase(ptr + stride, stride); } - StridedPtrBase operator--(int) noexcept { return StridedPtrBase(ptr - stride, stride); } - StridedPtrBase operator+(difference_type d) noexcept { return StridedPtrBase(ptr + d * stride, stride); } - StridedPtrBase operator-(difference_type d) noexcept { return StridedPtrBase(ptr - d * stride, stride); } - - StridedPtrBase& operator++() noexcept { ptr += stride; return *this; } - StridedPtrBase& operator--() noexcept { ptr -= stride; return *this; } - StridedPtrBase& operator+=(difference_type d) noexcept { ptr += d * stride; return *this; } - StridedPtrBase& operator-=(difference_type d) noexcept { ptr -= d * stride; return *this; } - - bool operator==(StridedPtrBase const& other) noexcept { return ptr == other.ptr; } - bool operator!=(StridedPtrBase const& other) noexcept { return ptr != other.ptr; } + using iterator_category = std::random_access_iterator_tag; + using value_type = TYPE; + using difference_type = std::ptrdiff_t; + + reference operator*() const noexcept { + return {ptr}; + } + reference operator[](std::size_t i) noexcept { + return reference(ptr + i * stride); + } + value_type operator[](std::size_t i) const noexcept { + return Load(ptr + i * stride); + } + + StridedPtrBase(PTR ptr, size_t stride) noexcept : ptr(ptr), stride(stride) { + } + StridedPtrBase operator++(int) noexcept { + return StridedPtrBase(ptr + stride, stride); + } + StridedPtrBase operator--(int) noexcept { + return StridedPtrBase(ptr - stride, stride); + } + StridedPtrBase operator+(difference_type d) noexcept { + return StridedPtrBase(ptr + d * stride, stride); + } + StridedPtrBase operator-(difference_type d) noexcept { + return StridedPtrBase(ptr - d * stride, stride); + } + + StridedPtrBase &operator++() noexcept { + ptr += stride; + return *this; + } + StridedPtrBase &operator--() noexcept { + ptr -= stride; + return *this; + } + StridedPtrBase &operator+=(difference_type d) noexcept { + ptr += d * stride; + return *this; + } + StridedPtrBase &operator-=(difference_type d) noexcept { + ptr -= d * stride; + return *this; + } + + bool operator==(StridedPtrBase const &other) noexcept { + return ptr == other.ptr; + } + bool operator!=(StridedPtrBase const &other) noexcept { + return ptr != other.ptr; + } }; -template +template class StridedPtr : public StridedPtrBase, MisalignedRef> { public: - StridedPtr(data_ptr_t ptr, size_t stride) noexcept - : StridedPtrBase, MisalignedRef>(ptr, stride) {} + StridedPtr(data_ptr_t ptr, size_t stride) noexcept + : StridedPtrBase, MisalignedRef>(ptr, stride) { + } }; -template +template class ConstStridedPtr : public StridedPtrBase, ConstMisalignedRef> { - public: - ConstStridedPtr(const_data_ptr_t ptr, size_t stride) noexcept - : StridedPtrBase, ConstMisalignedRef>(ptr, stride) {} +public: + ConstStridedPtr(const_data_ptr_t ptr, size_t stride) noexcept + : StridedPtrBase, ConstMisalignedRef>(ptr, stride) { + } }; - } // namespace core } // namespace spatial \ No newline at end of file diff --git a/spatial/include/spatial/core/util/misaligned_ref.hpp b/spatial/include/spatial/core/util/misaligned_ref.hpp index 611c83e7..153c5a5f 100644 --- a/spatial/include/spatial/core/util/misaligned_ref.hpp +++ b/spatial/include/spatial/core/util/misaligned_ref.hpp @@ -8,33 +8,44 @@ namespace core { template class MisalignedRef { - using TYPE = typename std::remove_const::type; - data_ptr_t ptr_; + using TYPE = typename std::remove_const::type; + data_ptr_t ptr_; public: - MisalignedRef(data_ptr_t ptr) noexcept : ptr_(ptr) {} - - operator TYPE() const noexcept { return Load(ptr_); } - MisalignedRef& operator=(TYPE const& v) noexcept { - Store(v, ptr_); - return *this; - } - void reset(data_ptr_t ptr) noexcept { ptr_ = ptr; } - data_ptr_t ptr() const noexcept { return ptr_; } + MisalignedRef(data_ptr_t ptr) noexcept : ptr_(ptr) { + } + + operator TYPE() const noexcept { + return Load(ptr_); + } + MisalignedRef &operator=(TYPE const &v) noexcept { + Store(v, ptr_); + return *this; + } + void reset(data_ptr_t ptr) noexcept { + ptr_ = ptr; + } + data_ptr_t ptr() const noexcept { + return ptr_; + } }; - template class ConstMisalignedRef { - using TYPE = typename std::remove_const::type; - const_data_ptr_t ptr_; + using TYPE = typename std::remove_const::type; + const_data_ptr_t ptr_; + public: - ConstMisalignedRef(const_data_ptr_t ptr) noexcept : ptr_(ptr) {} - operator TYPE() const noexcept { return Load(ptr_); } - const_data_ptr_t ptr() const noexcept { return ptr_; } + ConstMisalignedRef(const_data_ptr_t ptr) noexcept : ptr_(ptr) { + } + operator TYPE() const noexcept { + return Load(ptr_); + } + const_data_ptr_t ptr() const noexcept { + return ptr_; + } }; - } // namespace core } // namespace spatial \ No newline at end of file diff --git a/spatial/src/spatial/core/functions/cast/geometry_cast.cpp b/spatial/src/spatial/core/functions/cast/geometry_cast.cpp index 44b911d2..3558c3db 100644 --- a/spatial/src/spatial/core/functions/cast/geometry_cast.cpp +++ b/spatial/src/spatial/core/functions/cast/geometry_cast.cpp @@ -71,9 +71,9 @@ static bool LineString2DToGeometryCast(Vector &source, Vector &result, idx_t cou for (idx_t i = 0; i < line.length; i++) { auto x = x_data[line.offset + i]; auto y = y_data[line.offset + i]; - LineString::SetVertex(geom, i, VertexXY {x, y}); + LineString::SetVertex(geom, i, VertexXY {x, y}); } - return Geometry::Serialize(geom, result); + return Geometry::Serialize(geom, result); }); return true; } @@ -134,11 +134,11 @@ static bool Polygon2DToGeometryCast(Vector &source, Vector &result, idx_t count, for (idx_t i = 0; i < poly.length; i++) { auto ring = ring_entries[poly.offset + i]; auto &ring_array = Polygon::Part(geom, i); - LineString::Resize(ring_array, arena, ring.length); + LineString::Resize(ring_array, arena, ring.length); for (idx_t j = 0; j < ring.length; j++) { auto x = x_data[ring.offset + j]; auto y = y_data[ring.offset + j]; - LineString::SetVertex(ring_array, j, VertexXY {x, y}); + LineString::SetVertex(ring_array, j, VertexXY {x, y}); } } return Geometry::Serialize(geom, result); diff --git a/spatial/src/spatial/core/functions/cast/varchar_cast.cpp b/spatial/src/spatial/core/functions/cast/varchar_cast.cpp index a341f9ee..c11a6f7a 100644 --- a/spatial/src/spatial/core/functions/cast/varchar_cast.cpp +++ b/spatial/src/spatial/core/functions/cast/varchar_cast.cpp @@ -114,7 +114,7 @@ void CoreVectorOperations::Box2DToVarchar(Vector &source, Vector &result, idx_t GenericExecutor::ExecuteUnary(source, result, count, [&](BOX_TYPE &box) { return StringVector::AddString(result, StringUtil::Format("BOX(%s, %s)", MathUtil::format_coord(box.a_val, box.b_val), - MathUtil::format_coord(box.c_val, box.d_val))); + MathUtil::format_coord(box.c_val, box.d_val))); }); } @@ -324,8 +324,8 @@ static bool TextToGeometryCast(Vector &source, Vector &result, idx_t count, Cast UnaryExecutor::ExecuteWithNulls( source, result, count, [&](string_t &wkt, ValidityMask &mask, idx_t idx) { try { - auto geom = reader.Parse(wkt); - return Geometry::Serialize(geom, result); + auto geom = reader.Parse(wkt); + return Geometry::Serialize(geom, result); } catch (InvalidInputException &e) { if (success) { success = false; diff --git a/spatial/src/spatial/core/functions/cast/wkb_cast.cpp b/spatial/src/spatial/core/functions/cast/wkb_cast.cpp index 1d2ed1f1..63e829d5 100644 --- a/spatial/src/spatial/core/functions/cast/wkb_cast.cpp +++ b/spatial/src/spatial/core/functions/cast/wkb_cast.cpp @@ -25,8 +25,8 @@ static bool WKBToGeometryCast(Vector &source, Vector &result, idx_t count, CastP UnaryExecutor::ExecuteWithNulls( source, result, count, [&](string_t input, ValidityMask &mask, idx_t idx) { try { - auto geom = reader.Deserialize(input); - return Geometry::Serialize(geom, result); + auto geom = reader.Deserialize(input); + return Geometry::Serialize(geom, result); } catch (SerializationException &e) { if (success) { success = false; diff --git a/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp b/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp index 3aa1bab4..90a17047 100644 --- a/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp @@ -66,7 +66,7 @@ static void VerticesToGeoJSON(const Geometry &vertices, yyjson_mut_doc *doc, yyj if (haz_z && has_m) { for (uint32_t i = 0; i < vertices.Count(); i++) { auto coord = yyjson_mut_arr(doc); - auto vert = SinglePartGeometry::GetVertex(vertices, i); + auto vert = SinglePartGeometry::GetVertex(vertices, i); yyjson_mut_arr_add_real(doc, coord, vert.x); yyjson_mut_arr_add_real(doc, coord, vert.y); yyjson_mut_arr_add_real(doc, coord, vert.z); @@ -148,12 +148,12 @@ struct ToGeoJSONFunctor { auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - for(uint32_t i = 0; i < Polygon::PartCount(poly); i++) { - auto &ring = Polygon::Part(poly, i); - auto ring_coords = yyjson_mut_arr(doc); - VerticesToGeoJSON(ring, doc, ring_coords); - yyjson_mut_arr_append(coords, ring_coords); - } + for (uint32_t i = 0; i < Polygon::PartCount(poly); i++) { + auto &ring = Polygon::Part(poly, i); + auto ring_coords = yyjson_mut_arr(doc); + VerticesToGeoJSON(ring, doc, ring_coords); + yyjson_mut_arr_append(coords, ring_coords); + } } // MultiPoint @@ -162,10 +162,10 @@ struct ToGeoJSONFunctor { auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - for(uint32_t i = 0; i < MultiPoint::PartCount(mpoint); i++) { - auto &point = MultiPoint::Part(mpoint, i); - VerticesToGeoJSON(point, doc, coords); - } + for (uint32_t i = 0; i < MultiPoint::PartCount(mpoint); i++) { + auto &point = MultiPoint::Part(mpoint, i); + VerticesToGeoJSON(point, doc, coords); + } } // MultiLineString @@ -175,12 +175,12 @@ struct ToGeoJSONFunctor { auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - for(uint32_t i = 0; i < MultiLineString::PartCount(mline); i++) { - auto &line = MultiLineString::Part(mline, i); - auto line_coords = yyjson_mut_arr(doc); - VerticesToGeoJSON(line, doc, line_coords); - yyjson_mut_arr_append(coords, line_coords); - } + for (uint32_t i = 0; i < MultiLineString::PartCount(mline); i++) { + auto &line = MultiLineString::Part(mline, i); + auto line_coords = yyjson_mut_arr(doc); + VerticesToGeoJSON(line, doc, line_coords); + yyjson_mut_arr_append(coords, line_coords); + } } // MultiPolygon @@ -190,33 +190,33 @@ struct ToGeoJSONFunctor { auto coords = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); - - for(uint32_t i = 0; i < MultiPolygon::PartCount(mpoly); i++) { - auto &poly = MultiPolygon::Part(mpoly, i); - auto poly_coords = yyjson_mut_arr(doc); - for (uint32_t j = 0; j < Polygon::PartCount(poly); j++) { - auto &ring = Polygon::Part(poly, j); - auto ring_coords = yyjson_mut_arr(doc); - VerticesToGeoJSON(ring, doc, ring_coords); - yyjson_mut_arr_append(poly_coords, ring_coords); - } - yyjson_mut_arr_append(coords, poly_coords); - } + for (uint32_t i = 0; i < MultiPolygon::PartCount(mpoly); i++) { + auto &poly = MultiPolygon::Part(mpoly, i); + auto poly_coords = yyjson_mut_arr(doc); + for (uint32_t j = 0; j < Polygon::PartCount(poly); j++) { + auto &ring = Polygon::Part(poly, j); + auto ring_coords = yyjson_mut_arr(doc); + VerticesToGeoJSON(ring, doc, ring_coords); + yyjson_mut_arr_append(poly_coords, ring_coords); + } + yyjson_mut_arr_append(coords, poly_coords); + } } // GeometryCollection - static void Case(Geometry::Tags::GeometryCollection, const Geometry &collection, yyjson_mut_doc *doc, yyjson_mut_val *obj) { + static void Case(Geometry::Tags::GeometryCollection, const Geometry &collection, yyjson_mut_doc *doc, + yyjson_mut_val *obj) { yyjson_mut_obj_add_str(doc, obj, "type", "GeometryCollection"); auto arr = yyjson_mut_arr(doc); yyjson_mut_obj_add_val(doc, obj, "geometries", arr); - for(uint32_t i = 0; i < GeometryCollection::PartCount(collection); i++) { - auto &geom = GeometryCollection::Part(collection, i); - auto geom_obj = yyjson_mut_obj(doc); - Geometry::Match(geom, doc, geom_obj); - yyjson_mut_arr_append(arr, geom_obj); - } - } + for (uint32_t i = 0; i < GeometryCollection::PartCount(collection); i++) { + auto &geom = GeometryCollection::Part(collection, i); + auto geom_obj = yyjson_mut_obj(doc); + Geometry::Match(geom, doc, geom_obj); + yyjson_mut_arr_append(arr, geom_obj); + } + } }; static void GeometryToGeoJSONFragmentFunction(DataChunk &args, ExpressionState &state, Vector &result) { @@ -286,8 +286,7 @@ static Geometry PointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, } } -static Geometry VerticesFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, - bool &has_z) { +static Geometry VerticesFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, bool &has_z) { auto len = yyjson_arr_size(coord_array); if (len == 0) { // Empty @@ -342,9 +341,9 @@ static Geometry VerticesFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &are z = yyjson_get_num(z_val); } if (has_any_z) { - LineString::SetVertex(vertices, idx, {x, y, z}); + LineString::SetVertex(vertices, idx, {x, y, z}); } else { - LineString::SetVertex(vertices, idx, {x, y }); + LineString::SetVertex(vertices, idx, {x, y}); } } return vertices; @@ -352,7 +351,7 @@ static Geometry VerticesFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &are } static Geometry LineStringFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, - bool &has_z) { + bool &has_z) { return VerticesFromGeoJSON(coord_array, arena, raw, has_z); } @@ -371,7 +370,7 @@ static Geometry PolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &aren throw InvalidInputException("GeoJSON input coordinates field is not an array of arrays: %s", raw.GetString()); } - Polygon::Part(polygon, idx) = VerticesFromGeoJSON(ring_val, arena, raw, has_z); + Polygon::Part(polygon, idx) = VerticesFromGeoJSON(ring_val, arena, raw, has_z); } return polygon; @@ -379,7 +378,7 @@ static Geometry PolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &aren } static Geometry MultiPointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, - bool &has_z) { + bool &has_z) { auto num_points = yyjson_arr_size(coord_array); if (num_points == 0) { // Empty @@ -398,14 +397,14 @@ static Geometry MultiPointFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &a throw InvalidInputException( "GeoJSON input coordinates field is not an array of arrays of length >= 2: %s", raw.GetString()); } - MultiPoint::Part(multi_point, idx) = PointFromGeoJSON(point_val, arena, raw, has_z); + MultiPoint::Part(multi_point, idx) = PointFromGeoJSON(point_val, arena, raw, has_z); } return multi_point; } } static Geometry MultiLineStringFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, - bool &has_z) { + bool &has_z) { auto num_linestrings = yyjson_arr_size(coord_array); if (num_linestrings == 0) { // Empty @@ -420,7 +419,7 @@ static Geometry MultiLineStringFromGeoJSON(yyjson_val *coord_array, ArenaAllocat throw InvalidInputException("GeoJSON input coordinates field is not an array of arrays: %s", raw.GetString()); } - MultiLineString::Part(multi_linestring, idx) = LineStringFromGeoJSON(linestring_val, arena, raw, has_z); + MultiLineString::Part(multi_linestring, idx) = LineStringFromGeoJSON(linestring_val, arena, raw, has_z); } return multi_linestring; @@ -428,7 +427,7 @@ static Geometry MultiLineStringFromGeoJSON(yyjson_val *coord_array, ArenaAllocat } static Geometry MultiPolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator &arena, const string_t &raw, - bool &has_z) { + bool &has_z) { auto num_polygons = yyjson_arr_size(coord_array); if (num_polygons == 0) { // Empty @@ -443,7 +442,7 @@ static Geometry MultiPolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator throw InvalidInputException("GeoJSON input coordinates field is not an array of arrays: %s", raw.GetString()); } - MultiPolygon::Part(multi_polygon, idx) = PolygonFromGeoJSON(polygon_val, arena, raw, has_z); + MultiPolygon::Part(multi_polygon, idx) = PolygonFromGeoJSON(polygon_val, arena, raw, has_z); } return multi_polygon; @@ -453,7 +452,7 @@ static Geometry MultiPolygonFromGeoJSON(yyjson_val *coord_array, ArenaAllocator static Geometry FromGeoJSON(yyjson_val *root, ArenaAllocator &arena, const string_t &raw, bool &has_z); static Geometry GeometryCollectionFromGeoJSON(yyjson_val *root, ArenaAllocator &arena, const string_t &raw, - bool &has_z) { + bool &has_z) { auto geometries_val = yyjson_obj_get(root, "geometries"); if (!geometries_val) { throw InvalidInputException("GeoJSON input does not have a geometries field: %s", raw.GetString()); @@ -471,7 +470,7 @@ static Geometry GeometryCollectionFromGeoJSON(yyjson_val *root, ArenaAllocator & size_t idx, max; yyjson_val *geometry_val; yyjson_arr_foreach(geometries_val, idx, max, geometry_val) { - GeometryCollection::Part(geometry_collection, idx) = FromGeoJSON(geometry_val, arena, raw, has_z); + GeometryCollection::Part(geometry_collection, idx) = FromGeoJSON(geometry_val, arena, raw, has_z); } return geometry_collection; @@ -547,7 +546,7 @@ static void GeoJSONFragmentToGeometryFunction(DataChunk &args, ExpressionState & // Ensure the geometries has consistent Z values geom.SetVertexType(lstate.arena, has_z, false); } - return Geometry::Serialize(geom, result); + return Geometry::Serialize(geom, result); } }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_collect.cpp b/spatial/src/spatial/core/functions/scalar/st_collect.cpp index fba20892..1b1e0deb 100644 --- a/spatial/src/spatial/core/functions/scalar/st_collect.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_collect.cpp @@ -27,9 +27,9 @@ static void CollectFunction(DataChunk &args, ExpressionState &state, Vector &res // First figure out if we have Z or M bool has_z = false; bool has_m = false; - bool all_points = true; - bool all_lines = true; - bool all_polygons = true; + bool all_points = true; + bool all_lines = true; + bool all_polygons = true; for (idx_t i = offset; i < offset + length; i++) { auto mapped_idx = format.sel->get_index(i); @@ -49,24 +49,24 @@ static void CollectFunction(DataChunk &args, ExpressionState &state, Vector &res auto geometry = Geometry::Deserialize(arena, geometry_blob); // Dont add empty geometries if (!Geometry::IsEmpty(geometry)) { - all_points = all_points && geometry_blob.GetType() == GeometryType::POINT; - all_lines = all_lines && geometry_blob.GetType() == GeometryType::LINESTRING; - all_polygons = all_polygons && geometry_blob.GetType() == GeometryType::POLYGON; + all_points = all_points && geometry_blob.GetType() == GeometryType::POINT; + all_lines = all_lines && geometry_blob.GetType() == GeometryType::LINESTRING; + all_polygons = all_polygons && geometry_blob.GetType() == GeometryType::POLYGON; - // Ensure all geometries have the same Z and M - geometry.SetVertexType(arena, has_z, has_m); - geometries.push_back(std::move(geometry)); + // Ensure all geometries have the same Z and M + geometry.SetVertexType(arena, has_z, has_m); + geometries.push_back(std::move(geometry)); } } } if (geometries.empty()) { - return Geometry::Serialize(GeometryCollection::CreateEmpty(has_z, has_m), result); + return Geometry::Serialize(GeometryCollection::CreateEmpty(has_z, has_m), result); } // TODO: Dont upcast the children, just append them. if (all_points) { - return Geometry::Serialize(MultiPoint::Create(arena, geometries, has_z, has_m), result); + return Geometry::Serialize(MultiPoint::Create(arena, geometries, has_z, has_m), result); } else if (all_lines) { return Geometry::Serialize(MultiLineString::Create(arena, geometries, has_z, has_m), result); } else if (all_polygons) { diff --git a/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp b/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp index d621537d..9fdee12d 100644 --- a/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_collectionextract.cpp @@ -23,89 +23,82 @@ static void CollectionExtractTypeFunction(DataChunk &args, ExpressionState &stat auto &input = args.data[0]; auto &dim = args.data[1]; - // Items vector - vector items; - - BinaryExecutor::Execute( - input, dim, result, count, [&](geometry_t input, int32_t requested_type) { - // Reset the items vector - items.clear(); - - // Deserialize the input geometry - auto props = input.GetProperties(); - auto geometry = Geometry::Deserialize(arena, input); - - // Switch on the requested type - switch (requested_type) { - case 1: { - if (geometry.GetType() == GeometryType::MULTIPOINT || geometry.GetType() == GeometryType::POINT) { - return input; - } else if (geometry.IsCollection()) { - // if it is a geometry collection, we need to collect all points - if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { - Geometry::ExtractPoints(geometry, [&](const Geometry &point) { - items.push_back(point); - }); - auto mpoint = MultiPoint::Create(arena, items, props.HasZ(), props.HasM()); - return Geometry::Serialize(mpoint, result); - } - // otherwise, we return an empty multipoint - auto empty = MultiPoint::CreateEmpty(props.HasZ(), props.HasM()); - return Geometry::Serialize(empty, result); - } else { - // otherwise if its not a collection, we return an empty point - auto empty = Point::CreateEmpty(props.HasZ(), props.HasM()); - return Geometry::Serialize(empty, result); - } - } - case 2: { - if (geometry.GetType() == GeometryType::MULTILINESTRING || - geometry.GetType() == GeometryType::LINESTRING) { - return input; - } else if (geometry.IsCollection()) { - // if it is a geometry collection, we need to collect all lines - if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { - Geometry::ExtractLines(geometry, [&](const Geometry &line) { - items.push_back(line); - }); - auto mline = MultiLineString::Create(arena, items, props.HasZ(), props.HasM()); - return Geometry::Serialize(mline, result); - } - // otherwise, we return an empty multilinestring - auto empty = MultiLineString::CreateEmpty(props.HasZ(), props.HasM()); - return Geometry::Serialize(empty, result); - } else { - // otherwise if its not a collection, we return an empty linestring - auto empty = LineString::CreateEmpty(props.HasZ(), props.HasM()); - return Geometry::Serialize(empty, result); - } - } - case 3: { - if (geometry.GetType() == GeometryType::MULTIPOLYGON || geometry.GetType() == GeometryType::POLYGON) { - return input; - } else if (geometry.IsCollection()) { - // if it is a geometry collection, we need to collect all polygons - if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { - Geometry::ExtractPolygons(geometry, [&](const Geometry &poly) { - items.push_back(poly); - }); - auto mpoly = MultiPolygon::Create(arena, items, props.HasZ(), props.HasM()); - return Geometry::Serialize(mpoly, result); - } - // otherwise, we return an empty multipolygon - auto empty = MultiPolygon::CreateEmpty(props.HasZ(), props.HasM()); - return Geometry::Serialize(empty, result); - } else { - // otherwise if its not a collection, we return an empty polygon - auto empty = Polygon::CreateEmpty(props.HasZ(), props.HasM()); - return Geometry::Serialize(empty, result); - } - } - default: - throw InvalidInputException("Invalid requested type parameter for collection extract, must be 1 " - "(POINT), 2 (LINESTRING) or 3 (POLYGON)"); - } - }); + // Items vector + vector items; + + BinaryExecutor::Execute(input, dim, result, count, [&](geometry_t input, int32_t requested_type) { + // Reset the items vector + items.clear(); + + // Deserialize the input geometry + auto props = input.GetProperties(); + auto geometry = Geometry::Deserialize(arena, input); + + // Switch on the requested type + switch (requested_type) { + case 1: { + if (geometry.GetType() == GeometryType::MULTIPOINT || geometry.GetType() == GeometryType::POINT) { + return input; + } else if (geometry.IsCollection()) { + // if it is a geometry collection, we need to collect all points + if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { + Geometry::ExtractPoints(geometry, [&](const Geometry &point) { items.push_back(point); }); + auto mpoint = MultiPoint::Create(arena, items, props.HasZ(), props.HasM()); + return Geometry::Serialize(mpoint, result); + } + // otherwise, we return an empty multipoint + auto empty = MultiPoint::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); + } else { + // otherwise if its not a collection, we return an empty point + auto empty = Point::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); + } + } + case 2: { + if (geometry.GetType() == GeometryType::MULTILINESTRING || geometry.GetType() == GeometryType::LINESTRING) { + return input; + } else if (geometry.IsCollection()) { + // if it is a geometry collection, we need to collect all lines + if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { + Geometry::ExtractLines(geometry, [&](const Geometry &line) { items.push_back(line); }); + auto mline = MultiLineString::Create(arena, items, props.HasZ(), props.HasM()); + return Geometry::Serialize(mline, result); + } + // otherwise, we return an empty multilinestring + auto empty = MultiLineString::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); + } else { + // otherwise if its not a collection, we return an empty linestring + auto empty = LineString::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); + } + } + case 3: { + if (geometry.GetType() == GeometryType::MULTIPOLYGON || geometry.GetType() == GeometryType::POLYGON) { + return input; + } else if (geometry.IsCollection()) { + // if it is a geometry collection, we need to collect all polygons + if (geometry.GetType() == GeometryType::GEOMETRYCOLLECTION && !GeometryCollection::IsEmpty(geometry)) { + Geometry::ExtractPolygons(geometry, [&](const Geometry &poly) { items.push_back(poly); }); + auto mpoly = MultiPolygon::Create(arena, items, props.HasZ(), props.HasM()); + return Geometry::Serialize(mpoly, result); + } + // otherwise, we return an empty multipolygon + auto empty = MultiPolygon::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); + } else { + // otherwise if its not a collection, we return an empty polygon + auto empty = Polygon::CreateEmpty(props.HasZ(), props.HasM()); + return Geometry::Serialize(empty, result); + } + } + default: + throw InvalidInputException("Invalid requested type parameter for collection extract, must be 1 " + "(POINT), 2 (LINESTRING) or 3 (POLYGON)"); + } + }); } // Note: We're being smart here and reusing the memory from the input geometry @@ -116,14 +109,14 @@ static void CollectionExtractAutoFunction(DataChunk &args, ExpressionState &stat auto count = args.size(); auto &input = args.data[0]; - vector items; + vector items; UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { if (input.GetType() == GeometryType::GEOMETRYCOLLECTION) { - // Reset the items vector - items.clear(); + // Reset the items vector + items.clear(); - auto props = input.GetProperties(); + auto props = input.GetProperties(); auto collection = Geometry::Deserialize(arena, input); if (GeometryCollection::IsEmpty(collection)) { return input; @@ -135,27 +128,21 @@ static void CollectionExtractAutoFunction(DataChunk &args, ExpressionState &stat switch (dim) { // Point case case 0: { - Geometry::ExtractPoints(collection, [&](const Geometry &point) { - items.push_back(point); - }); + Geometry::ExtractPoints(collection, [&](const Geometry &point) { items.push_back(point); }); auto mpoint = MultiPoint::Create(arena, items, props.HasZ(), props.HasM()); - return Geometry::Serialize(mpoint, result); + return Geometry::Serialize(mpoint, result); } // LineString case case 1: { - Geometry::ExtractLines(collection, [&](const Geometry &line) { - items.push_back(line); - }); + Geometry::ExtractLines(collection, [&](const Geometry &line) { items.push_back(line); }); auto mline = MultiLineString::Create(arena, items, props.HasZ(), props.HasM()); - return Geometry::Serialize(mline, result); + return Geometry::Serialize(mline, result); } // Polygon case case 2: { - Geometry::ExtractPolygons(collection, [&](const Geometry &poly) { - items.push_back(poly); - }); + Geometry::ExtractPolygons(collection, [&](const Geometry &poly) { items.push_back(poly); }); auto mpoly = MultiPolygon::Create(arena, items, props.HasZ(), props.HasM()); - return Geometry::Serialize(mpoly, result); + return Geometry::Serialize(mpoly, result); } default: { throw InternalException("Invalid dimension in collection extract"); diff --git a/spatial/src/spatial/core/functions/scalar/st_dump.cpp b/spatial/src/spatial/core/functions/scalar/st_dump.cpp index db904029..bbe45614 100644 --- a/spatial/src/spatial/core/functions/scalar/st_dump.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_dump.cpp @@ -46,12 +46,12 @@ static void DumpFunction(DataChunk &args, ExpressionState &state, Vector &result auto current_path = std::get<1>(current); stack.pop_back(); - if(current_geom.IsCollection()) { - for(int32_t i = 0; i < CollectionGeometry::PartCount(current_geom); i++) { - auto path = current_path; - path.push_back(i + 1); // path is 1-indexed - stack.emplace_back(CollectionGeometry::Part(current_geom, i), path); - } + if (current_geom.IsCollection()) { + for (int32_t i = 0; i < CollectionGeometry::PartCount(current_geom); i++) { + auto path = current_path; + path.push_back(i + 1); // path is 1-indexed + stack.emplace_back(CollectionGeometry::Part(current_geom, i), path); + } } else { items.push_back(current); } diff --git a/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp b/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp index dc2d8165..ce218afb 100644 --- a/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp @@ -79,8 +79,8 @@ static void GeometryEndPointFunction(DataChunk &args, ExpressionState &state, Ve return geometry_t {}; } - auto point = LineString::GetPointAsReference(line, point_count - 1); - return Geometry::Serialize(point, result); + auto point = LineString::GetPointAsReference(line, point_count - 1); + return Geometry::Serialize(point, result); }); } //------------------------------------------------------------------------------ diff --git a/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp b/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp index 9bf5d933..31445045 100644 --- a/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp @@ -104,11 +104,11 @@ static void GeometryExteriorRingFunction(DataChunk &args, ExpressionState &state } auto polygon = Geometry::Deserialize(arena, input); if (Polygon::IsEmpty(polygon)) { - auto empty = LineString::CreateEmpty(polygon.GetProperties().HasZ(), polygon.GetProperties().HasM()); - return Geometry::Serialize(empty, result); + auto empty = LineString::CreateEmpty(polygon.GetProperties().HasZ(), polygon.GetProperties().HasM()); + return Geometry::Serialize(empty, result); } - auto &shell = Polygon::ExteriorRing(polygon); - return Geometry::Serialize(shell, result); + auto &shell = Polygon::ExteriorRing(polygon); + return Geometry::Serialize(shell, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp b/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp index ad91fbbe..af6839a3 100644 --- a/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp @@ -163,25 +163,25 @@ static void GeometryFlipCoordinatesFunction(DataChunk &args, ExpressionState &st struct op { static void Case(Geometry::Tags::SinglePartGeometry, Geometry &geom, ArenaAllocator &arena) { - SinglePartGeometry::MakeMutable(geom, arena); + SinglePartGeometry::MakeMutable(geom, arena); for (idx_t i = 0; i < SinglePartGeometry::VertexCount(geom); i++) { - auto vertex = SinglePartGeometry::GetVertex(geom, i); + auto vertex = SinglePartGeometry::GetVertex(geom, i); std::swap(vertex.x, vertex.y); - SinglePartGeometry::SetVertex(geom, i, vertex); + SinglePartGeometry::SetVertex(geom, i, vertex); } } static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, ArenaAllocator &arena) { - for (uint32_t i = 0; i < MultiPartGeometry::PartCount(geom); i++) { - auto &part = MultiPartGeometry::Part(geom, i); - Geometry::Match(part, arena); - } + for (uint32_t i = 0; i < MultiPartGeometry::PartCount(geom); i++) { + auto &part = MultiPartGeometry::Part(geom, i); + Geometry::Match(part, arena); + } } }; UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { auto geom = Geometry::Deserialize(lstate.arena, input); Geometry::Match(geom, lstate.arena); - return Geometry::Serialize(geom, result); + return Geometry::Serialize(geom, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_force.cpp b/spatial/src/spatial/core/functions/scalar/st_force.cpp index ae1720dd..07e73a50 100644 --- a/spatial/src/spatial/core/functions/scalar/st_force.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_force.cpp @@ -25,9 +25,9 @@ static void GeometryFunction(DataChunk &args, ExpressionState &state, Vector &re auto &m_values = args.data[2]; TernaryExecutor::Execute( input, z_values, m_values, result, count, [&](const geometry_t &blob, double default_z, double default_m) { - auto geom = Geometry::Deserialize(arena, blob); - geom.SetVertexType(arena, HAS_Z, HAS_M, default_z, default_m); - return Geometry::Serialize(geom, result); + auto geom = Geometry::Deserialize(arena, blob); + geom.SetVertexType(arena, HAS_Z, HAS_M, default_z, default_m); + return Geometry::Serialize(geom, result); }); } else if (HAS_Z || HAS_M) { @@ -37,15 +37,15 @@ static void GeometryFunction(DataChunk &args, ExpressionState &state, Vector &re auto def_z = HAS_Z ? default_value : 0; auto def_m = HAS_M ? default_value : 0; - auto geom = Geometry::Deserialize(arena, blob); - geom.SetVertexType(arena, HAS_Z, HAS_M, def_z, def_m); - return Geometry::Serialize(geom, result); + auto geom = Geometry::Deserialize(arena, blob); + geom.SetVertexType(arena, HAS_Z, HAS_M, def_z, def_m); + return Geometry::Serialize(geom, result); }); } else { UnaryExecutor::Execute(input, result, count, [&](const geometry_t &blob) { - auto geom = Geometry::Deserialize(arena, blob); - geom.SetVertexType(arena, HAS_Z, HAS_M); - return Geometry::Serialize(geom, result); + auto geom = Geometry::Deserialize(arena, blob); + geom.SetVertexType(arena, HAS_Z, HAS_M); + return Geometry::Serialize(geom, result); }); } } diff --git a/spatial/src/spatial/core/functions/scalar/st_geomfromhexwkb.cpp b/spatial/src/spatial/core/functions/scalar/st_geomfromhexwkb.cpp index 2e2b9f14..3192d5c7 100644 --- a/spatial/src/spatial/core/functions/scalar/st_geomfromhexwkb.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_geomfromhexwkb.cpp @@ -48,8 +48,8 @@ void GeometryFromHEXWKB(DataChunk &args, ExpressionState &state, Vector &result) blob_ptr[blob_idx++] = (byte_a << 4) + byte_b; } - auto geom = reader.Deserialize(blob_ptr, blob_size); - return Geometry::Serialize(geom, result); + auto geom = reader.Deserialize(blob_ptr, blob_size); + return Geometry::Serialize(geom, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_geomfromtext.cpp b/spatial/src/spatial/core/functions/scalar/st_geomfromtext.cpp index d8b8512a..50962670 100644 --- a/spatial/src/spatial/core/functions/scalar/st_geomfromtext.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_geomfromtext.cpp @@ -45,7 +45,7 @@ static void GeometryFromWKTFunction(DataChunk &args, ExpressionState &state, Vec [&](string_t &wkt, ValidityMask &mask, idx_t idx) { try { auto geom = reader.Parse(wkt); - return Geometry::Serialize(geom, result); + return Geometry::Serialize(geom, result); } catch (InvalidInputException &error) { if (!info.ignore_invalid) { throw; diff --git a/spatial/src/spatial/core/functions/scalar/st_geomfromwkb.cpp b/spatial/src/spatial/core/functions/scalar/st_geomfromwkb.cpp index 6d72beaa..8605fa66 100644 --- a/spatial/src/spatial/core/functions/scalar/st_geomfromwkb.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_geomfromwkb.cpp @@ -273,11 +273,10 @@ static void GeometryFromWKBFunction(DataChunk &args, ExpressionState &state, Vec auto count = args.size(); WKBReader reader(arena); - UnaryExecutor::Execute( - input, result, count, [&](string_t input) { - auto geom = reader.Deserialize(input); - return Geometry::Serialize(geom, result); - }); + UnaryExecutor::Execute(input, result, count, [&](string_t input) { + auto geom = reader.Deserialize(input); + return Geometry::Serialize(geom, result); + }); } //------------------------------------------------------------------------------ diff --git a/spatial/src/spatial/core/functions/scalar/st_is_closed.cpp b/spatial/src/spatial/core/functions/scalar/st_is_closed.cpp index 30caca5a..88d24989 100644 --- a/spatial/src/spatial/core/functions/scalar/st_is_closed.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_is_closed.cpp @@ -14,23 +14,23 @@ namespace core { static void IsClosedFunction(DataChunk &args, ExpressionState &state, Vector &result) { auto &lstate = GeometryFunctionLocalState::ResetAndGet(state); - auto &arena = lstate.arena; + auto &arena = lstate.arena; - // TODO: We should support more than just LINESTRING and MULTILINESTRING (like PostGIS does) + // TODO: We should support more than just LINESTRING and MULTILINESTRING (like PostGIS does) UnaryExecutor::Execute(args.data[0], result, args.size(), [&](geometry_t input) { - struct op { - static bool Case(Geometry::Tags::LineString, const Geometry &geom) { - return LineString::IsClosed(geom); - } - static bool Case(Geometry::Tags::MultiLineString, const Geometry &geom) { - return MultiLineString::IsClosed(geom); - } - static bool Case(Geometry::Tags::AnyGeometry, const Geometry &) { - throw InvalidInputException("ST_IsClosed only accepts LINESTRING and MULTILINESTRING geometries"); - } - }; - auto geom = Geometry::Deserialize(arena, input); - return Geometry::Match(geom); + struct op { + static bool Case(Geometry::Tags::LineString, const Geometry &geom) { + return LineString::IsClosed(geom); + } + static bool Case(Geometry::Tags::MultiLineString, const Geometry &geom) { + return MultiLineString::IsClosed(geom); + } + static bool Case(Geometry::Tags::AnyGeometry, const Geometry &) { + throw InvalidInputException("ST_IsClosed only accepts LINESTRING and MULTILINESTRING geometries"); + } + }; + auto geom = Geometry::Deserialize(arena, input); + return Geometry::Match(geom); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_isempty.cpp b/spatial/src/spatial/core/functions/scalar/st_isempty.cpp index 11448594..011e3d0b 100644 --- a/spatial/src/spatial/core/functions/scalar/st_isempty.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_isempty.cpp @@ -52,11 +52,10 @@ static void GeometryIsEmptyFunction(DataChunk &args, ExpressionState &state, Vec auto &input = args.data[0]; auto count = args.size(); - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { - auto geom = Geometry::Deserialize(lstate.arena, input); - return Geometry::IsEmpty(geom); - }); + UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(lstate.arena, input); + return Geometry::IsEmpty(geom); + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/core/functions/scalar/st_length.cpp b/spatial/src/spatial/core/functions/scalar/st_length.cpp index 46481cbe..52d094f7 100644 --- a/spatial/src/spatial/core/functions/scalar/st_length.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_length.cpp @@ -54,15 +54,12 @@ static void GeometryLengthFunction(DataChunk &args, ExpressionState &state, Vect auto &input = args.data[0]; auto count = args.size(); - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { - auto geom = Geometry::Deserialize(arena, input); - double length = 0.0; - Geometry::ExtractLines(geom, [&](const Geometry &line) { - length += LineString::Length(line); - }); - return length; - }); + UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + double length = 0.0; + Geometry::ExtractLines(geom, [&](const Geometry &line) { length += LineString::Length(line); }); + return length; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp b/spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp index 446514f0..2b3acf1f 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makeenvelope.cpp @@ -27,7 +27,7 @@ static void MakeEnvelopeFunction(DataChunk &args, ExpressionState &state, Vector 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) { auto box = Polygon::CreateFromBox(lstate.arena, x_min.val, y_min.val, x_max.val, y_max.val); - return Geometry::Serialize(box, result); + return Geometry::Serialize(box, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_makeline.cpp b/spatial/src/spatial/core/functions/scalar/st_makeline.cpp index 1615a4bf..3716d27f 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makeline.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makeline.cpp @@ -48,17 +48,17 @@ static void MakeLineListFunction(DataChunk &args, ExpressionState &state, Vector if (Point::IsEmpty(point)) { continue; } - LineString::Vertex(line, vertex_idx++) = Point::GetVertex(point); + LineString::Vertex(line, vertex_idx++) = Point::GetVertex(point); } // Shrink the vertex array to the actual size - LineString::Resize(line, arena, vertex_idx); + LineString::Resize(line, arena, vertex_idx); if (line.Count() == 1) { throw InvalidInputException("ST_MakeLine requires zero or two or more POINT geometries"); } - return Geometry::Serialize(line, result); + return Geometry::Serialize(line, result); }); } @@ -70,8 +70,7 @@ static void MakeLineBinaryFunction(DataChunk &args, ExpressionState &state, Vect BinaryExecutor::Execute( args.data[0], args.data[1], result, count, [&](geometry_t &geom_blob_left, geometry_t &geom_blob_right) { - - if (geom_blob_left.GetType() != GeometryType::POINT || geom_blob_right.GetType() != GeometryType::POINT) { + if (geom_blob_left.GetType() != GeometryType::POINT || geom_blob_right.GetType() != GeometryType::POINT) { throw InvalidInputException("ST_MakeLine only accepts POINT geometries"); } @@ -80,8 +79,8 @@ static void MakeLineBinaryFunction(DataChunk &args, ExpressionState &state, Vect if (Point::IsEmpty(geometry_left) && Point::IsEmpty(geometry_right)) { // Empty linestring - auto empty = LineString::CreateEmpty(false, false); - return Geometry::Serialize(empty, result); + auto empty = LineString::CreateEmpty(false, false); + return Geometry::Serialize(empty, result); } if (Point::IsEmpty(geometry_left) || Point::IsEmpty(geometry_right)) { @@ -97,9 +96,9 @@ static void MakeLineBinaryFunction(DataChunk &args, ExpressionState &state, Vect geometry_right.SetVertexType(arena, has_z, has_m); auto line_geom = LineString::CreateEmpty(has_z, has_m); - LineString::Append(line_geom, arena, geometry_left); - LineString::Append(line_geom, arena, geometry_right); - return Geometry::Serialize(line_geom, result); + LineString::Append(line_geom, arena, geometry_left); + LineString::Append(line_geom, arena, geometry_right); + return Geometry::Serialize(line_geom, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp b/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp index 2f9bef08..2300d60b 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp @@ -66,7 +66,7 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state throw InvalidInputException( StringUtil::Format("ST_MakePolygon hole #%lu is not a LINESTRING geometry", hole_idx + 1)); } - auto hole = Geometry::Deserialize(arena, geometry_blob); + auto hole = Geometry::Deserialize(arena, geometry_blob); if (LineString::VertexCount(hole) < 4) { throw InvalidInputException( StringUtil::Format("ST_MakePolygon hole #%lu requires at least 4 vertices", hole_idx + 1)); @@ -79,12 +79,12 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state rings.push_back(hole); } - // TODO: Add constructor that takes a vector of rings + // TODO: Add constructor that takes a vector of rings auto polygon = Polygon::Create(arena, rings.size(), false, false); for (idx_t ring_idx = 0; ring_idx < rings.size(); ring_idx++) { - Polygon::Part(polygon, ring_idx) = std::move(rings[ring_idx]); + Polygon::Part(polygon, ring_idx) = std::move(rings[ring_idx]); } - return Geometry::Serialize(polygon, result); + return Geometry::Serialize(polygon, result); }); } @@ -113,7 +113,7 @@ static void MakePolygonFromShellFunction(DataChunk &args, ExpressionState &state auto polygon = Polygon::Create(arena, 1, props.HasZ(), props.HasM()); Polygon::Part(polygon, 0) = std::move(line); - return Geometry::Serialize(polygon, result); + return Geometry::Serialize(polygon, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp b/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp index 5c1e3c54..c5cfe420 100644 --- a/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_ngeometries.cpp @@ -29,11 +29,11 @@ static void GeometryNGeometriesFunction(DataChunk &args, ExpressionState &state, return Polygon::IsEmpty(geom) ? 0 : 1; } static int32_t Case(Geometry::Tags::SinglePartGeometry, const Geometry &geom) { - return SinglePartGeometry::IsEmpty(geom)? 0 : 1; + return SinglePartGeometry::IsEmpty(geom) ? 0 : 1; } }; - auto geom = Geometry::Deserialize(ctx.arena, input); - return Geometry::Match(geom); + auto geom = Geometry::Deserialize(ctx.arena, input); + return Geometry::Match(geom); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_ninteriorrings.cpp b/spatial/src/spatial/core/functions/scalar/st_ninteriorrings.cpp index 52be0ea1..b3a312dc 100644 --- a/spatial/src/spatial/core/functions/scalar/st_ninteriorrings.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_ninteriorrings.cpp @@ -40,8 +40,8 @@ static void GeometryInteriorRingsFunction(DataChunk &args, ExpressionState &stat validity.SetInvalid(idx); return 0; } - auto polygon = Geometry::Deserialize(arena, input); - auto rings = Polygon::PartCount(polygon); + auto polygon = Geometry::Deserialize(arena, input); + auto rings = Polygon::PartCount(polygon); return rings == 0 ? 0 : static_cast(rings - 1); // -1 for the exterior ring }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_npoints.cpp b/spatial/src/spatial/core/functions/scalar/st_npoints.cpp index 22c748b2..0c4d7572 100644 --- a/spatial/src/spatial/core/functions/scalar/st_npoints.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_npoints.cpp @@ -81,19 +81,18 @@ static void GeometryNumPointsFunction(DataChunk &args, ExpressionState &state, V } static uint32_t Case(Geometry::Tags::MultiPartGeometry, const Geometry &geom) { uint32_t count = 0; - for(uint32_t i = 0; i < MultiPartGeometry::PartCount(geom); i++) { - auto part = MultiPartGeometry::Part(geom, i); + for (uint32_t i = 0; i < MultiPartGeometry::PartCount(geom); i++) { + auto part = MultiPartGeometry::Part(geom, i); count += Geometry::Match(part); } return count; } }; - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { - auto geom = Geometry::Deserialize(arena, input); - return Geometry::Match(geom); - }); + UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + return Geometry::Match(geom); + }); } //------------------------------------------------------------------------------ diff --git a/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp b/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp index 4630e883..cdd30c4f 100644 --- a/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp @@ -78,17 +78,16 @@ static void GeometryPerimeterFunction(DataChunk &args, ExpressionState &state, V auto &input = args.data[0]; auto count = args.size(); - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { - auto geom = Geometry::Deserialize(arena, input); - double perimeter = 0.0; - Geometry::ExtractPolygons(geom, [&](const Geometry &poly) { - for(auto &p : Polygon::Parts(poly)) { - perimeter += LineString::Length(p); - } - }); - return perimeter; - }); + UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + double perimeter = 0.0; + Geometry::ExtractPolygons(geom, [&](const Geometry &poly) { + for (auto &p : Polygon::Parts(poly)) { + perimeter += LineString::Length(p); + } + }); + return perimeter; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/core/functions/scalar/st_point.cpp b/spatial/src/spatial/core/functions/scalar/st_point.cpp index 3cfa568d..d6c79e74 100644 --- a/spatial/src/spatial/core/functions/scalar/st_point.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_point.cpp @@ -107,7 +107,7 @@ static void PointFunction(DataChunk &args, ExpressionState &state, Vector &resul auto count = args.size(); BinaryExecutor::Execute(x, y, result, count, [&](double x, double y) { - return Geometry::Serialize(Point::CreateFromVertex(arena, VertexXY {x, y}), result); + return Geometry::Serialize(Point::CreateFromVertex(arena, VertexXY {x, y}), result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_pointn.cpp b/spatial/src/spatial/core/functions/scalar/st_pointn.cpp index 0c79e898..e4d33d57 100644 --- a/spatial/src/spatial/core/functions/scalar/st_pointn.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_pointn.cpp @@ -90,7 +90,7 @@ static void GeometryPointNFunction(DataChunk &args, ExpressionState &state, Vect auto actual_index = index < 0 ? point_count + index : index - 1; auto point = LineString::GetPointAsReference(line, actual_index); - return Geometry::Serialize(point, result); + return Geometry::Serialize(point, result); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_quadkey.cpp b/spatial/src/spatial/core/functions/scalar/st_quadkey.cpp index 0cbe6a99..c8c25032 100644 --- a/spatial/src/spatial/core/functions/scalar/st_quadkey.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_quadkey.cpp @@ -69,14 +69,14 @@ static void GeometryQuadKeyFunction(DataChunk &args, ExpressionState &state, Vec BinaryExecutor::Execute( geom, level, result, count, [&](geometry_t input, int32_t level) { - if (level < 1 || level > 23) { - throw InvalidInputException("ST_QuadKey: Level must be between 1 and 23"); - } + if (level < 1 || level > 23) { + throw InvalidInputException("ST_QuadKey: Level must be between 1 and 23"); + } if (input.GetType() != GeometryType::POINT) { throw InvalidInputException("ST_QuadKey: Only POINT geometries are supported"); } auto point = Geometry::Deserialize(arena, input); - if (Point::IsEmpty(point)) { + if (Point::IsEmpty(point)) { throw InvalidInputException("ST_QuadKey: Empty geometries are not supported"); } auto vertex = Point::GetVertex(point); diff --git a/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp b/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp index 9577e418..bbe1a545 100644 --- a/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp @@ -64,21 +64,21 @@ static void GeometryStartPointFunction(DataChunk &args, ExpressionState &state, auto &geom_vec = args.data[0]; auto count = args.size(); - UnaryExecutor::ExecuteWithNulls( - geom_vec, result, count, [&](geometry_t input, ValidityMask &mask, idx_t row_idx) { - if (input.GetType() != GeometryType::LINESTRING) { - mask.SetInvalid(row_idx); - return geometry_t {}; - } - - auto line = Geometry::Deserialize(lstate.arena, input); - if (LineString::IsEmpty(line)) { - mask.SetInvalid(row_idx); - return geometry_t {}; - } - auto point = LineString::GetPointAsReference(line, 0); - return Geometry::Serialize(point, result); - }); + UnaryExecutor::ExecuteWithNulls(geom_vec, result, count, + [&](geometry_t input, ValidityMask &mask, idx_t row_idx) { + if (input.GetType() != GeometryType::LINESTRING) { + mask.SetInvalid(row_idx); + return geometry_t {}; + } + + auto line = Geometry::Deserialize(lstate.arena, input); + if (LineString::IsEmpty(line)) { + mask.SetInvalid(row_idx); + return geometry_t {}; + } + auto point = LineString::GetPointAsReference(line, 0); + return Geometry::Serialize(point, result); + }); } //------------------------------------------------------------------------------ // Documentation diff --git a/spatial/src/spatial/core/geometry/geometry.cpp b/spatial/src/spatial/core/geometry/geometry.cpp index ec8d82ba..9f06451c 100644 --- a/spatial/src/spatial/core/geometry/geometry.cpp +++ b/spatial/src/spatial/core/geometry/geometry.cpp @@ -9,8 +9,8 @@ namespace core { //------------------------------------------------------------------------------ // Single Part Geometry //------------------------------------------------------------------------------ -void SinglePartGeometry::Resize(Geometry& geom, ArenaAllocator &alloc, uint32_t new_count) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); +void SinglePartGeometry::Resize(Geometry &geom, ArenaAllocator &alloc, uint32_t new_count) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); auto vertex_size = geom.properties.VertexSize(); if (new_count == geom.data_count) { @@ -43,7 +43,7 @@ void SinglePartGeometry::Append(Geometry &geom, ArenaAllocator &alloc, const Geo } void SinglePartGeometry::Append(Geometry &geom, ArenaAllocator &alloc, const Geometry *others, uint32_t others_count) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); + D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); if (geom.IsReadOnly()) { MakeMutable(geom, alloc); } @@ -52,9 +52,9 @@ void SinglePartGeometry::Append(Geometry &geom, ArenaAllocator &alloc, const Geo auto new_count = old_count; for (uint32_t i = 0; i < others_count; i++) { new_count += others[i].Count(); - // The other geometries has to be single part - D_ASSERT(GeometryTypes::IsSinglePart(others[i].type)); - // And have the same z and m properties + // The other geometries has to be single part + D_ASSERT(GeometryTypes::IsSinglePart(others[i].type)); + // And have the same z and m properties D_ASSERT(geom.properties.HasZ() == others[i].properties.HasZ()); D_ASSERT(geom.properties.HasM() == others[i].properties.HasM()); } @@ -67,7 +67,7 @@ void SinglePartGeometry::Append(Geometry &geom, ArenaAllocator &alloc, const Geo memcpy(geom.data_ptr + old_count * vertex_size, other.data_ptr, vertex_size * other.data_count); old_count += other.data_count; } - geom.data_count = new_count; + geom.data_count = new_count; } void SinglePartGeometry::SetVertexType(Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, @@ -83,14 +83,14 @@ void SinglePartGeometry::SetVertexType(Geometry &geom, ArenaAllocator &alloc, bo auto used_to_have_m = geom.properties.HasM(); auto old_vertex_size = geom.properties.VertexSize(); - geom.properties.SetZ(has_z); - geom.properties.SetM(has_m); + geom.properties.SetZ(has_z); + geom.properties.SetM(has_m); auto new_vertex_size = geom.properties.VertexSize(); // Case 1: The new vertex size is larger than the old vertex size if (new_vertex_size > old_vertex_size) { - geom.data_ptr = - alloc.ReallocateAligned(geom.data_ptr, geom.data_count * old_vertex_size, geom.data_count * new_vertex_size); + geom.data_ptr = alloc.ReallocateAligned(geom.data_ptr, geom.data_count * old_vertex_size, + geom.data_count * new_vertex_size); // There are 5 cases here: if (used_to_have_m && has_m && !used_to_have_z && has_z) { @@ -167,7 +167,7 @@ void SinglePartGeometry::SetVertexType(Geometry &geom, ArenaAllocator &alloc, bo memcpy(new_data + new_offset, geom.data_ptr + old_offset, new_vertex_size); } } - geom.data_ptr = new_data; + geom.data_ptr = new_data; } } @@ -177,16 +177,16 @@ void SinglePartGeometry::MakeMutable(Geometry &geom, ArenaAllocator &alloc) { } if (geom.data_count == 0) { - geom.data_ptr = nullptr; + geom.data_ptr = nullptr; geom.is_readonly = false; return; } - auto data_size = ByteSize(geom); + auto data_size = ByteSize(geom); auto new_data = alloc.AllocateAligned(data_size); memcpy(new_data, geom.data_ptr, data_size); - geom.data_ptr = new_data; - geom.is_readonly = false; + geom.data_ptr = new_data; + geom.is_readonly = false; } bool SinglePartGeometry::IsClosed(const Geometry &geom) { @@ -197,25 +197,25 @@ bool SinglePartGeometry::IsClosed(const Geometry &geom) { return true; default: VertexXY first = GetVertex(geom, 0); - VertexXY last = GetVertex(geom, geom.Count() - 1); + VertexXY last = GetVertex(geom, geom.Count() - 1); // TODO: Approximate comparison? return first.x == last.x && first.y == last.y; } } -double SinglePartGeometry::Length(const Geometry& geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); +double SinglePartGeometry::Length(const Geometry &geom) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); double length = 0; for (uint32_t i = 1; i < geom.data_count; i++) { - auto p1 = GetVertex(geom, i - 1); - auto p2 = GetVertex(geom, i); + auto p1 = GetVertex(geom, i - 1); + auto p2 = GetVertex(geom, i); length += sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)); } return length; } -string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32_t count) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); +string SinglePartGeometry::ToString(const Geometry &geom, uint32_t start, uint32_t count) { + D_ASSERT(GeometryTypes::IsSinglePart(geom.type)); auto has_z = geom.properties.HasZ(); auto has_m = geom.properties.HasM(); @@ -236,7 +236,7 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 } else if (has_z) { string result = StringUtil::Format("%s XYZ ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { - auto vertex = GetVertex(geom, i); + auto vertex = GetVertex(geom, i); result += "(" + MathUtil::format_coord(vertex.x, vertex.y, vertex.z) + ")"; if (i < count - 1) { result += ", "; @@ -247,7 +247,7 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 } else if (has_m) { string result = StringUtil::Format("%s XYM ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { - auto vertex = GetVertex(geom, i); + auto vertex = GetVertex(geom, i); result += "(" + MathUtil::format_coord(vertex.x, vertex.y, vertex.m) + ")"; if (i < count - 1) { result += ", "; @@ -258,7 +258,7 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 } else { string result = StringUtil::Format("%s XY ([%d-%d]/%d) [", type_name, start, start + count, geom.data_count); for (uint32_t i = start; i < count; i++) { - auto vertex = GetVertex(geom, i); + auto vertex = GetVertex(geom, i); result += "(" + MathUtil::format_coord(vertex.x, vertex.y) + ")"; if (i < count - 1) { result += ", "; @@ -269,24 +269,25 @@ string SinglePartGeometry::ToString(const Geometry& geom, uint32_t start, uint32 } } - //------------------------------------------------------------------------------ // Geometry //------------------------------------------------------------------------------ void Geometry::SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { - struct op { - static void Case(Geometry::Tags::SinglePartGeometry, Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { - SinglePartGeometry::SetVertexType(geom, alloc, has_z, has_m, default_z, default_m); - } - static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, ArenaAllocator &alloc, bool has_z, bool has_m, double default_z, double default_m) { - geom.properties.SetZ(has_z); - geom.properties.SetM(has_m); - for(auto &p : MultiPartGeometry::Parts(geom)) { - p.SetVertexType(alloc, has_z, has_m, default_z, default_m); - } - } - }; - Geometry::Match(*this, alloc, has_z, has_m, default_z, default_m); + struct op { + static void Case(Geometry::Tags::SinglePartGeometry, Geometry &geom, ArenaAllocator &alloc, bool has_z, + bool has_m, double default_z, double default_m) { + SinglePartGeometry::SetVertexType(geom, alloc, has_z, has_m, default_z, default_m); + } + static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, ArenaAllocator &alloc, bool has_z, + bool has_m, double default_z, double default_m) { + geom.properties.SetZ(has_z); + geom.properties.SetM(has_m); + for (auto &p : MultiPartGeometry::Parts(geom)) { + p.SetVertexType(alloc, has_z, has_m, default_z, default_m); + } + } + }; + Geometry::Match(*this, alloc, has_z, has_m, default_z, default_m); } //------------------------------------------------------------------------------ @@ -295,16 +296,16 @@ void Geometry::SetVertexType(ArenaAllocator &alloc, bool has_z, bool has_m, doub /* void MultiPartGeometry::Resize(Geometry& geom, ArenaAllocator &alloc, uint32_t new_count) { D_ASSERT(GeometryTypes::IsMultiPart(geom.type)); - if (new_count == geom.data_count) { - return; - } - if (geom.data_ptr == nullptr) { - geom.data_ptr = alloc.AllocateAligned(sizeof(Geometry) * new_count); + if (new_count == geom.data_count) { + return; + } + if (geom.data_ptr == nullptr) { + geom.data_ptr = alloc.AllocateAligned(sizeof(Geometry) * new_count); // Need to create a new Geometry for each entry for (uint32_t i = 0; i < new_count; i++) { new (geom.data_ptr + i * sizeof(Geometry)) Geometry(); } - } + } else if(geom.IsReadOnly()) { auto new_data = alloc.AllocateAligned(sizeof(Geometry) * new_count); for(uint32_t i = 0; i < geom.data_count; i++) { @@ -316,13 +317,13 @@ void MultiPartGeometry::Resize(Geometry& geom, ArenaAllocator &alloc, uint32_t n geom.data_ptr = new_data; } else { - geom.data_ptr = alloc.ReallocateAligned( - geom.data_ptr, geom.data_count * sizeof(Geometry), new_count * sizeof(Geometry)); + geom.data_ptr = alloc.ReallocateAligned( + geom.data_ptr, geom.data_count * sizeof(Geometry), new_count * sizeof(Geometry)); // If we added new entries, we need to create a new Geometry for each entry for (uint32_t i = geom.data_count; i < new_count; i++) { new (geom.data_ptr + i * sizeof(Geometry)) Geometry(); } - } + } geom.data_count = new_count; } */ diff --git a/spatial/src/spatial/core/geometry/geometry_serialization.cpp b/spatial/src/spatial/core/geometry/geometry_serialization.cpp index 73652a6b..44933fe4 100644 --- a/spatial/src/spatial/core/geometry/geometry_serialization.cpp +++ b/spatial/src/spatial/core/geometry/geometry_serialization.cpp @@ -53,10 +53,10 @@ struct GetRequiredSizeOp { // - sizeof(vertex) * count for each ring // (+ 4 bytes for padding if num_rings is odd) uint32_t size = 4 + 4; - for(uint32_t i = 0; i < Polygon::PartCount(polygon); i++) { - size += 4; - size += Polygon::Part(polygon, i).Count() * sizeof(VERTEX); - } + for (uint32_t i = 0; i < Polygon::PartCount(polygon); i++) { + size += 4; + size += Polygon::Part(polygon, i).Count() * sizeof(VERTEX); + } if (Polygon::PartCount(polygon) % 2 == 1) { size += 4; } @@ -68,10 +68,10 @@ struct GetRequiredSizeOp { // 4 bytes for the number of items // recursive call for each item uint32_t size = 4 + 4; - for(uint32_t i = 0; i < CollectionGeometry::PartCount(collection); i++) { - auto &part = CollectionGeometry::Part(collection, i); - size += Geometry::Match>(part); - } + for (uint32_t i = 0; i < CollectionGeometry::PartCount(collection); i++) { + auto &part = CollectionGeometry::Part(collection, i); + size += Geometry::Match>(part); + } return size; } }; @@ -80,8 +80,7 @@ template struct SerializeOp { static constexpr uint32_t MAX_DEPTH = 256; - static void SerializeVertices(const Geometry &verts, Cursor &cursor, BoundingBox &bbox, - bool update_bounds) { + static void SerializeVertices(const Geometry &verts, Cursor &cursor, BoundingBox &bbox, bool update_bounds) { // Write the vertex data auto byte_size = SinglePartGeometry::ByteSize(verts); memcpy(cursor.GetPtr(), verts.GetData(), byte_size); @@ -111,7 +110,8 @@ struct SerializeOp { SerializeVertices(point, cursor, bbox, depth != 0); } - static void Case(Geometry::Tags::LineString, const Geometry &linestring, Cursor &cursor, BoundingBox &bbox, uint32_t) { + static void Case(Geometry::Tags::LineString, const Geometry &linestring, Cursor &cursor, BoundingBox &bbox, + uint32_t) { D_ASSERT(linestring.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(linestring.GetProperties().HasM() == VERTEX::HAS_M); @@ -136,9 +136,9 @@ struct SerializeOp { cursor.Write(polygon.Count()); // Write ring lengths - for(uint32_t i = 0; i < Polygon::PartCount(polygon); i++) { - cursor.Write(Polygon::Part(polygon, i).Count()); - } + for (uint32_t i = 0; i < Polygon::PartCount(polygon); i++) { + cursor.Write(Polygon::Part(polygon, i).Count()); + } if (polygon.Count() % 2 == 1) { // Write padding (4 bytes) @@ -153,7 +153,8 @@ struct SerializeOp { } } - static void Case(Geometry::Tags::MultiPoint, const Geometry &multipoint, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::MultiPoint, const Geometry &multipoint, Cursor &cursor, BoundingBox &bbox, + uint32_t depth) { D_ASSERT(multipoint.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(multipoint.GetProperties().HasM() == VERTEX::HAS_M); @@ -164,12 +165,13 @@ struct SerializeOp { cursor.Write(multipoint.Count()); // Write point data - for (uint32_t i = 0; i < MultiPoint::PartCount(multipoint); i++) { - Case(Geometry::Tags::Point{}, MultiPoint::Part(multipoint, i), cursor, bbox, depth + 1); - } + for (uint32_t i = 0; i < MultiPoint::PartCount(multipoint); i++) { + Case(Geometry::Tags::Point {}, MultiPoint::Part(multipoint, i), cursor, bbox, depth + 1); + } } - static void Case(Geometry::Tags::MultiLineString, const Geometry &multilinestring, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::MultiLineString, const Geometry &multilinestring, Cursor &cursor, + BoundingBox &bbox, uint32_t depth) { D_ASSERT(multilinestring.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(multilinestring.GetProperties().HasM() == VERTEX::HAS_M); @@ -180,12 +182,13 @@ struct SerializeOp { cursor.Write(multilinestring.Count()); // Write linestring data - for(uint32_t i = 0; i < MultiLineString::PartCount(multilinestring); i++) { - Case(Geometry::Tags::LineString{}, MultiLineString::Part(multilinestring, i), cursor, bbox, depth + 1); - } + for (uint32_t i = 0; i < MultiLineString::PartCount(multilinestring); i++) { + Case(Geometry::Tags::LineString {}, MultiLineString::Part(multilinestring, i), cursor, bbox, depth + 1); + } } - static void Case(Geometry::Tags::MultiPolygon, const Geometry &multipolygon, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::MultiPolygon, const Geometry &multipolygon, Cursor &cursor, BoundingBox &bbox, + uint32_t depth) { D_ASSERT(multipolygon.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(multipolygon.GetProperties().HasM() == VERTEX::HAS_M); @@ -196,12 +199,13 @@ struct SerializeOp { cursor.Write(multipolygon.Count()); // Write polygon data - for(uint32_t i = 0; i < MultiPolygon::PartCount(multipolygon); i++) { - Case(Geometry::Tags::Polygon{}, MultiPolygon::Part(multipolygon, i), cursor, bbox, depth + 1); - } + for (uint32_t i = 0; i < MultiPolygon::PartCount(multipolygon); i++) { + Case(Geometry::Tags::Polygon {}, MultiPolygon::Part(multipolygon, i), cursor, bbox, depth + 1); + } } - static void Case(Geometry::Tags::GeometryCollection, const Geometry &collection, Cursor &cursor, BoundingBox &bbox, uint32_t depth) { + static void Case(Geometry::Tags::GeometryCollection, const Geometry &collection, Cursor &cursor, BoundingBox &bbox, + uint32_t depth) { D_ASSERT(collection.GetProperties().HasZ() == VERTEX::HAS_Z); D_ASSERT(collection.GetProperties().HasM() == VERTEX::HAS_M); @@ -217,10 +221,10 @@ struct SerializeOp { cursor.Write(collection.Count()); // write geometry data - for(uint32_t i = 0; i < GeometryCollection::PartCount(collection); i++) { - auto &geom = GeometryCollection::Part(collection, i); - Geometry::Match>(geom, cursor, bbox, depth + 1); - } + for (uint32_t i = 0; i < GeometryCollection::PartCount(collection); i++) { + auto &geom = GeometryCollection::Part(collection, i); + Geometry::Match>(geom, cursor, bbox, depth + 1); + } } }; @@ -270,11 +274,11 @@ geometry_t Geometry::Serialize(const Geometry &geom, Vector &result) { if (has_z && has_m) { Geometry::Match>(geom, cursor, bbox, 0); } else if (has_z) { - Geometry::Match>(geom, cursor, bbox, 0); + Geometry::Match>(geom, cursor, bbox, 0); } else if (has_m) { - Geometry::Match>(geom, cursor, bbox, 0); + Geometry::Match>(geom, cursor, bbox, 0); } else { - Geometry::Match>(geom, cursor, bbox, 0); + Geometry::Match>(geom, cursor, bbox, 0); } // Now write the bounding box @@ -308,7 +312,7 @@ class GeometryDeserializer final : GeometryProcessor { Geometry ProcessPoint(const VertexData &vertices) override { auto point = Point::CreateEmpty(HasZ(), HasM()); if (!vertices.IsEmpty()) { - Point::ReferenceData(point, vertices.data[0], vertices.count); + Point::ReferenceData(point, vertices.data[0], vertices.count); } return point; } @@ -316,7 +320,7 @@ class GeometryDeserializer final : GeometryProcessor { Geometry ProcessLineString(const VertexData &vertices) override { auto line_string = LineString::Create(allocator, vertices.count, HasZ(), HasM()); if (!vertices.IsEmpty()) { - LineString::ReferenceData(line_string, vertices.data[0], vertices.count); + LineString::ReferenceData(line_string, vertices.data[0], vertices.count); } return line_string; } @@ -326,8 +330,8 @@ class GeometryDeserializer final : GeometryProcessor { for (auto i = 0; i < state.RingCount(); i++) { auto vertices = state.Next(); if (!vertices.IsEmpty()) { - auto &part = Polygon::Part(polygon, i); - LineString::ReferenceData(part, vertices.data[0], vertices.count); + auto &part = Polygon::Part(polygon, i); + LineString::ReferenceData(part, vertices.data[0], vertices.count); } } return polygon; @@ -338,21 +342,21 @@ class GeometryDeserializer final : GeometryProcessor { case GeometryType::MULTIPOINT: { auto multi_point = MultiPoint::Create(allocator, state.ItemCount(), HasZ(), HasM()); for (auto i = 0; i < state.ItemCount(); i++) { - MultiPoint::Part(multi_point, i) = state.Next(); + MultiPoint::Part(multi_point, i) = state.Next(); } return multi_point; } case GeometryType::MULTILINESTRING: { auto multi_line_string = MultiLineString::Create(allocator, state.ItemCount(), HasZ(), HasM()); for (auto i = 0; i < state.ItemCount(); i++) { - MultiLineString::Part(multi_line_string, i) = state.Next(); + MultiLineString::Part(multi_line_string, i) = state.Next(); } return multi_line_string; } case GeometryType::MULTIPOLYGON: { auto multi_polygon = MultiPolygon::Create(allocator, state.ItemCount(), HasZ(), HasM()); for (auto i = 0; i < state.ItemCount(); i++) { - MultiPolygon::Part(multi_polygon, i) = state.Next(); + MultiPolygon::Part(multi_polygon, i) = state.Next(); } return multi_polygon; } diff --git a/spatial/src/spatial/core/geometry/wkb_reader.cpp b/spatial/src/spatial/core/geometry/wkb_reader.cpp index 6109b722..713646fb 100644 --- a/spatial/src/spatial/core/geometry/wkb_reader.cpp +++ b/spatial/src/spatial/core/geometry/wkb_reader.cpp @@ -19,7 +19,7 @@ Geometry WKBReader::Deserialize(const_data_ptr_t wkb, uint32_t size) { auto geom = ReadGeometry(cursor); // Make sure the geometry has unified vertex type, in case we got some funky nested WKB with mixed dimensions - geom.SetVertexType(arena, has_any_z, has_any_m); + geom.SetVertexType(arena, has_any_z, has_any_m); return geom; } @@ -123,7 +123,7 @@ Geometry WKBReader::ReadPolygon(Cursor &cursor, bool little_endian, bool has_z, auto polygon = Polygon::Create(arena, ring_count, has_z, has_m); for (uint32_t i = 0; i < ring_count; i++) { auto point_count = ReadInt(cursor, little_endian); - Polygon::Part(polygon, i) = LineString::Create(arena, point_count, has_z, has_m); + Polygon::Part(polygon, i) = LineString::Create(arena, point_count, has_z, has_m); ReadVertices(cursor, little_endian, has_z, has_m, Polygon::Part(polygon, i)); } return polygon; @@ -146,7 +146,8 @@ Geometry WKBReader::ReadMultiLineString(Cursor &cursor, bool little_endian, bool for (uint32_t i = 0; i < count; i++) { bool line_order = cursor.Read(); auto line_type = ReadType(cursor, line_order); - MultiLineString::Part(multi_line_string, i) = ReadLineString(cursor, line_order, line_type.has_z, line_type.has_m); + MultiLineString::Part(multi_line_string, i) = + ReadLineString(cursor, line_order, line_type.has_z, line_type.has_m); } return multi_line_string; } @@ -157,7 +158,8 @@ Geometry WKBReader::ReadMultiPolygon(Cursor &cursor, bool little_endian, bool ha for (uint32_t i = 0; i < count; i++) { bool polygon_order = cursor.Read(); auto polygon_type = ReadType(cursor, polygon_order); - MultiPolygon::Part(multi_polygon, i) = ReadPolygon(cursor, polygon_order, polygon_type.has_z, polygon_type.has_m); + MultiPolygon::Part(multi_polygon, i) = + ReadPolygon(cursor, polygon_order, polygon_type.has_z, polygon_type.has_m); } return multi_polygon; } @@ -166,7 +168,7 @@ Geometry WKBReader::ReadGeometryCollection(Cursor &cursor, bool little_endian, b uint32_t count = ReadInt(cursor, little_endian); auto geometry_collection = GeometryCollection::Create(arena, count, has_z, has_m); for (uint32_t i = 0; i < count; i++) { - GeometryCollection::Part(geometry_collection, i) = ReadGeometry(cursor); + GeometryCollection::Part(geometry_collection, i) = ReadGeometry(cursor); } return geometry_collection; } diff --git a/spatial/src/spatial/core/geometry/wkt_reader.cpp b/spatial/src/spatial/core/geometry/wkt_reader.cpp index 21287c2d..d1d25fd2 100644 --- a/spatial/src/spatial/core/geometry/wkt_reader.cpp +++ b/spatial/src/spatial/core/geometry/wkt_reader.cpp @@ -154,8 +154,8 @@ Geometry WKTReader::ParsePolygon() { Expect(')'); auto result = Polygon::Create(arena, rings.size(), has_z, has_m); for (uint32_t i = 0; i < rings.size(); i++) { - auto &ring = Polygon::Part(result, i); - LineString::CopyData(ring, arena, data_ptr_cast(rings[i].second.data()), rings[i].first); + auto &ring = Polygon::Part(result, i); + LineString::CopyData(ring, arena, data_ptr_cast(rings[i].second.data()), rings[i].first); } return result; } @@ -213,7 +213,7 @@ Geometry WKTReader::ParseMultiLineString() { Expect(')'); auto result = MultiLineString::Create(arena, lines.size(), has_z, has_m); for (uint32_t i = 0; i < lines.size(); i++) { - MultiLineString::Part(result, i) = lines[i]; + MultiLineString::Part(result, i) = lines[i]; } return result; } diff --git a/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp b/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp index d635b4c3..bf330af2 100644 --- a/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp +++ b/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp @@ -268,10 +268,10 @@ struct ConvertPolygon { auto end = i == shape->nParts - 1 ? shape->nVertices : shape->panPartStart[i + 1]; auto &ring = Polygon::Part(polygon, i); auto ring_size = end - start; - LineString::Resize(ring, arena, ring_size); + LineString::Resize(ring, arena, ring_size); for (int j = 0; j < ring_size; j++) { auto offset = start + j; - LineString::SetVertex(ring, j, {shape->padfX[offset], shape->padfY[offset]}); + LineString::SetVertex(ring, j, {shape->padfX[offset], shape->padfY[offset]}); } start = end; } @@ -294,10 +294,10 @@ struct ConvertPolygon { LineString::Resize(ring, arena, ring_size); for (int j = 0; j < ring_size; j++) { auto offset = start + j; - LineString::SetVertex(ring, j, {shape->padfX[offset], shape->padfY[offset]}); + LineString::SetVertex(ring, j, {shape->padfX[offset], shape->padfY[offset]}); } } - MultiPolygon::Part(multi_polygon, polygon_idx) = std::move(polygon); + MultiPolygon::Part(multi_polygon, polygon_idx) = std::move(polygon); } return multi_polygon; } @@ -308,7 +308,7 @@ struct ConvertMultiPoint { static Geometry Convert(SHPObjectPtr &shape, ArenaAllocator &arena) { auto multi_point = MultiPoint::Create(arena, shape->nVertices, false, false); for (int i = 0; i < shape->nVertices; i++) { - auto point = Point::CreateFromVertex(arena, VertexXY {shape->padfX[i], shape->padfY[i]}); + auto point = Point::CreateFromVertex(arena, VertexXY {shape->padfX[i], shape->padfY[i]}); MultiPoint::Part(multi_point, i) = std::move(point); } return multi_point; diff --git a/spatial/src/spatial/core/io/shapefile/shapefile_common.cpp b/spatial/src/spatial/core/io/shapefile/shapefile_common.cpp index 2d3a99f1..75676992 100644 --- a/spatial/src/spatial/core/io/shapefile/shapefile_common.cpp +++ b/spatial/src/spatial/core/io/shapefile/shapefile_common.cpp @@ -88,9 +88,9 @@ static int DuckDBShapefileRemove(void *userData, const char *filename) { try { auto &fs = *reinterpret_cast(userData); auto file = fs.OpenFile(filename, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS); - if (!file) { - return -1; - } + if (!file) { + return -1; + } auto file_type = fs.GetFileType(*file); if (file_type == FileType::FILE_TYPE_DIR) { fs.RemoveDirectory(filename); diff --git a/spatial/src/spatial/core/util/math.cpp b/spatial/src/spatial/core/util/math.cpp index b0f4952a..2f7cf705 100644 --- a/spatial/src/spatial/core/util/math.cpp +++ b/spatial/src/spatial/core/util/math.cpp @@ -8,43 +8,43 @@ namespace core { extern "C" int geos_d2sfixed_buffered_n(double f, uint32_t precision, char *result); string MathUtil::format_coord(double d) { - char buf[25]; - auto len = geos_d2sfixed_buffered_n(d, 15, buf); - buf[len] = '\0'; - return string{buf}; + char buf[25]; + auto len = geos_d2sfixed_buffered_n(d, 15, buf); + buf[len] = '\0'; + return string {buf}; } string MathUtil::format_coord(double x, double y) { - char buf[51]; - auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); - buf[res_x++] = ' '; - auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); - buf[res_x + res_y] = '\0'; - return string{buf}; + char buf[51]; + auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); + buf[res_x++] = ' '; + auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); + buf[res_x + res_y] = '\0'; + return string {buf}; } string MathUtil::format_coord(double x, double y, double zm) { - char buf[76]; - auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); - buf[res_x++] = ' '; - auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); - buf[res_x + res_y++] = ' '; - auto res_zm = geos_d2sfixed_buffered_n(zm, 15, buf + res_x + res_y); - buf[res_x + res_y + res_zm] = '\0'; - return string{buf}; + char buf[76]; + auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); + buf[res_x++] = ' '; + auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); + buf[res_x + res_y++] = ' '; + auto res_zm = geos_d2sfixed_buffered_n(zm, 15, buf + res_x + res_y); + buf[res_x + res_y + res_zm] = '\0'; + return string {buf}; } string MathUtil::format_coord(double x, double y, double z, double m) { - char buf[101]; - auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); - buf[res_x++] = ' '; - auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); - buf[res_x + res_y++] = ' '; - auto res_z = geos_d2sfixed_buffered_n(z, 15, buf + res_x + res_y); - buf[res_x + res_y + res_z++] = ' '; - auto res_m = geos_d2sfixed_buffered_n(m, 15, buf + res_x + res_y + res_z); - buf[res_x + res_y + res_z + res_m] = '\0'; - return string{buf}; + char buf[101]; + auto res_x = geos_d2sfixed_buffered_n(x, 15, buf); + buf[res_x++] = ' '; + auto res_y = geos_d2sfixed_buffered_n(y, 15, buf + res_x); + buf[res_x + res_y++] = ' '; + auto res_z = geos_d2sfixed_buffered_n(z, 15, buf + res_x + res_y); + buf[res_x + res_y + res_z++] = ' '; + auto res_m = geos_d2sfixed_buffered_n(m, 15, buf + res_x + res_y + res_z); + buf[res_x + res_y + res_z + res_m] = '\0'; + return string {buf}; } } // namespace core diff --git a/spatial/src/spatial/gdal/file_handler.cpp b/spatial/src/spatial/gdal/file_handler.cpp index 022c6d4d..cb50144d 100644 --- a/spatial/src/spatial/gdal/file_handler.cpp +++ b/spatial/src/spatial/gdal/file_handler.cpp @@ -169,8 +169,7 @@ class DuckDBFileSystemHandler : public VSIFilesystemHandler { // Check if the file is a directory #ifdef _WIN32 - if (!FileSystem::IsRemoteFile(file_name) && fs.DirectoryExists(file_name_str) && - (flags.OpenForReading())) { + if (!FileSystem::IsRemoteFile(file_name) && fs.DirectoryExists(file_name_str) && (flags.OpenForReading())) { // We can't open a directory for reading on windows without special flags // so just open nul instead, gdal will reject it when it tries to read auto file = fs.OpenFile("nul", flags); @@ -179,17 +178,15 @@ class DuckDBFileSystemHandler : public VSIFilesystemHandler { #endif // If the file is remote and NOT in write mode, we can cache it. - if (FileSystem::IsRemoteFile(file_name_str) && - !flags.OpenForWriting() && - !flags.OpenForAppending()) { + if (FileSystem::IsRemoteFile(file_name_str) && !flags.OpenForWriting() && !flags.OpenForAppending()) { // Pass the direct IO flag to the file system since we use GDAL's caching instead flags |= FileFlags::FILE_FLAGS_DIRECT_IO; - auto file = fs.OpenFile(file_name, flags | FileCompressionType::AUTO_DETECT); - return VSICreateCachedFile(new DuckDBFileHandle(std::move(file))); + auto file = fs.OpenFile(file_name, flags | FileCompressionType::AUTO_DETECT); + return VSICreateCachedFile(new DuckDBFileHandle(std::move(file))); } else { - auto file = fs.OpenFile(file_name, flags | FileCompressionType::AUTO_DETECT); + auto file = fs.OpenFile(file_name, flags | FileCompressionType::AUTO_DETECT); return new DuckDBFileHandle(std::move(file)); } } catch (std::exception &ex) { @@ -238,7 +235,8 @@ class DuckDBFileSystemHandler : public VSIFilesystemHandler { unique_ptr file; try { - file = fs.OpenFile(file_name, FileFlags::FILE_FLAGS_READ | FileCompressionType::AUTO_DETECT | FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS); + file = fs.OpenFile(file_name, FileFlags::FILE_FLAGS_READ | FileCompressionType::AUTO_DETECT | + FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS); } catch (std::exception &ex) { return -1; } diff --git a/spatial/src/spatial/gdal/functions/st_read.cpp b/spatial/src/spatial/gdal/functions/st_read.cpp index cc2a13d6..ccb20868 100644 --- a/spatial/src/spatial/gdal/functions/st_read.cpp +++ b/spatial/src/spatial/gdal/functions/st_read.cpp @@ -570,8 +570,8 @@ void GdalTableFunction::Scan(ClientContext &context, TableFunctionInput &input, validity.SetInvalid(out_idx); return core::geometry_t {}; } - auto geom = state.wkb_reader.Deserialize(input); - return core::Geometry::Serialize(geom, geom_vec); + auto geom = state.wkb_reader.Deserialize(input); + return core::Geometry::Serialize(geom, geom_vec); }); output.data[col_idx].ReferenceAndSetType(geom_vec); } diff --git a/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp index 2e0c425e..ec1c25a7 100644 --- a/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp @@ -115,15 +115,12 @@ static void GeodesicGeometryFunction(DataChunk &args, ExpressionState &state, Ve const GeographicLib::Geodesic &geod = GeographicLib::Geodesic::WGS84(); auto comp = GeographicLib::PolygonArea(geod, false); - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { - auto geom = Geometry::Deserialize(arena, input); - double area = 0; - Geometry::ExtractPolygons(geom, [&](const Geometry &geom) { - area += PolygonArea(geom, comp); - }); - return area; - }); + UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + double area = 0; + Geometry::ExtractPolygons(geom, [&](const Geometry &geom) { area += PolygonArea(geom, comp); }); + return area; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp index 238920fc..6ec8e4fa 100644 --- a/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp @@ -60,9 +60,9 @@ static void GeodesicLineString2DFunction(DataChunk &args, ExpressionState &state //------------------------------------------------------------------------------ static double LineLength(const Geometry &line, GeographicLib::PolygonArea &comp) { comp.Clear(); - for(VertexXY vert : LineString::Vertices(line)) { - comp.AddPoint(vert.x, vert.y); - } + for (VertexXY vert : LineString::Vertices(line)) { + comp.AddPoint(vert.x, vert.y); + } double _area; double linestring_length; comp.Compute(false, true, linestring_length, _area); @@ -79,15 +79,12 @@ static void GeodesicGeometryFunction(DataChunk &args, ExpressionState &state, Ve const GeographicLib::Geodesic &geod = GeographicLib::Geodesic::WGS84(); auto comp = GeographicLib::PolygonArea(geod, true); - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { - auto geom = Geometry::Deserialize(arena, input); - double length = 0.0; - Geometry::ExtractLines(geom, [&](const Geometry &line) { - length += LineLength(line, comp); - }); - return length; - }); + UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + double length = 0.0; + Geometry::ExtractLines(geom, [&](const Geometry &line) { length += LineLength(line, comp); }); + return length; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp index 85d42ddd..e9e219bc 100644 --- a/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp @@ -99,15 +99,12 @@ static void GeodesicGeometryFunction(DataChunk &args, ExpressionState &state, Ve const GeographicLib::Geodesic &geod = GeographicLib::Geodesic::WGS84(); auto comp = GeographicLib::PolygonArea(geod, false); - UnaryExecutor::Execute( - input, result, count, [&](geometry_t input) { - auto geom = Geometry::Deserialize(arena, input); - auto length = 0.0; - Geometry::ExtractPolygons(geom, [&](const Geometry &poly) { - length += PolygonPerimeter(poly, comp); - }); - return length; - }); + UnaryExecutor::Execute(input, result, count, [&](geometry_t input) { + auto geom = Geometry::Deserialize(arena, input); + auto length = 0.0; + Geometry::ExtractPolygons(geom, [&](const Geometry &poly) { length += PolygonPerimeter(poly, comp); }); + return length; + }); if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); diff --git a/spatial/src/spatial/geos/functions/scalar/st_is_valid.cpp b/spatial/src/spatial/geos/functions/scalar/st_is_valid.cpp index ae746ed3..1463bfc4 100644 --- a/spatial/src/spatial/geos/functions/scalar/st_is_valid.cpp +++ b/spatial/src/spatial/geos/functions/scalar/st_is_valid.cpp @@ -22,42 +22,42 @@ static bool IsValidForGeos(Geometry &geometry) { case GeometryType::POLYGON: { // Every ring needs 0 or at least 4 points - for(uint32_t i = 0; i < Polygon::PartCount(geometry); i++) { - auto &ring = Polygon::Part(geometry, i); - if (LineString::VertexCount(ring) < 4) { - return false; - } - } + for (uint32_t i = 0; i < Polygon::PartCount(geometry); i++) { + auto &ring = Polygon::Part(geometry, i); + if (LineString::VertexCount(ring) < 4) { + return false; + } + } return true; } case GeometryType::MULTILINESTRING: { - for(uint32_t i = 0; i < MultiLineString::PartCount(geometry); i++) { - auto &linestring = MultiLineString::Part(geometry, i); - if (LineString::VertexCount(linestring) == 1) { - return false; - } - } + for (uint32_t i = 0; i < MultiLineString::PartCount(geometry); i++) { + auto &linestring = MultiLineString::Part(geometry, i); + if (LineString::VertexCount(linestring) == 1) { + return false; + } + } return true; } case GeometryType::MULTIPOLYGON: { - for(uint32_t i = 0; i < MultiPolygon::PartCount(geometry); i++) { - auto &polygon = MultiPolygon::Part(geometry, i); - for(uint32_t j = 0; j < Polygon::PartCount(polygon); j++) { - auto &ring = Polygon::Part(polygon, j); - if (LineString::VertexCount(ring) < 4) { - return false; - } - } - } + for (uint32_t i = 0; i < MultiPolygon::PartCount(geometry); i++) { + auto &polygon = MultiPolygon::Part(geometry, i); + for (uint32_t j = 0; j < Polygon::PartCount(polygon); j++) { + auto &ring = Polygon::Part(polygon, j); + if (LineString::VertexCount(ring) < 4) { + return false; + } + } + } return true; } case GeometryType::GEOMETRYCOLLECTION: { - for(uint32_t i = 0; i < GeometryCollection::PartCount(geometry); i++) { - auto &geom = GeometryCollection::Part(geometry, i); - if (!IsValidForGeos(geom)) { - return false; - } - } + for (uint32_t i = 0; i < GeometryCollection::PartCount(geometry); i++) { + auto &geom = GeometryCollection::Part(geometry, i); + if (!IsValidForGeos(geom)) { + return false; + } + } return true; } default: diff --git a/spatial/src/spatial/proj/functions.cpp b/spatial/src/spatial/proj/functions.cpp index c3cc45d3..990c2109 100644 --- a/spatial/src/spatial/proj/functions.cpp +++ b/spatial/src/spatial/proj/functions.cpp @@ -239,7 +239,7 @@ struct TransformOp { auto vertex = SinglePartGeometry::GetVertex(geom, i); auto transformed = proj_trans(crs, PJ_FWD, proj_coord(vertex.x, vertex.y, 0, 0)).xy; // we own the array, so we can use SetUnsafe - SinglePartGeometry::SetVertex(geom, i, { transformed.x, transformed.y }); + SinglePartGeometry::SetVertex(geom, i, {transformed.x, transformed.y}); } } static void Case(Geometry::Tags::MultiPartGeometry, Geometry &geom, PJ *crs, ArenaAllocator &arena) { @@ -296,8 +296,8 @@ static void GeometryTransformFunction(DataChunk &args, ExpressionState &state, V UnaryExecutor::Execute(geom_vec, result, count, [&](geometry_t input_geom) { auto geom = Geometry::Deserialize(arena, input_geom); - Geometry::Match(geom, crs.get(), arena); - return Geometry::Serialize(geom, result); + Geometry::Match(geom, crs.get(), arena); + return Geometry::Serialize(geom, result); }); } else { // General case: projections are not constant @@ -322,7 +322,7 @@ static void GeometryTransformFunction(DataChunk &args, ExpressionState &state, V } auto geom = Geometry::Deserialize(arena, input_geom); - Geometry::Match(geom, crs.get(), arena); + Geometry::Match(geom, crs.get(), arena); return Geometry::Serialize(geom, result); }); } From 0ef857b525cd1a4ec13d256c55138fed438d1595 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 24 Apr 2024 11:29:16 +0200 Subject: [PATCH 04/13] commit cmakelists --- spatial/src/spatial/core/util/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 spatial/src/spatial/core/util/CMakeLists.txt diff --git a/spatial/src/spatial/core/util/CMakeLists.txt b/spatial/src/spatial/core/util/CMakeLists.txt new file mode 100644 index 00000000..fcbaa0c3 --- /dev/null +++ b/spatial/src/spatial/core/util/CMakeLists.txt @@ -0,0 +1,5 @@ +set(EXTENSION_SOURCES + ${EXTENSION_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/math.cpp + PARENT_SCOPE +) \ No newline at end of file From 11ba3837a9bc799daf442f1385912b244b330e48 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 24 Apr 2024 12:26:05 +0200 Subject: [PATCH 05/13] update CI --- .github/workflows/StableDistributionPipeline.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/StableDistributionPipeline.yml b/.github/workflows/StableDistributionPipeline.yml index 2e16bfa1..d9c223b4 100644 --- a/.github/workflows/StableDistributionPipeline.yml +++ b/.github/workflows/StableDistributionPipeline.yml @@ -5,13 +5,13 @@ name: Stable Extension Distribution Pipeline on: pull_request: branches: - - v0.10.1 + - v0.10.2 paths-ignore: - '**/README.md' - 'doc/**' push: branches: - - v0.10.1 + - v0.10.2 paths-ignore: - '**/README.md' - 'doc/**' @@ -24,17 +24,17 @@ concurrency: jobs: duckdb-stable-build: name: Build extension binaries - uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v0.10.1 + uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v0.10.2 with: - duckdb_version: v0.10.1 + duckdb_version: v0.10.2 extension_name: spatial duckdb-stable-deploy: name: Deploy extension binaries needs: duckdb-stable-build - uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@v0.10.1 + uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@v0.10.2 secrets: inherit with: - duckdb_version: v0.10.1 + duckdb_version: v0.10.2 extension_name: spatial - deploy_latest: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/v0.10.1' }} + deploy_latest: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/v0.10.2' }} From 5f7844ecd98e38a83d0f855fb3000d1950d954cf Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 24 Apr 2024 12:27:09 +0200 Subject: [PATCH 06/13] update CI --- .github/workflows/StableDistributionPipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/StableDistributionPipeline.yml b/.github/workflows/StableDistributionPipeline.yml index d9c223b4..e02f661d 100644 --- a/.github/workflows/StableDistributionPipeline.yml +++ b/.github/workflows/StableDistributionPipeline.yml @@ -32,7 +32,7 @@ jobs: duckdb-stable-deploy: name: Deploy extension binaries needs: duckdb-stable-build - uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@v0.10.2 + uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@v0.10.1 secrets: inherit with: duckdb_version: v0.10.2 From 72b498852e43d56a6c4e92cfadaebf76bd18e602 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 24 Apr 2024 12:28:20 +0200 Subject: [PATCH 07/13] update CI --- .github/workflows/StableDistributionPipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/StableDistributionPipeline.yml b/.github/workflows/StableDistributionPipeline.yml index e02f661d..325b2e28 100644 --- a/.github/workflows/StableDistributionPipeline.yml +++ b/.github/workflows/StableDistributionPipeline.yml @@ -24,7 +24,7 @@ concurrency: jobs: duckdb-stable-build: name: Build extension binaries - uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v0.10.2 + uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v0.10.1 with: duckdb_version: v0.10.2 extension_name: spatial From ba52349987d15e4dfb0149c0369ed887e5131eda Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 24 Apr 2024 12:31:46 +0200 Subject: [PATCH 08/13] update CI again --- .github/workflows/StableDistributionPipeline.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/StableDistributionPipeline.yml b/.github/workflows/StableDistributionPipeline.yml index 325b2e28..69c74754 100644 --- a/.github/workflows/StableDistributionPipeline.yml +++ b/.github/workflows/StableDistributionPipeline.yml @@ -24,7 +24,7 @@ concurrency: jobs: duckdb-stable-build: name: Build extension binaries - uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v0.10.1 + uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@main with: duckdb_version: v0.10.2 extension_name: spatial @@ -32,7 +32,7 @@ jobs: duckdb-stable-deploy: name: Deploy extension binaries needs: duckdb-stable-build - uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@v0.10.1 + uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@main secrets: inherit with: duckdb_version: v0.10.2 From e7987ff725b07ec9489ffd6aaec1ebf1fe165177 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Thu, 25 Apr 2024 13:54:40 +0200 Subject: [PATCH 09/13] pin to v0.10.2 again --- .github/workflows/StableDistributionPipeline.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/StableDistributionPipeline.yml b/.github/workflows/StableDistributionPipeline.yml index 69c74754..d9c223b4 100644 --- a/.github/workflows/StableDistributionPipeline.yml +++ b/.github/workflows/StableDistributionPipeline.yml @@ -24,7 +24,7 @@ concurrency: jobs: duckdb-stable-build: name: Build extension binaries - uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@main + uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v0.10.2 with: duckdb_version: v0.10.2 extension_name: spatial @@ -32,7 +32,7 @@ jobs: duckdb-stable-deploy: name: Deploy extension binaries needs: duckdb-stable-build - uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@main + uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@v0.10.2 secrets: inherit with: duckdb_version: v0.10.2 From 94e30a9b072ad3e73c61297da8eeaf6af5fd09e7 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Thu, 25 Apr 2024 22:56:24 +0200 Subject: [PATCH 10/13] remove misaligned ref/ptr for now --- .../spatial/core/geometry/geometry.hpp | 143 ---------------- .../spatial/core/util/misaligned_ptr.hpp | 156 ------------------ .../spatial/core/util/misaligned_ref.hpp | 51 ------ .../core/functions/scalar/st_makeline.cpp | 2 +- .../functions/st_length_spheroid.cpp | 7 +- 5 files changed, 5 insertions(+), 354 deletions(-) delete mode 100644 spatial/include/spatial/core/util/misaligned_ptr.hpp delete mode 100644 spatial/include/spatial/core/util/misaligned_ref.hpp diff --git a/spatial/include/spatial/core/geometry/geometry.hpp b/spatial/include/spatial/core/geometry/geometry.hpp index 7f9ab0a6..288dce9a 100644 --- a/spatial/include/spatial/core/geometry/geometry.hpp +++ b/spatial/include/spatial/core/geometry/geometry.hpp @@ -3,7 +3,6 @@ #include "spatial/common.hpp" #include "spatial/core/geometry/geometry_properties.hpp" #include "spatial/core/util/cursor.hpp" -#include "spatial/core/util/misaligned_ptr.hpp" #include "spatial/core/geometry/geometry_type.hpp" #include "spatial/core/geometry/vertex.hpp" @@ -402,96 +401,6 @@ class ConstPartView { } }; -class VertexView { -private: - data_ptr_t beg_ptr; - data_ptr_t end_ptr; - uint32_t vertex_size; - -public: - VertexView(data_ptr_t beg, data_ptr_t end, uint32_t vertex_size) - : beg_ptr(beg), end_ptr(end), vertex_size(vertex_size) { - } - - StridedPtr begin() { - return {beg_ptr, vertex_size}; - } - StridedPtr end() { - return {end_ptr, vertex_size}; - } - MisalignedRef operator[](uint32_t index) { - D_ASSERT(beg_ptr + index * vertex_size < end_ptr); - return {beg_ptr + index * vertex_size}; - } -}; - -class ConstVertexView { -private: - const_data_ptr_t beg_ptr; - const_data_ptr_t end_ptr; - uint32_t vertex_size; - -public: - ConstVertexView(const_data_ptr_t beg, const_data_ptr_t end, uint32_t vertex_size) - : beg_ptr(beg), end_ptr(end), vertex_size(vertex_size) { - } - - ConstStridedPtr begin() const { - return {beg_ptr, vertex_size}; - } - ConstStridedPtr end() const { - return {end_ptr, vertex_size}; - } - ConstMisalignedRef operator[](uint32_t index) { - D_ASSERT(beg_ptr + index * vertex_size < end_ptr); - return {beg_ptr + index * vertex_size}; - } -}; - -template -class TypedVertexView { - static_assert(V::IS_VERTEX, "V must be a vertex type"); - data_ptr_t beg_ptr; - data_ptr_t end_ptr; - -public: - TypedVertexView(data_ptr_t beg, data_ptr_t end) : beg_ptr(beg), end_ptr(end) { - } - - MisalignedPtr begin() { - return {beg_ptr}; - } - MisalignedPtr end() { - return {end_ptr}; - } - MisalignedRef operator[](uint32_t index) { - D_ASSERT(beg_ptr + index * sizeof(V) < end_ptr); - return {beg_ptr + index * sizeof(V)}; - } -}; - -template -class ConstTypedVertexView { - static_assert(V::IS_VERTEX, "V must be a vertex type"); - const_data_ptr_t beg_ptr; - const_data_ptr_t end_ptr; - -public: - ConstTypedVertexView(const_data_ptr_t beg, const_data_ptr_t end) : beg_ptr(beg), end_ptr(end) { - } - - ConstMisalignedPtr begin() { - return {beg_ptr}; - } - ConstMisalignedPtr end() { - return {end_ptr}; - } - ConstMisalignedRef operator[](uint32_t index) { - D_ASSERT(beg_ptr + index * sizeof(V) < end_ptr); - return {beg_ptr + index * sizeof(V)}; - } -}; - //------------------------------------------------------------------------------ // Accessors //------------------------------------------------------------------------------ @@ -594,31 +503,9 @@ struct SinglePartGeometry { template static void SetVertex(Geometry &geom, uint32_t index, const V &vertex); - template - static MisalignedRef Vertex(Geometry &geom, uint32_t index); - static MisalignedRef Vertex(Geometry &geom, uint32_t index); - - template - static ConstMisalignedRef Vertex(const Geometry &geom, uint32_t index); - static ConstMisalignedRef Vertex(const Geometry &geom, uint32_t index); - static uint32_t VertexCount(const Geometry &geom); static uint32_t VertexSize(const Geometry &geom); static uint32_t ByteSize(const Geometry &geom); - - static VertexView Vertices(Geometry &geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - auto count = VertexCount(geom); - auto size = VertexSize(geom); - return {geom.GetData(), geom.GetData() + count * size, size}; - } - - static ConstVertexView Vertices(const Geometry &geom) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - auto count = VertexCount(geom); - auto size = VertexSize(geom); - return {geom.GetData(), geom.GetData() + count * size, size}; - } }; inline VertexXY SinglePartGeometry::GetVertex(const Geometry &geom, uint32_t index) { @@ -651,36 +538,6 @@ inline void SinglePartGeometry::SetVertex(Geometry &geom, uint32_t index, const Store(vertex, geom.GetData() + index * sizeof(V)); } -template -inline MisalignedRef SinglePartGeometry::Vertex(Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); - D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); - D_ASSERT(index < geom.data_count); - return {geom.GetData() + index * sizeof(V)}; -} - -inline MisalignedRef SinglePartGeometry::Vertex(Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(index < geom.data_count); - return {geom.GetData() + index * geom.GetProperties().VertexSize()}; -} - -template -inline ConstMisalignedRef SinglePartGeometry::Vertex(const Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(V::HAS_Z == geom.GetProperties().HasZ()); - D_ASSERT(V::HAS_M == geom.GetProperties().HasM()); - D_ASSERT(index < geom.data_count); - return {geom.GetData() + index * sizeof(V)}; -} - -inline ConstMisalignedRef SinglePartGeometry::Vertex(const Geometry &geom, uint32_t index) { - D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); - D_ASSERT(index < geom.data_count); - return {geom.GetData() + index * geom.GetProperties().VertexSize()}; -} - inline uint32_t SinglePartGeometry::VertexCount(const Geometry &geom) { D_ASSERT(GeometryTypes::IsSinglePart(geom.GetType())); return geom.data_count; diff --git a/spatial/include/spatial/core/util/misaligned_ptr.hpp b/spatial/include/spatial/core/util/misaligned_ptr.hpp deleted file mode 100644 index b3bd1df8..00000000 --- a/spatial/include/spatial/core/util/misaligned_ptr.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once - -#include "spatial/common.hpp" -#include "spatial/core/util/misaligned_ref.hpp" - -namespace spatial { - -namespace core { - -template -class MisalignedPtrBase { -private: - PTR ptr_; - -public: - using iterator_category = std::random_access_iterator_tag; - using value_type = TYPE; - using difference_type = std::ptrdiff_t; - - reference operator*() const noexcept { - return {ptr_}; - } - reference operator[](std::size_t i) noexcept { - return reference(ptr_ + i * sizeof(TYPE)); - } - value_type operator[](std::size_t i) const noexcept { - return Load(ptr_ + i * sizeof(TYPE)); - } - - MisalignedPtrBase(PTR ptr) noexcept : ptr_(ptr) { - } - MisalignedPtrBase operator++(int) noexcept { - return MisalignedPtr(ptr_ + sizeof(TYPE)); - } - MisalignedPtrBase operator--(int) noexcept { - return MisalignedPtr(ptr_ - sizeof(TYPE)); - } - MisalignedPtrBase operator+(difference_type d) noexcept { - return MisalignedPtr(ptr_ + d * sizeof(TYPE)); - } - MisalignedPtrBase operator-(difference_type d) noexcept { - return MisalignedPtr(ptr_ - d * sizeof(TYPE)); - } - - MisalignedPtrBase &operator++() noexcept { - ptr_ += sizeof(TYPE); - return *this; - } - MisalignedPtrBase &operator--() noexcept { - ptr_ -= sizeof(TYPE); - return *this; - } - MisalignedPtrBase &operator+=(difference_type d) noexcept { - ptr_ += d * sizeof(TYPE); - return *this; - } - MisalignedPtrBase &operator-=(difference_type d) noexcept { - ptr_ -= d * sizeof(TYPE); - return *this; - } - - bool operator==(MisalignedPtrBase const &other) noexcept { - return ptr_ == other.ptr_; - } - bool operator!=(MisalignedPtrBase const &other) noexcept { - return ptr_ != other.ptr_; - } -}; - -template -class MisalignedPtr : public MisalignedPtrBase, MisalignedRef> {}; - -template -class ConstMisalignedPtr : public MisalignedPtrBase, ConstMisalignedRef> { -}; - -template -class StridedPtrBase { -private: - PTR ptr; - size_t stride; - -public: - using iterator_category = std::random_access_iterator_tag; - using value_type = TYPE; - using difference_type = std::ptrdiff_t; - - reference operator*() const noexcept { - return {ptr}; - } - reference operator[](std::size_t i) noexcept { - return reference(ptr + i * stride); - } - value_type operator[](std::size_t i) const noexcept { - return Load(ptr + i * stride); - } - - StridedPtrBase(PTR ptr, size_t stride) noexcept : ptr(ptr), stride(stride) { - } - StridedPtrBase operator++(int) noexcept { - return StridedPtrBase(ptr + stride, stride); - } - StridedPtrBase operator--(int) noexcept { - return StridedPtrBase(ptr - stride, stride); - } - StridedPtrBase operator+(difference_type d) noexcept { - return StridedPtrBase(ptr + d * stride, stride); - } - StridedPtrBase operator-(difference_type d) noexcept { - return StridedPtrBase(ptr - d * stride, stride); - } - - StridedPtrBase &operator++() noexcept { - ptr += stride; - return *this; - } - StridedPtrBase &operator--() noexcept { - ptr -= stride; - return *this; - } - StridedPtrBase &operator+=(difference_type d) noexcept { - ptr += d * stride; - return *this; - } - StridedPtrBase &operator-=(difference_type d) noexcept { - ptr -= d * stride; - return *this; - } - - bool operator==(StridedPtrBase const &other) noexcept { - return ptr == other.ptr; - } - bool operator!=(StridedPtrBase const &other) noexcept { - return ptr != other.ptr; - } -}; - -template -class StridedPtr : public StridedPtrBase, MisalignedRef> { -public: - StridedPtr(data_ptr_t ptr, size_t stride) noexcept - : StridedPtrBase, MisalignedRef>(ptr, stride) { - } -}; - -template -class ConstStridedPtr : public StridedPtrBase, ConstMisalignedRef> { -public: - ConstStridedPtr(const_data_ptr_t ptr, size_t stride) noexcept - : StridedPtrBase, ConstMisalignedRef>(ptr, stride) { - } -}; - -} // namespace core - -} // namespace spatial \ No newline at end of file diff --git a/spatial/include/spatial/core/util/misaligned_ref.hpp b/spatial/include/spatial/core/util/misaligned_ref.hpp deleted file mode 100644 index 153c5a5f..00000000 --- a/spatial/include/spatial/core/util/misaligned_ref.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "spatial/common.hpp" - -namespace spatial { - -namespace core { - -template -class MisalignedRef { - using TYPE = typename std::remove_const::type; - data_ptr_t ptr_; - -public: - MisalignedRef(data_ptr_t ptr) noexcept : ptr_(ptr) { - } - - operator TYPE() const noexcept { - return Load(ptr_); - } - MisalignedRef &operator=(TYPE const &v) noexcept { - Store(v, ptr_); - return *this; - } - void reset(data_ptr_t ptr) noexcept { - ptr_ = ptr; - } - data_ptr_t ptr() const noexcept { - return ptr_; - } -}; - -template -class ConstMisalignedRef { - using TYPE = typename std::remove_const::type; - const_data_ptr_t ptr_; - -public: - ConstMisalignedRef(const_data_ptr_t ptr) noexcept : ptr_(ptr) { - } - operator TYPE() const noexcept { - return Load(ptr_); - } - const_data_ptr_t ptr() const noexcept { - return ptr_; - } -}; - -} // namespace core - -} // namespace spatial \ No newline at end of file diff --git a/spatial/src/spatial/core/functions/scalar/st_makeline.cpp b/spatial/src/spatial/core/functions/scalar/st_makeline.cpp index 3716d27f..76e45305 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makeline.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makeline.cpp @@ -48,7 +48,7 @@ static void MakeLineListFunction(DataChunk &args, ExpressionState &state, Vector if (Point::IsEmpty(point)) { continue; } - LineString::Vertex(line, vertex_idx++) = Point::GetVertex(point); + LineString::SetVertex(line, vertex_idx++, Point::GetVertex(point)); } // Shrink the vertex array to the actual size diff --git a/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp index 6ec8e4fa..446aae19 100644 --- a/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp @@ -60,9 +60,10 @@ static void GeodesicLineString2DFunction(DataChunk &args, ExpressionState &state //------------------------------------------------------------------------------ static double LineLength(const Geometry &line, GeographicLib::PolygonArea &comp) { comp.Clear(); - for (VertexXY vert : LineString::Vertices(line)) { - comp.AddPoint(vert.x, vert.y); - } + for(uint32_t i = 0; i < LineString::VertexCount(line); i++) { + auto vert = LineString::GetVertex(line, i); + comp.AddPoint(vert.x, vert.y); + } double _area; double linestring_length; comp.Compute(false, true, linestring_length, _area); From 0c972d9d446fe8e480ec345a9ea91ff437686418 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Thu, 25 Apr 2024 23:01:54 +0200 Subject: [PATCH 11/13] update duckdb also --- duckdb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/duckdb b/duckdb index 6598220b..1601d94f 160000 --- a/duckdb +++ b/duckdb @@ -1 +1 @@ -Subproject commit 6598220b953b0be8a612ea1a9d5c1bd85c5379c8 +Subproject commit 1601d94f94a7e0d2eb805a94803eb1e3afbbe4ed From 0a028bb1d7fac85cc075d648143fb007e2a21bac Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Mon, 6 May 2024 17:50:28 +0200 Subject: [PATCH 12/13] fix typo --- spatial/include/spatial/core/geometry/geometry.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spatial/include/spatial/core/geometry/geometry.hpp b/spatial/include/spatial/core/geometry/geometry.hpp index 288dce9a..efc60928 100644 --- a/spatial/include/spatial/core/geometry/geometry.hpp +++ b/spatial/include/spatial/core/geometry/geometry.hpp @@ -797,7 +797,7 @@ inline Geometry Polygon::CreateFromBox(ArenaAllocator &alloc, double minx, doubl auto &ring = Polygon::Part(polygon, 0); LineString::Resize(ring, alloc, 5); LineString::SetVertex(ring, 0, {minx, miny}); - LineString::SetVertex(ring, 1, {miny, maxy}); + LineString::SetVertex(ring, 1, {minx, maxy}); LineString::SetVertex(ring, 2, {maxx, maxy}); LineString::SetVertex(ring, 3, {maxx, miny}); LineString::SetVertex(ring, 4, {minx, miny}); From 988b90d255ce73f16a764acc8ff1c246f429742c Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Thu, 16 May 2024 16:16:29 +0200 Subject: [PATCH 13/13] apply duckdb patches --- duckdb | 2 +- spatial/include/spatial/gdal/functions.hpp | 5 +++-- spatial/src/spatial/core/io/osm/st_read_osm.cpp | 5 +++-- spatial/src/spatial/core/io/shapefile/read_shapefile.cpp | 5 +++-- .../src/spatial/core/io/shapefile/read_shapefile_meta.cpp | 7 +++++-- spatial/src/spatial/gdal/functions/st_read.cpp | 7 ++++--- spatial/src/spatial/gdal/functions/st_read_meta.cpp | 6 ++---- 7 files changed, 21 insertions(+), 16 deletions(-) diff --git a/duckdb b/duckdb index 6598220b..148a1025 160000 --- a/duckdb +++ b/duckdb @@ -1 +1 @@ -Subproject commit 6598220b953b0be8a612ea1a9d5c1bd85c5379c8 +Subproject commit 148a1025d1d486d4c2751728bb6ac70e4f3bea61 diff --git a/spatial/include/spatial/gdal/functions.hpp b/spatial/include/spatial/gdal/functions.hpp index 9bf2945c..0a4bb2a5 100644 --- a/spatial/include/spatial/gdal/functions.hpp +++ b/spatial/include/spatial/gdal/functions.hpp @@ -3,6 +3,7 @@ #include "duckdb/function/table/arrow.hpp" #include "duckdb/parser/parsed_data/copy_info.hpp" #include "duckdb/function/copy_function.hpp" +#include "duckdb/function/replacement_scan.hpp" #include "spatial/common.hpp" @@ -26,8 +27,8 @@ struct GdalTableFunction : ArrowTableFunction { static unique_ptr Cardinality(ClientContext &context, const FunctionData *data); - static unique_ptr ReplacementScan(ClientContext &context, const string &table_name, - ReplacementScanData *data); + static unique_ptr ReplacementScan(ClientContext &context, ReplacementScanInput &input, + optional_ptr data); public: static void Register(DatabaseInstance &db); diff --git a/spatial/src/spatial/core/io/osm/st_read_osm.cpp b/spatial/src/spatial/core/io/osm/st_read_osm.cpp index 11055c9c..72b3b713 100644 --- a/spatial/src/spatial/core/io/osm/st_read_osm.cpp +++ b/spatial/src/spatial/core/io/osm/st_read_osm.cpp @@ -839,8 +839,9 @@ static idx_t GetBatchIndex(ClientContext &context, const FunctionData *bind_data return state.block->block_idx; } -static unique_ptr ReadOsmPBFReplacementScan(ClientContext &context, const string &table_name, - ReplacementScanData *data) { +static unique_ptr ReadOsmPBFReplacementScan(ClientContext &context, ReplacementScanInput &input, + optional_ptr data) { + auto &table_name = input.table_name; // Check if the table name ends with .osm.pbf if (!StringUtil::EndsWith(StringUtil::Lower(table_name), ".osm.pbf")) { return nullptr; diff --git a/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp b/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp index fcc182ea..1e7b5f75 100644 --- a/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp +++ b/spatial/src/spatial/core/io/shapefile/read_shapefile.cpp @@ -540,8 +540,9 @@ static unique_ptr GetCardinality(ClientContext &context, const F return result; } -static unique_ptr GetReplacementScan(ClientContext &context, const string &table_name, - ReplacementScanData *data) { +static unique_ptr GetReplacementScan(ClientContext &context, ReplacementScanInput &input, + optional_ptr data) { + auto &table_name = input.table_name; // Check if the table name ends with .shp if (!StringUtil::EndsWith(StringUtil::Lower(table_name), ".shp")) { return nullptr; diff --git a/spatial/src/spatial/core/io/shapefile/read_shapefile_meta.cpp b/spatial/src/spatial/core/io/shapefile/read_shapefile_meta.cpp index da3fe1ee..d42342a9 100644 --- a/spatial/src/spatial/core/io/shapefile/read_shapefile_meta.cpp +++ b/spatial/src/spatial/core/io/shapefile/read_shapefile_meta.cpp @@ -47,8 +47,11 @@ static ShapeTypeEntry shape_type_map[] = { static unique_ptr ShapeFileMetaBind(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { auto result = make_uniq(); - auto files = MultiFileReader::GetFileList(context, input.inputs[0], "ShapeFiles", FileGlobOptions::ALLOW_EMPTY); - for (auto &file : files) { + + auto multi_file_reader = MultiFileReader::Create(input.table_function); + auto file_list = multi_file_reader->CreateFileList(context, input.inputs[0], FileGlobOptions::ALLOW_EMPTY); + + for (auto &file : file_list->Files()) { if (StringUtil::EndsWith(StringUtil::Lower(file), ".shp")) { result->files.push_back(file); } diff --git a/spatial/src/spatial/gdal/functions/st_read.cpp b/spatial/src/spatial/gdal/functions/st_read.cpp index 9edf968a..793a4dfd 100644 --- a/spatial/src/spatial/gdal/functions/st_read.cpp +++ b/spatial/src/spatial/gdal/functions/st_read.cpp @@ -5,6 +5,7 @@ #include "duckdb/planner/filter/conjunction_filter.hpp" #include "duckdb/planner/filter/constant_filter.hpp" #include "duckdb/planner/table_filter.hpp" +#include "duckdb/parser/tableref.hpp" #include "duckdb/function/function.hpp" #include "duckdb/function/replacement_scan.hpp" @@ -592,9 +593,9 @@ unique_ptr GdalTableFunction::Cardinality(ClientContext &context return result; } -unique_ptr GdalTableFunction::ReplacementScan(ClientContext &, const string &table_name, - ReplacementScanData *) { - +unique_ptr GdalTableFunction::ReplacementScan(ClientContext &, ReplacementScanInput &input, + optional_ptr) { + auto &table_name = input.table_name; auto lower_name = StringUtil::Lower(table_name); // Check if the table name ends with some common geospatial file extensions if (StringUtil::EndsWith(lower_name, ".gpkg") || StringUtil::EndsWith(lower_name, ".fgb")) { diff --git a/spatial/src/spatial/gdal/functions/st_read_meta.cpp b/spatial/src/spatial/gdal/functions/st_read_meta.cpp index 2293072c..f950fe77 100644 --- a/spatial/src/spatial/gdal/functions/st_read_meta.cpp +++ b/spatial/src/spatial/gdal/functions/st_read_meta.cpp @@ -56,12 +56,10 @@ static LogicalType LAYER_TYPE = LogicalType::STRUCT({ static unique_ptr Bind(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { - - auto file_name = input.inputs[0].GetValue(); auto result = make_uniq(); - result->file_names = - MultiFileReader::GetFileList(context, input.inputs[0], "gdal metadata", FileGlobOptions::ALLOW_EMPTY); + auto multi_file_reader = MultiFileReader::Create(input.table_function); + result->file_names = multi_file_reader->CreateFileList(context, input.inputs[0], FileGlobOptions::ALLOW_EMPTY)->GetAllFiles(); names.push_back("file_name"); return_types.push_back(LogicalType::VARCHAR);