From aa500cf0ce6f40ef443255c98d53a0d1f74c8f33 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Mon, 11 Sep 2023 14:48:36 +0200 Subject: [PATCH 1/2] Use DuckDB's filesystem for GDAL I/O by default (works with httpfsgit status). Throw proper exceptions on GDAL errors --- spatial/include/spatial/gdal/file_handler.hpp | 20 ++ spatial/include/spatial/gdal/functions.hpp | 2 + spatial/src/spatial/gdal/CMakeLists.txt | 1 + spatial/src/spatial/gdal/file_handler.cpp | 200 ++++++++++++++++++ .../src/spatial/gdal/functions/st_read.cpp | 25 ++- .../src/spatial/gdal/functions/st_write.cpp | 9 +- spatial/src/spatial/gdal/module.cpp | 40 +++- 7 files changed, 288 insertions(+), 9 deletions(-) create mode 100644 spatial/include/spatial/gdal/file_handler.hpp create mode 100644 spatial/src/spatial/gdal/file_handler.cpp diff --git a/spatial/include/spatial/gdal/file_handler.hpp b/spatial/include/spatial/gdal/file_handler.hpp new file mode 100644 index 00000000..bfbd43d2 --- /dev/null +++ b/spatial/include/spatial/gdal/file_handler.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "spatial/common.hpp" + +namespace spatial { + +namespace gdal { + +struct GdalFileHandler { + static void Register(ClientContext &context); + + // This is a workaround to allow the global file handler to access the current client context + // by storing it in a thread_local variable before executing a GDAL IO operation + static void SetLocalClientContext(ClientContext &context); + static ClientContext &GetLocalClientContext(); +}; + +} // namespace gdal + +} // namespace spatial diff --git a/spatial/include/spatial/gdal/functions.hpp b/spatial/include/spatial/gdal/functions.hpp index 383ecdb2..a66aef75 100644 --- a/spatial/include/spatial/gdal/functions.hpp +++ b/spatial/include/spatial/gdal/functions.hpp @@ -17,6 +17,8 @@ struct GdalTableFunction : ArrowTableFunction { static void RenameColumns(vector &names); static unique_ptr InitGlobal(ClientContext &context, TableFunctionInitInput &input); + static unique_ptr InitLocal(ExecutionContext &context, TableFunctionInitInput &input, + GlobalTableFunctionState *global_state_p); static void Scan(ClientContext &context, TableFunctionInput &input, DataChunk &output); diff --git a/spatial/src/spatial/gdal/CMakeLists.txt b/spatial/src/spatial/gdal/CMakeLists.txt index 558a4cd6..8dce5a4f 100644 --- a/spatial/src/spatial/gdal/CMakeLists.txt +++ b/spatial/src/spatial/gdal/CMakeLists.txt @@ -2,5 +2,6 @@ add_subdirectory(functions) set(EXTENSION_SOURCES ${EXTENSION_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/module.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/file_handler.cpp PARENT_SCOPE ) \ No newline at end of file diff --git a/spatial/src/spatial/gdal/file_handler.cpp b/spatial/src/spatial/gdal/file_handler.cpp new file mode 100644 index 00000000..b7a1efbd --- /dev/null +++ b/spatial/src/spatial/gdal/file_handler.cpp @@ -0,0 +1,200 @@ +#include "spatial/gdal/file_handler.hpp" + +#include "cpl_vsi.h" +#include "cpl_string.h" + +namespace spatial { + +namespace gdal { + +//-------------------------------------------------------------------------- +// Local Client Context +//-------------------------------------------------------------------------- + +static thread_local ClientContext *local_context = nullptr; + +void GdalFileHandler::SetLocalClientContext(ClientContext &context) { + local_context = &context; +} + +ClientContext &GdalFileHandler::GetLocalClientContext() { + if (!local_context) { + throw InternalException("No local client context set"); + } + return *local_context; +} + +//-------------------------------------------------------------------------- +// Required Callbacks +//-------------------------------------------------------------------------- + +static void *DuckDBOpen(void *, const char *file_name, const char *access) { + auto &context = GdalFileHandler::GetLocalClientContext(); + auto &fs = context.db->GetFileSystem(); + + // TODO: Double check that this is correct + uint8_t flags; + auto len = strlen(access); + if (access[0] == 'r') { + flags = FileFlags::FILE_FLAGS_READ; + if (len > 1 && access[1] == '+') { + flags |= FileFlags::FILE_FLAGS_WRITE; + } + } else if (access[0] == 'w') { + flags = FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_FILE_CREATE_NEW; + if (len > 1 && access[1] == '+') { + flags |= FileFlags::FILE_FLAGS_READ; + } + } else if (access[0] == 'a') { + flags = FileFlags::FILE_FLAGS_APPEND; + if (len > 1 && access[1] == '+') { + flags |= FileFlags::FILE_FLAGS_READ; + } + } else { + throw InternalException("Unknown file access type"); + } + + try { + auto file = fs.OpenFile(file_name, flags); + return file.release(); + } catch (std::exception &ex) { + return nullptr; + } +} + +static vsi_l_offset DuckDBTell(void *file) { + auto file_handle = static_cast(file); + auto offset = file_handle->SeekPosition(); + return static_cast(offset); +} + +static int DuckDBSeek(void *file, vsi_l_offset offset, int whence) { + auto file_handle = static_cast(file); + switch (whence) { + case SEEK_SET: + file_handle->Seek(offset); + break; + case SEEK_CUR: + file_handle->Seek(file_handle->SeekPosition() + offset); + break; + case SEEK_END: + file_handle->Seek(file_handle->GetFileSize() + offset); + break; + default: + throw InternalException("Unknown seek type"); + } + return 0; +} + +static size_t DuckDBRead(void *pFile, void *pBuffer, size_t n_size, size_t n_count) { + auto file_handle = static_cast(pFile); + auto read_bytes = file_handle->Read(pBuffer, n_size * n_count); + // Return the number of items read + return static_cast(read_bytes / n_size); +} + +static size_t DuckDBWrite(void *file, const void *buffer, size_t n_size, size_t n_count) { + auto file_handle = static_cast(file); + auto written_bytes = file_handle->Write(const_cast(buffer), n_size * n_count); + // Return the number of items written + return static_cast(written_bytes / n_size); +} + +static int DuckDBEoF(void *file) { + // TODO: Is this correct? + auto file_handle = static_cast(file); + return file_handle->SeekPosition() == file_handle->GetFileSize() ? TRUE : FALSE; +} + +static int DuckDBTruncate(void *file, vsi_l_offset size) { + auto file_handle = static_cast(file); + file_handle->Truncate(static_cast(size)); + return 0; +} + +static int DuckDBClose(void *file) { + auto file_handle = static_cast(file); + file_handle->Close(); + delete file_handle; + return 0; +} + +static int DuckDBFlush(void *file) { + auto file_handle = static_cast(file); + file_handle->Sync(); + return 0; +} + +static int DuckDBMakeDir(void *, const char *dir_name, long mode) { + auto &context = GdalFileHandler::GetLocalClientContext(); + auto &fs = context.db->GetFileSystem(); + + fs.CreateDirectory(dir_name); + return 0; +} + +static int DuckDBDeleteDir(void *, const char *dir_name) { + auto &context = GdalFileHandler::GetLocalClientContext(); + auto &fs = context.db->GetFileSystem(); + + fs.RemoveDirectory(dir_name); + return 0; +} + +static char **DuckDBReadDir(void *, const char *dir_name, int max_files) { + auto &context = GdalFileHandler::GetLocalClientContext(); + auto &fs = context.db->GetFileSystem(); + + CPLStringList files; + auto files_count = 0; + fs.ListFiles(dir_name, [&](const string &file_name, bool is_dir) { + if (files_count >= max_files) { + return; + } + files.AddString(file_name.c_str()); + files_count++; + }); + return files.StealList(); +} + +static char **DuckDBSiblingFiles(void *, const char *dir_name) { + auto &context = GdalFileHandler::GetLocalClientContext(); + auto &fs = context.db->GetFileSystem(); + + CPLStringList files; + auto file_vector = fs.Glob(dir_name); + for (auto &file : file_vector) { + files.AddString(file.c_str()); + } + return files.StealList(); +} + +//-------------------------------------------------------------------------- +// Register +//-------------------------------------------------------------------------- +void GdalFileHandler::Register(ClientContext &context) { + + auto callbacks = VSIAllocFilesystemPluginCallbacksStruct(); + + callbacks->nCacheSize = 16384000; // same as /vsicurl/ + callbacks->open = DuckDBOpen; + callbacks->read = DuckDBRead; + callbacks->write = DuckDBWrite; + callbacks->close = DuckDBClose; + callbacks->tell = DuckDBTell; + callbacks->seek = DuckDBSeek; + callbacks->eof = DuckDBEoF; + callbacks->flush = DuckDBFlush; + callbacks->truncate = DuckDBTruncate; + callbacks->mkdir = DuckDBMakeDir; + callbacks->rmdir = DuckDBDeleteDir; + callbacks->read_dir = DuckDBReadDir; + callbacks->sibling_files = DuckDBSiblingFiles; + + // Override this as the default file system + VSIInstallPluginHandler("", callbacks); +} + +} // namespace gdal + +} // namespace spatial diff --git a/spatial/src/spatial/gdal/functions/st_read.cpp b/spatial/src/spatial/gdal/functions/st_read.cpp index 37f656bb..4d4f5f73 100644 --- a/spatial/src/spatial/gdal/functions/st_read.cpp +++ b/spatial/src/spatial/gdal/functions/st_read.cpp @@ -11,6 +11,7 @@ #include "spatial/common.hpp" #include "spatial/core/types.hpp" #include "spatial/gdal/functions.hpp" +#include "spatial/gdal/file_handler.hpp" #include "ogrsf_frmts.h" @@ -134,6 +135,9 @@ struct ScopedOption { } }; +//------------------------------------------------------------------------------ +// Bind +//------------------------------------------------------------------------------ unique_ptr GdalTableFunction::Bind(ClientContext &context, TableFunctionBindInput &input, vector &return_types, vector &names) { @@ -142,6 +146,9 @@ unique_ptr GdalTableFunction::Bind(ClientContext &context, TableFu throw PermissionException("Scanning GDAL files is disabled through configuration"); } + // Set the local client context so that we can access it from the filesystem handler + GdalFileHandler::SetLocalClientContext(context); + // First scan for "options" parameter auto gdal_open_options = vector(); auto options_param = input.named_parameters.find("open_options"); @@ -453,7 +460,9 @@ OGRLayer *open_layer(const GdalScanFunctionData &data) { return layer; } -// init global +//----------------------------------------------------------------------------- +// Init global +//----------------------------------------------------------------------------- unique_ptr GdalTableFunction::InitGlobal(ClientContext &context, TableFunctionInitInput &input) { auto &data = input.bind_data->Cast(); @@ -500,7 +509,19 @@ unique_ptr GdalTableFunction::InitGlobal(ClientContext return std::move(global_state); } +//----------------------------------------------------------------------------- +// Init Local +//----------------------------------------------------------------------------- +unique_ptr GdalTableFunction::InitLocal(ExecutionContext &context, + TableFunctionInitInput &input, + GlobalTableFunctionState *global_state_p) { + GdalFileHandler::SetLocalClientContext(context.client); + return ArrowTableFunction::ArrowScanInitLocal(context, input, global_state_p); +} + +//----------------------------------------------------------------------------- // Scan +//----------------------------------------------------------------------------- void GdalTableFunction::Scan(ClientContext &context, TableFunctionInput &input, DataChunk &output) { if (!input.local_state) { return; @@ -578,7 +599,7 @@ void GdalTableFunction::Register(ClientContext &context) { TableFunctionSet set("st_read"); TableFunction scan({LogicalType::VARCHAR}, GdalTableFunction::Scan, GdalTableFunction::Bind, - GdalTableFunction::InitGlobal, ArrowTableFunction::ArrowScanInitLocal); + GdalTableFunction::InitGlobal, GdalTableFunction::InitLocal); scan.cardinality = GdalTableFunction::Cardinality; scan.get_batch_index = ArrowTableFunction::ArrowGetBatchIndex; diff --git a/spatial/src/spatial/gdal/functions/st_write.cpp b/spatial/src/spatial/gdal/functions/st_write.cpp index 2d373006..01e40cf9 100644 --- a/spatial/src/spatial/gdal/functions/st_write.cpp +++ b/spatial/src/spatial/gdal/functions/st_write.cpp @@ -9,8 +9,8 @@ #include "duckdb/parser/parsed_data/create_table_function_info.hpp" #include "spatial/core/types.hpp" #include "spatial/core/geometry/geometry_factory.hpp" -#include "spatial/core/geometry/wkb_writer.hpp" #include "spatial/gdal/functions.hpp" +#include "spatial/gdal/file_handler.hpp" #include "ogrsf_frmts.h" @@ -115,6 +115,7 @@ static unique_ptr Bind(ClientContext &context, CopyInfo &info, vec // Init Local //===--------------------------------------------------------------------===// static unique_ptr InitLocal(ExecutionContext &context, FunctionData &bind_data) { + GdalFileHandler::SetLocalClientContext(context.client); auto local_data = make_uniq(context.client); return std::move(local_data); } @@ -208,9 +209,9 @@ static unique_ptr OGRFieldTypeFromLogicalType(const string &name, } static unique_ptr InitGlobal(ClientContext &context, FunctionData &bind_data, const string &file_path) { - // auto gdal_data = (BindData&)bind_data; - // auto global_data = make_uniq(file_path, "FlatGeobuf"); - // return std::move(global_data); + + // Set the local client context so that we can access it from the filesystem handler + GdalFileHandler::SetLocalClientContext(context); auto &gdal_data = (BindData &)bind_data; GDALDriver *driver = GetGDALDriverManager()->GetDriverByName(gdal_data.driver_name.c_str()); diff --git a/spatial/src/spatial/gdal/module.cpp b/spatial/src/spatial/gdal/module.cpp index 752d9802..11de810d 100644 --- a/spatial/src/spatial/gdal/module.cpp +++ b/spatial/src/spatial/gdal/module.cpp @@ -1,18 +1,52 @@ #include "spatial/gdal/module.hpp" #include "spatial/gdal/functions.hpp" - +#include "spatial/gdal/file_handler.hpp" #include "spatial/common.hpp" #include "ogrsf_frmts.h" +#include + namespace spatial { namespace gdal { void GdalModule::Register(ClientContext &context) { - // Load GDAL - OGRRegisterAll(); + // Load GDAL (once) + static std::once_flag loaded; + std::call_once(loaded, [&]() { + // Register all embedded drivers (dont go looking for plugins) + OGRRegisterAllInternal(); + + // Set GDAL error handler + CPLSetErrorHandler([](CPLErr e, int code, const char *msg) { + switch (code) { + case CPLE_NoWriteAccess: + throw PermissionException("GDAL Error (%d): %s", code, msg); + case CPLE_UserInterrupt: + throw InterruptException(); + case CPLE_OutOfMemory: + throw OutOfMemoryException("GDAL Error (%d): %s", code, msg); + case CPLE_NotSupported: + throw NotImplementedException("GDAL Error (%d): %s", code, msg); + case CPLE_AssertionFailed: + case CPLE_ObjectNull: + throw InternalException("GDAL Error (%d): %s", code, msg); + case CPLE_IllegalArg: + throw InvalidInputException("GDAL Error (%d): %s", code, msg); + case CPLE_AppDefined: + case CPLE_HttpResponse: + case CPLE_FileIO: + case CPLE_OpenFailed: + default: + throw IOException("GDAL Error (%d): %s", code, msg); + } + }); + + // Install the duckdb file handler + GdalFileHandler::Register(context); + }); // Register functions GdalTableFunction::Register(context); From d90eadcaf5885147472a97f481b3261bc3bf0815 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Mon, 11 Sep 2023 18:08:23 +0200 Subject: [PATCH 2/2] update duckdb, add geos aggregates --- duckdb | 2 +- .../spatial/core/functions/aggregate.hpp | 4 +- .../include/spatial/geos/geos_wrappers.hpp | 3 + spatial/src/spatial/core/module.cpp | 2 +- .../src/spatial/geos/functions/aggregate.cpp | 185 ++++++++++++++++++ spatial/src/spatial/geos/geos_wrappers.cpp | 22 ++- 6 files changed, 208 insertions(+), 10 deletions(-) diff --git a/duckdb b/duckdb index a8ce02cc..9db510bd 160000 --- a/duckdb +++ b/duckdb @@ -1 +1 @@ -Subproject commit a8ce02cc2e740d8973d26ccdb77d0068c69c9124 +Subproject commit 9db510bd1105f883747baca317a9b63adedf0d8e diff --git a/spatial/include/spatial/core/functions/aggregate.hpp b/spatial/include/spatial/core/functions/aggregate.hpp index bafc3573..3e90763a 100644 --- a/spatial/include/spatial/core/functions/aggregate.hpp +++ b/spatial/include/spatial/core/functions/aggregate.hpp @@ -6,7 +6,9 @@ namespace spatial { namespace core { struct CoreAggregateFunctions { - static void Register(ClientContext &context); +public: + static void Register(ClientContext &context) { + } }; } // namespace core diff --git a/spatial/include/spatial/geos/geos_wrappers.hpp b/spatial/include/spatial/geos/geos_wrappers.hpp index 9f651d9d..80bd1954 100644 --- a/spatial/include/spatial/geos/geos_wrappers.hpp +++ b/spatial/include/spatial/geos/geos_wrappers.hpp @@ -206,6 +206,9 @@ struct GeosContextWrapper { string_t Serialize(Vector &result, const unique_ptr> &geom); }; +GEOSGeometry *DeserializeGEOSGeometry(const string_t &blob, GEOSContextHandle_t ctx); +string_t SerializeGEOSGeometry(Vector &result, const GEOSGeometry *geom, GEOSContextHandle_t ctx); + } // namespace geos } // namespace spatial \ No newline at end of file diff --git a/spatial/src/spatial/core/module.cpp b/spatial/src/spatial/core/module.cpp index 35ba203d..db0014ef 100644 --- a/spatial/src/spatial/core/module.cpp +++ b/spatial/src/spatial/core/module.cpp @@ -18,7 +18,7 @@ void CoreModule::Register(ClientContext &context) { CoreScalarFunctions::Register(context); CoreCastFunctions::Register(context); CoreTableFunctions::Register(context); - // CoreAggregateFunctions::Register(context); + CoreAggregateFunctions::Register(context); } } // namespace core diff --git a/spatial/src/spatial/geos/functions/aggregate.cpp b/spatial/src/spatial/geos/functions/aggregate.cpp index 7c83f92f..2ab0797f 100644 --- a/spatial/src/spatial/geos/functions/aggregate.cpp +++ b/spatial/src/spatial/geos/functions/aggregate.cpp @@ -1,12 +1,197 @@ +#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" +#include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp" #include "spatial/common.hpp" #include "spatial/geos/functions/aggregate.hpp" +#include "spatial/geos/geos_wrappers.hpp" + +#include "geos_c.h" namespace spatial { namespace geos { +struct GEOSAggState { + GEOSGeometry *geom = nullptr; + GEOSContextHandle_t context = nullptr; + + ~GEOSAggState() { + if (geom) { + GEOSGeom_destroy_r(context, geom); + geom = nullptr; + } + if (context) { + GEOS_finish_r(context); + context = nullptr; + } + } +}; + +//------------------------------------------------------------------------ +// INTERSECTION +//------------------------------------------------------------------------ +struct IntersectionAggFunction { + template + static void Initialize(STATE &state) { + state.geom = nullptr; + state.context = GEOS_init_r(); + } + + template + static void Combine(const STATE &source, STATE &target, AggregateInputData &data) { + if (!source.geom) { + return; + } + if (!target.geom) { + target.geom = GEOSGeom_clone_r(target.context, source.geom); + return; + } + auto curr = target.geom; + target.geom = GEOSIntersection_r(target.context, curr, source.geom); + GEOSGeom_destroy_r(target.context, curr); + } + + template + static void Operation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &) { + if (!state.geom) { + state.geom = DeserializeGEOSGeometry(input, state.context); + } else { + auto next = DeserializeGEOSGeometry(input, state.context); + auto curr = state.geom; + state.geom = GEOSIntersection_r(state.context, curr, next); + GEOSGeom_destroy_r(state.context, next); + GEOSGeom_destroy_r(state.context, curr); + } + } + + template + static void ConstantOperation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &, idx_t count) { + // There is no point in doing anything else, intersection is idempotent + if (!state.geom) { + state.geom = DeserializeGEOSGeometry(input, state.context); + } + } + + template + static void Finalize(STATE &state, T &target, AggregateFinalizeData &finalize_data) { + if (!state.geom) { + finalize_data.ReturnNull(); + } else { + target = SerializeGEOSGeometry(finalize_data.result, state.geom, state.context); + } + } + + template + static void Destroy(STATE &state, AggregateInputData &) { + if (state.geom) { + GEOSGeom_destroy_r(state.context, state.geom); + state.geom = nullptr; + } + if (state.context) { + GEOS_finish_r(state.context); + state.context = nullptr; + } + } + + static bool IgnoreNull() { + return true; + } +}; + +//------------------------------------------------------------------------ +// UNION +//------------------------------------------------------------------------ + +struct UnionAggFunction { + template + static void Initialize(STATE &state) { + state.geom = nullptr; + state.context = GEOS_init_r(); + } + + template + static void Combine(const STATE &source, STATE &target, AggregateInputData &data) { + if (!source.geom) { + return; + } + if (!target.geom) { + target.geom = GEOSGeom_clone_r(target.context, source.geom); + return; + } + auto curr = target.geom; + target.geom = GEOSUnion_r(target.context, curr, source.geom); + GEOSGeom_destroy_r(target.context, curr); + } + + template + static void Operation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &) { + if (!state.geom) { + state.geom = DeserializeGEOSGeometry(input, state.context); + } else { + auto next = DeserializeGEOSGeometry(input, state.context); + auto curr = state.geom; + state.geom = GEOSUnion_r(state.context, curr, next); + GEOSGeom_destroy_r(state.context, next); + GEOSGeom_destroy_r(state.context, curr); + } + } + + template + static void ConstantOperation(STATE &state, const INPUT_TYPE &input, AggregateUnaryInput &, idx_t count) { + // There is no point in doing anything else, union is idempotent + if (!state.geom) { + state.geom = DeserializeGEOSGeometry(input, state.context); + } + } + + template + static void Finalize(STATE &state, T &target, AggregateFinalizeData &finalize_data) { + if (!state.geom) { + finalize_data.ReturnNull(); + } else { + target = SerializeGEOSGeometry(finalize_data.result, state.geom, state.context); + } + } + + template + static void Destroy(STATE &state, AggregateInputData &) { + if (state.geom) { + GEOSGeom_destroy_r(state.context, state.geom); + state.geom = nullptr; + } + if (state.context) { + GEOS_finish_r(state.context); + state.context = nullptr; + } + } + + static bool IgnoreNull() { + return true; + } +}; + +//------------------------------------------------------------------------ +// Register +//------------------------------------------------------------------------ void GeosAggregateFunctions::Register(ClientContext &context) { + + auto &catalog = Catalog::GetSystemCatalog(context); + + AggregateFunctionSet st_intersection_agg("st_intersection_agg"); + st_intersection_agg.AddFunction( + AggregateFunction::UnaryAggregateDestructor( + core::GeoTypes::GEOMETRY(), core::GeoTypes::GEOMETRY())); + CreateAggregateFunctionInfo intersection_info(std::move(st_intersection_agg)); + intersection_info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(context, intersection_info); + + AggregateFunctionSet st_union_agg("st_union_agg"); + st_union_agg.AddFunction( + AggregateFunction::UnaryAggregateDestructor( + core::GeoTypes::GEOMETRY(), core::GeoTypes::GEOMETRY())); + CreateAggregateFunctionInfo union_info(std::move(st_union_agg)); + union_info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(context, union_info); } } // namespace geos diff --git a/spatial/src/spatial/geos/geos_wrappers.cpp b/spatial/src/spatial/geos/geos_wrappers.cpp index cc5375bf..9f09273d 100644 --- a/spatial/src/spatial/geos/geos_wrappers.cpp +++ b/spatial/src/spatial/geos/geos_wrappers.cpp @@ -143,7 +143,7 @@ static GEOSGeometry *DeserializeGeometryCollection(Cursor &reader, GEOSContextHa } } -static GEOSGeometry *DeserializeGeometry(Cursor &reader, GEOSContextHandle_t ctx) { +GEOSGeometry *DeserializeGeometry(Cursor &reader, GEOSContextHandle_t ctx) { auto type = reader.Peek(); switch (type) { case GeometryType::POINT: { @@ -174,11 +174,15 @@ static GEOSGeometry *DeserializeGeometry(Cursor &reader, GEOSContextHandle_t ctx } } -GeometryPtr GeosContextWrapper::Deserialize(const string_t &blob) { +GEOSGeometry *DeserializeGEOSGeometry(const string_t &blob, GEOSContextHandle_t ctx) { Cursor reader(blob); reader.Skip(4); // Skip type, flags and hash reader.Skip(4); // Skip padding - return GeometryPtr(DeserializeGeometry(reader, ctx)); + return DeserializeGeometry(reader, ctx); +} + +GeometryPtr GeosContextWrapper::Deserialize(const string_t &blob) { + return GeometryPtr(DeserializeGEOSGeometry(blob, ctx)); } //------------------------------------------------------------------- @@ -451,8 +455,8 @@ static void SerializeGeometry(Cursor &writer, const GEOSGeometry *geom, const GE } } -string_t GeosContextWrapper::Serialize(Vector &result, const GeometryPtr &geom) { - auto size = GetSerializedSize(geom.get(), ctx); +string_t SerializeGEOSGeometry(Vector &result, const GEOSGeometry *geom, GEOSContextHandle_t ctx) { + auto size = GetSerializedSize(geom, ctx); size += sizeof(GeometryHeader); // Header size += sizeof(uint32_t); // Padding @@ -465,7 +469,7 @@ string_t GeosContextWrapper::Serialize(Vector &result, const GeometryPtr &geom) } GeometryType type; - auto geos_type = GEOSGeomTypeId_r(ctx, geom.get()); + auto geos_type = GEOSGeomTypeId_r(ctx, geom); switch (geos_type) { case GEOS_POINT: type = GeometryType::POINT; @@ -501,11 +505,15 @@ string_t GeosContextWrapper::Serialize(Vector &result, const GeometryPtr &geom) writer.Write(header); // Header writer.Write(0); // Padding - SerializeGeometry(writer, geom.get(), ctx); + SerializeGeometry(writer, geom, ctx); return blob; } +string_t GeosContextWrapper::Serialize(Vector &result, const GeometryPtr &geom) { + return SerializeGEOSGeometry(result, geom.get(), ctx); +} + } // namespace geos } // namespace spatial \ No newline at end of file