diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e8179d..16c3c50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,12 +24,13 @@ set(GEOARROW_GEOS_VERSION_PATCH "${GEOARROW_GEOS_VERSION_PATCH}") option(GEOARROW_GEOS_BUILD_TESTS "Build tests" OFF) -# Ensure geoarrow_c +# Ensure geoarrow_c with namespace +set(GEOARROW_NAMESPACE GeoArrowGEOS) FetchContent_Declare( geoarrow_c - URL https://github.com/geoarrow/geoarrow-c/archive/898af28230cf11c4804dae64090993c8c3173bd9.zip + URL https://github.com/geoarrow/geoarrow-c/archive/22794ce83fae1e2e99511508fa936c1e4cb115cb.zip URL_HASH - SHA256=bbac8132f64a59db88983347a0337e8a37a8d50f35af248075edc59ce934547b) + SHA256=3631aa2c0883a76d18bfa56395f780f8ff6eec49b2fad21c24f72bb3e15ceed7) FetchContent_MakeAvailable(geoarrow_c) diff --git a/src/geoarrow_geos/geoarrow_geos.c b/src/geoarrow_geos/geoarrow_geos.c index a8e595e..12710b7 100644 --- a/src/geoarrow_geos/geoarrow_geos.c +++ b/src/geoarrow_geos/geoarrow_geos.c @@ -61,40 +61,6 @@ GeoArrowGEOSErrorCode GeoArrowGEOSArrayBuilderCreate( return GEOARROW_OK; } -GeoArrowGEOSErrorCode GeoArrowGEOSMakeSchema(int32_t encoding, int32_t wkb_type, - struct ArrowSchema* out) { - enum GeoArrowType type = GEOARROW_TYPE_UNINITIALIZED; - enum GeoArrowGeometryType geometry_type = GEOARROW_GEOMETRY_TYPE_GEOMETRY; - enum GeoArrowDimensions dimensions = GEOARROW_DIMENSIONS_UNKNOWN; - enum GeoArrowCoordType coord_type = GEOARROW_COORD_TYPE_UNKNOWN; - - switch (encoding) { - case GEOARROW_GEOS_ENCODING_WKT: - type = GEOARROW_TYPE_WKT; - break; - case GEOARROW_GEOS_ENCODING_WKB: - type = GEOARROW_TYPE_WKB; - break; - case GEOARROW_GEOS_ENCODING_GEOARROW: - coord_type = GEOARROW_COORD_TYPE_SEPARATE; - break; - case GEOARROW_GEOS_ENCODING_GEOARROW_INTERLEAVED: - coord_type = GEOARROW_COORD_TYPE_INTERLEAVED; - break; - default: - return EINVAL; - } - - if (type == GEOARROW_TYPE_UNINITIALIZED) { - geometry_type = wkb_type % 1000; - dimensions = wkb_type / 1000 + 1; - type = GeoArrowMakeType(geometry_type, dimensions, GEOARROW_COORD_TYPE_SEPARATE); - } - - GEOARROW_RETURN_NOT_OK(GeoArrowSchemaInitExtension(out, type)); - return GEOARROW_OK; -} - static GeoArrowErrorCode GeoArrowGEOSArrayBuilderEnsureCoords( struct GeoArrowGEOSArrayBuilder* builder, uint32_t n_coords, int n_dims) { int64_t n_required = n_coords * n_dims; @@ -891,3 +857,244 @@ void GeoArrowGEOSArrayReaderDestroy(struct GeoArrowGEOSArrayReader* reader) { free(reader); } + +struct GeoArrowGEOSSchemaCalculator { + int geometry_type; + int dimensions; +}; + +GeoArrowGEOSErrorCode GeoArrowGEOSSchemaCalculatorCreate( + struct GeoArrowGEOSSchemaCalculator** out) { + struct GeoArrowGEOSSchemaCalculator* calc = + (struct GeoArrowGEOSSchemaCalculator*)malloc( + sizeof(struct GeoArrowGEOSSchemaCalculator)); + if (calc == NULL) { + *out = NULL; + return ENOMEM; + } + + calc->geometry_type = -1; + calc->dimensions = GEOARROW_DIMENSIONS_UNKNOWN; + *out = calc; + + return GEOARROW_OK; +} + +static int GeometryType2(int x, int y) { + switch (x) { + case -1: + return y; + case GEOARROW_GEOMETRY_TYPE_GEOMETRY: + return x; + case GEOARROW_GEOMETRY_TYPE_POINT: + switch (y) { + case -1: + return x; + case GEOARROW_TYPE_POINT: + case GEOARROW_TYPE_MULTIPOINT: + return y; + default: + return GEOARROW_GEOMETRY_TYPE_GEOMETRY; + } + case GEOARROW_GEOMETRY_TYPE_LINESTRING: + switch (y) { + case -1: + return x; + case GEOARROW_TYPE_LINESTRING: + case GEOARROW_TYPE_MULTILINESTRING: + return y; + default: + return GEOARROW_GEOMETRY_TYPE_GEOMETRY; + } + case GEOARROW_GEOMETRY_TYPE_POLYGON: + switch (y) { + case -1: + return x; + case GEOARROW_TYPE_POLYGON: + case GEOARROW_TYPE_MULTIPOLYGON: + return y; + default: + return GEOARROW_GEOMETRY_TYPE_GEOMETRY; + } + case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: + switch (y) { + case -1: + return x; + case GEOARROW_TYPE_POINT: + case GEOARROW_TYPE_MULTIPOINT: + return x; + default: + return GEOARROW_GEOMETRY_TYPE_GEOMETRY; + } + case GEOARROW_GEOMETRY_TYPE_MULTILINESTRING: + switch (y) { + case -1: + return x; + case GEOARROW_TYPE_LINESTRING: + case GEOARROW_TYPE_MULTILINESTRING: + return x; + default: + return GEOARROW_GEOMETRY_TYPE_GEOMETRY; + } + case GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON: + switch (y) { + case -1: + return x; + case GEOARROW_TYPE_POLYGON: + case GEOARROW_TYPE_MULTIPOLYGON: + return x; + default: + return GEOARROW_GEOMETRY_TYPE_GEOMETRY; + } + case GEOARROW_GEOMETRY_TYPE_GEOMETRYCOLLECTION: + switch (y) { + case -1: + return x; + case GEOARROW_GEOMETRY_TYPE_GEOMETRYCOLLECTION: + return x; + default: + return GEOARROW_GEOMETRY_TYPE_GEOMETRY; + } + default: + return GEOARROW_GEOMETRY_TYPE_GEOMETRY; + } +} + +static int Dimensions2(int x, int y) { + switch (x) { + case GEOARROW_DIMENSIONS_UNKNOWN: + return y; + case GEOARROW_DIMENSIONS_XY: + switch (y) { + case GEOARROW_DIMENSIONS_UNKNOWN: + return x; + default: + return y; + } + case GEOARROW_DIMENSIONS_XYZ: + switch (y) { + case GEOARROW_DIMENSIONS_UNKNOWN: + return x; + case GEOARROW_DIMENSIONS_XYM: + return GEOARROW_DIMENSIONS_XYZM; + default: + return y; + } + case GEOARROW_DIMENSIONS_XYM: + switch (y) { + case GEOARROW_DIMENSIONS_UNKNOWN: + return x; + case GEOARROW_DIMENSIONS_XYZ: + return GEOARROW_DIMENSIONS_XYZM; + default: + return y; + } + default: + return GEOARROW_DIMENSIONS_XYZM; + } +} + +void GeoArrowGEOSSchemaCalculatorIngest(struct GeoArrowGEOSSchemaCalculator* calc, + const int32_t* wkb_type, size_t n) { + for (size_t i = 0; i < n; i++) { + if (wkb_type[i] == 0) { + continue; + } + + calc->geometry_type = GeometryType2(calc->geometry_type, wkb_type[i] % 1000); + calc->dimensions = Dimensions2(calc->dimensions, wkb_type[i] / 1000); + } +} + +GeoArrowGEOSErrorCode GeoArrowGEOSSchemaCalculatorFinish( + struct GeoArrowGEOSSchemaCalculator* calc, enum GeoArrowGEOSEncoding encoding, + struct ArrowSchema* out) { + enum GeoArrowCoordType coord_type; + switch (encoding) { + case GEOARROW_GEOS_ENCODING_WKT: + case GEOARROW_GEOS_ENCODING_WKB: + return GeoArrowGEOSMakeSchema(encoding, 0, out); + case GEOARROW_GEOS_ENCODING_GEOARROW: + coord_type = GEOARROW_COORD_TYPE_INTERLEAVED; + break; + case GEOARROW_GEOS_ENCODING_GEOARROW_INTERLEAVED: + coord_type = GEOARROW_COORD_TYPE_INTERLEAVED; + break; + default: + return EINVAL; + } + + enum GeoArrowGeometryType geometry_type; + switch (calc->geometry_type) { + case GEOARROW_GEOMETRY_TYPE_POINT: + case GEOARROW_GEOMETRY_TYPE_LINESTRING: + case GEOARROW_GEOMETRY_TYPE_POLYGON: + case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: + case GEOARROW_GEOMETRY_TYPE_MULTILINESTRING: + case GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON: + geometry_type = (enum GeoArrowGeometryType)calc->geometry_type; + break; + case -1: + // We don't have an "empty"/"null" type to return, but "POINT" is also + // not quite right. + default: + return GeoArrowGEOSMakeSchema(GEOARROW_GEOS_ENCODING_WKB, 0, out); + } + + enum GeoArrowDimensions dimensions; + switch (calc->dimensions) { + case GEOARROW_DIMENSIONS_UNKNOWN: + dimensions = GEOARROW_DIMENSIONS_XY; + break; + case GEOARROW_DIMENSIONS_XY: + case GEOARROW_DIMENSIONS_XYZ: + case GEOARROW_DIMENSIONS_XYM: + case GEOARROW_DIMENSIONS_XYZM: + dimensions = (enum GeoArrowDimensions)calc->dimensions; + break; + default: + return GeoArrowGEOSMakeSchema(GEOARROW_GEOS_ENCODING_WKB, 0, out); + } + + enum GeoArrowType type = GeoArrowMakeType(geometry_type, dimensions, coord_type); + GEOARROW_RETURN_NOT_OK(GeoArrowSchemaInitExtension(out, type)); + return GEOARROW_OK; +} + +void GeoArrowGEOSSchemaCalculatorDestroy(struct GeoArrowGEOSSchemaCalculator* calc) { + free(calc); +} + +GeoArrowGEOSErrorCode GeoArrowGEOSMakeSchema(int32_t encoding, int32_t wkb_type, + struct ArrowSchema* out) { + enum GeoArrowType type = GEOARROW_TYPE_UNINITIALIZED; + enum GeoArrowGeometryType geometry_type = GEOARROW_GEOMETRY_TYPE_GEOMETRY; + enum GeoArrowDimensions dimensions = GEOARROW_DIMENSIONS_UNKNOWN; + enum GeoArrowCoordType coord_type = GEOARROW_COORD_TYPE_UNKNOWN; + + switch (encoding) { + case GEOARROW_GEOS_ENCODING_WKT: + type = GEOARROW_TYPE_WKT; + break; + case GEOARROW_GEOS_ENCODING_WKB: + type = GEOARROW_TYPE_WKB; + break; + case GEOARROW_GEOS_ENCODING_GEOARROW: + coord_type = GEOARROW_COORD_TYPE_SEPARATE; + break; + case GEOARROW_GEOS_ENCODING_GEOARROW_INTERLEAVED: + coord_type = GEOARROW_COORD_TYPE_INTERLEAVED; + break; + default: + return EINVAL; + } + + if (type == GEOARROW_TYPE_UNINITIALIZED) { + geometry_type = wkb_type % 1000; + dimensions = wkb_type / 1000 + 1; + type = GeoArrowMakeType(geometry_type, dimensions, coord_type); + } + + GEOARROW_RETURN_NOT_OK(GeoArrowSchemaInitExtension(out, type)); + return GEOARROW_OK; +} diff --git a/src/geoarrow_geos/geoarrow_geos.h b/src/geoarrow_geos/geoarrow_geos.h index 4a0cf39..35a36c5 100644 --- a/src/geoarrow_geos/geoarrow_geos.h +++ b/src/geoarrow_geos/geoarrow_geos.h @@ -74,9 +74,6 @@ const char* GeoArrowGEOSVersionGeoArrow(void); struct GeoArrowGEOSArrayBuilder; -GeoArrowGEOSErrorCode GeoArrowGEOSMakeSchema(int32_t encoding, int32_t wkb_type, - struct ArrowSchema* out); - GeoArrowGEOSErrorCode GeoArrowGEOSArrayBuilderCreate( GEOSContextHandle_t handle, struct ArrowSchema* schema, struct GeoArrowGEOSArrayBuilder** out); @@ -108,6 +105,70 @@ GeoArrowGEOSErrorCode GeoArrowGEOSArrayReaderRead(struct GeoArrowGEOSArrayReader void GeoArrowGEOSArrayReaderDestroy(struct GeoArrowGEOSArrayReader* reader); +struct GeoArrowGEOSSchemaCalculator; + +GeoArrowGEOSErrorCode GeoArrowGEOSSchemaCalculatorCreate( + struct GeoArrowGEOSSchemaCalculator** out); + +void GeoArrowGEOSSchemaCalculatorIngest(struct GeoArrowGEOSSchemaCalculator* calc, + const int32_t* wkb_type, size_t n); + +GeoArrowGEOSErrorCode GeoArrowGEOSSchemaCalculatorFinish( + struct GeoArrowGEOSSchemaCalculator* calc, enum GeoArrowGEOSEncoding encoding, + struct ArrowSchema* out); + +void GeoArrowGEOSSchemaCalculatorDestroy(struct GeoArrowGEOSSchemaCalculator* calc); + +GeoArrowGEOSErrorCode GeoArrowGEOSMakeSchema(int32_t encoding, int32_t wkb_type, + struct ArrowSchema* out); + +static inline int32_t GeoArrowGEOSWKBType(GEOSContextHandle_t handle, + const GEOSGeometry* geom) { + if (geom == NULL || GEOSGetNumCoordinates_r(handle, geom) == 0) { + return 0; + } + + int n_dim = GEOSGeom_getCoordinateDimension_r(handle, geom); + + // Not sure how GEOS handles M in newer versions + int32_t wkb_type; + if (n_dim == 3) { + wkb_type = 2000; + } else { + wkb_type = 0; + } + + int type_id = GEOSGeomTypeId_r(handle, geom); + switch (type_id) { + case GEOS_POINT: + wkb_type += 1; + break; + case GEOS_LINEARRING: + case GEOS_LINESTRING: + wkb_type += 2; + break; + case GEOS_POLYGON: + wkb_type += 3; + break; + case GEOS_MULTIPOINT: + wkb_type += 4; + break; + case GEOS_MULTILINESTRING: + wkb_type += 5; + break; + case GEOS_MULTIPOLYGON: + wkb_type += 6; + break; + case GEOS_GEOMETRYCOLLECTION: + wkb_type += 7; + break; + default: + break; + } + + return wkb_type; +} + #ifdef __cplusplus } #endif diff --git a/src/geoarrow_geos/geoarrow_geos.hpp b/src/geoarrow_geos/geoarrow_geos.hpp index 2267474..29c768c 100644 --- a/src/geoarrow_geos/geoarrow_geos.hpp +++ b/src/geoarrow_geos/geoarrow_geos.hpp @@ -180,6 +180,32 @@ class ArrayReader { GeoArrowGEOSArrayReader* reader_; }; +class SchemaCalculator { + public: + SchemaCalculator() : calc_(nullptr) { GeoArrowGEOSSchemaCalculatorCreate(&calc_); } + + SchemaCalculator(SchemaCalculator&& rhs) : calc_(rhs.calc_) { rhs.calc_ = nullptr; } + + SchemaCalculator(SchemaCalculator& rhs) = delete; + + ~SchemaCalculator() { + if (calc_ != nullptr) { + GeoArrowGEOSSchemaCalculatorDestroy(calc_); + } + } + + void Ingest(const int32_t* wkb_type, size_t n) { + GeoArrowGEOSSchemaCalculatorIngest(calc_, wkb_type, n); + } + + GeoArrowGEOSErrorCode Finish(enum GeoArrowGEOSEncoding encoding, ArrowSchema* out) { + return GeoArrowGEOSSchemaCalculatorFinish(calc_, encoding, out); + } + + private: + GeoArrowGEOSSchemaCalculator* calc_; +}; + } // namespace geos } // namespace geoarrow diff --git a/src/geoarrow_geos/geoarrow_geos_test.cc b/src/geoarrow_geos/geoarrow_geos_test.cc index 841001c..20b6757 100644 --- a/src/geoarrow_geos/geoarrow_geos_test.cc +++ b/src/geoarrow_geos/geoarrow_geos_test.cc @@ -404,3 +404,278 @@ TEST(GeoArrowGEOSTest, TestHppArrayReader) { geoarrow::geos::ArrayReader reader2 = std::move(reader); } + +GeoArrowGEOSErrorCode SchemaFromWkbType(const std::vector& wkb_type, + enum GeoArrowGEOSEncoding encoding, + ArrowSchema* out) { + geoarrow::geos::SchemaCalculator calc; + calc.Ingest(wkb_type.data(), wkb_type.size()); + + return calc.Finish(encoding, out); +} + +GeoArrowGEOSErrorCode SchemaFromWKT(const std::vector& wkt, + enum GeoArrowGEOSEncoding encoding, + ArrowSchema* out) { + GEOSCppHandle handle; + GEOSCppWKTReader wkt_reader(handle.handle); + geoarrow::geos::GeometryVector geom(handle.handle); + geom.resize(wkt.size()); + std::vector wkb_type(wkt.size()); + + for (size_t i = 0; i < wkt.size(); i++) { + if (wkt[i] == "") { + wkb_type[i] = 0; + continue; + } + + wkt_reader.Read(wkt[i], geom.mutable_data() + i); + wkb_type[i] = GeoArrowGEOSWKBType(handle.handle, geom.borrow(i)); + } + + return SchemaFromWkbType(wkb_type, encoding, out); +} + +std::string SchemaExtensionName(ArrowSchema* schema) { + ArrowStringView value; + value.data = ""; + value.size_bytes = 0; + ArrowMetadataGetValue(schema->metadata, ArrowCharView("ARROW:extension:name"), &value); + return std::string(value.data, value.size_bytes); +} + +std::string SchemaExtensionDims(ArrowSchema* schema) { + if (std::string(schema->format) == "+l") { + return SchemaExtensionDims(schema->children[0]); + } + + std::stringstream ss; + for (int64_t i = 0; i < schema->n_children; i++) { + ss << schema->children[i]->name; + } + + return ss.str(); +} + +TEST(GeoArrowGEOSTest, TestSchemaCalcEmpty) { + nanoarrow::UniqueSchema schema; + ASSERT_EQ(SchemaFromWkbType({}, GEOARROW_GEOS_ENCODING_UNKNOWN, schema.get()), EINVAL); + + ASSERT_EQ(SchemaFromWkbType({}, GEOARROW_GEOS_ENCODING_WKT, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.wkt"); + + schema.reset(); + ASSERT_EQ(SchemaFromWkbType({}, GEOARROW_GEOS_ENCODING_WKB, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.wkb"); + + schema.reset(); + ASSERT_EQ(SchemaFromWkbType({}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.wkb"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({}, GEOARROW_GEOS_ENCODING_GEOARROW_INTERLEAVED, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.wkb"); +} + +TEST(GeoArrowGEOSTest, TestSchemaCalcZM) { + nanoarrow::UniqueSchema schema; + + ASSERT_EQ(SchemaFromWkbType({1, 2001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyz"); + + schema.reset(); + ASSERT_EQ(SchemaFromWkbType({2001, 1}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyz"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({2001, 2001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyz"); + + schema.reset(); + ASSERT_EQ(SchemaFromWkbType({1, 3001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xym"); + + schema.reset(); + ASSERT_EQ(SchemaFromWkbType({3001, 1}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xym"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({3001, 3001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xym"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({3001, 3001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xym"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({2001, 3001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyzm"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({3001, 2001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyzm"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({2001, 4001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyzm"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({4001, 2001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyzm"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({3001, 4001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyzm"); + + schema.reset(); + ASSERT_EQ( + SchemaFromWkbType({4001, 3001}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + ASSERT_EQ(SchemaExtensionName(schema.get()), "geoarrow.point"); + EXPECT_EQ(SchemaExtensionDims(schema.get()), "xyzm"); +} + +class SchemaCalcFixture : public ::testing::TestWithParam> { + protected: + std::vector params; +}; + +TEST_P(SchemaCalcFixture, TestSchemaCalcSingleType) { + auto params = GetParam(); + std::string extension_name = params[0]; + std::string dimensions = params[1]; + std::string non_null = params[2]; + std::string non_null_simple = params[3]; + std::string non_null_mixed = params[4]; + + nanoarrow::UniqueSchema schema; + + // Length 1 + schema.reset(); + ASSERT_EQ(SchemaFromWKT({non_null}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), extension_name); + EXPECT_EQ(SchemaExtensionDims(schema.get()), dimensions); + + // non-null, null + schema.reset(); + ASSERT_EQ(SchemaFromWKT({non_null, ""}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), extension_name); + EXPECT_EQ(SchemaExtensionDims(schema.get()), dimensions); + + // null, non-null + schema.reset(); + ASSERT_EQ(SchemaFromWKT({"", non_null}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), extension_name); + EXPECT_EQ(SchemaExtensionDims(schema.get()), dimensions); + + // non-null, non-null + schema.reset(); + ASSERT_EQ( + SchemaFromWKT({non_null, non_null}, GEOARROW_GEOS_ENCODING_GEOARROW, schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), extension_name); + EXPECT_EQ(SchemaExtensionDims(schema.get()), dimensions); + + // non-null, EMPTY + schema.reset(); + ASSERT_EQ(SchemaFromWKT({non_null, "POINT EMPTY"}, GEOARROW_GEOS_ENCODING_GEOARROW, + schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), extension_name); + EXPECT_EQ(SchemaExtensionDims(schema.get()), dimensions); + + // simple, multi + schema.reset(); + ASSERT_EQ(SchemaFromWKT({non_null_simple, non_null}, GEOARROW_GEOS_ENCODING_GEOARROW, + schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), extension_name); + EXPECT_EQ(SchemaExtensionDims(schema.get()), dimensions); + + // multi, simple + schema.reset(); + ASSERT_EQ(SchemaFromWKT({non_null, non_null_simple}, GEOARROW_GEOS_ENCODING_GEOARROW, + schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), extension_name); + EXPECT_EQ(SchemaExtensionDims(schema.get()), dimensions); + + // mixed + schema.reset(); + ASSERT_EQ(SchemaFromWKT({non_null, non_null_mixed}, GEOARROW_GEOS_ENCODING_GEOARROW, + schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), "geoarrow.wkb"); + + schema.reset(); + ASSERT_EQ(SchemaFromWKT({non_null_mixed, non_null}, GEOARROW_GEOS_ENCODING_GEOARROW, + schema.get()), + NANOARROW_OK); + EXPECT_EQ(SchemaExtensionName(schema.get()), "geoarrow.wkb"); +} + +INSTANTIATE_TEST_SUITE_P( + GeoArrowGEOSTest, SchemaCalcFixture, + ::testing::Values( + // XY + std::vector({"geoarrow.point", "xy", "POINT (0 1)", "", + "LINESTRING (0 1, 2 3)"}), + std::vector({"geoarrow.linestring", "xy", "LINESTRING (0 1, 2 3)", + "", "POINT (0 1)"}), + std::vector({"geoarrow.polygon", "xy", + "POLYGON ((0 0, 1 0, 0 1, 0 0))", "", "POINT (0 1)"}), + std::vector({"geoarrow.multipoint", "xy", "MULTIPOINT (0 1)", + "POINT (0 1)", "LINESTRING (0 1, 2 3)"}), + std::vector({"geoarrow.multilinestring", "xy", + "MULTILINESTRING ((0 1, 2 3))", "LINESTRING (0 1, 2 3)", + "POINT (0 1)"}), + std::vector({"geoarrow.multipolygon", "xy", + "MULTIPOLYGON (((0 0, 1 0, 0 1, 0 0)))", + "POLYGON ((0 0, 1 0, 0 1, 0 0))", "POINT (0 1)"}), + std::vector({"geoarrow.wkb", "", "GEOMETRYCOLLECTION (POINT (0 1))", + "", ""}), + // XYZ + std::vector({"geoarrow.point", "xyz", "POINT Z (0 1 2)", + "POINT (0 1)", "LINESTRING (0 1, 2 3)"}) + + ));