diff --git a/CMakeLists.txt b/CMakeLists.txt index 27e54abe..1afd3a78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ set(GEOARROW_SOURCES src/geoarrow/array_view.c src/geoarrow/util.c src/geoarrow/visitor.c + src/geoarrow/native_writer.c src/geoarrow/wkb_reader.c src/geoarrow/wkb_writer.c src/geoarrow/wkt_reader.c @@ -262,6 +263,7 @@ if(GEOARROW_BUILD_TESTS) add_executable(kernel_test src/geoarrow/kernel_test.cc) add_executable(visitor_test src/geoarrow/visitor_test.cc) add_executable(util_test src/geoarrow/util_test.cc) + add_executable(native_writer_test src/geoarrow/native_writer_test.cc) add_executable(wkb_reader_test src/geoarrow/wkb_reader_test.cc) add_executable(wkb_writer_test src/geoarrow/wkb_writer_test.cc) add_executable(wkt_reader_test src/geoarrow/wkt_reader_test.cc) @@ -286,6 +288,7 @@ if(GEOARROW_BUILD_TESTS) target_link_libraries(metadata_test geoarrow gtest_main nanoarrow::nanoarrow) target_link_libraries(visitor_test geoarrow gtest_main) target_link_libraries(util_test geoarrow gtest_main) + target_link_libraries(native_writer_test geoarrow gtest_main nanoarrow::nanoarrow) target_link_libraries(wkb_reader_test geoarrow gtest_main nanoarrow::nanoarrow) target_link_libraries(wkb_writer_test geoarrow gtest_main nanoarrow::nanoarrow) target_link_libraries(wkt_reader_test geoarrow gtest_main nanoarrow::nanoarrow) @@ -319,6 +322,7 @@ if(GEOARROW_BUILD_TESTS) gtest_discover_tests(metadata_test) gtest_discover_tests(visitor_test) gtest_discover_tests(util_test) + gtest_discover_tests(native_writer_test) gtest_discover_tests(wkb_reader_test) gtest_discover_tests(wkb_writer_test) gtest_discover_tests(wkt_reader_test) diff --git a/src/geoarrow/array_reader.c b/src/geoarrow/array_reader.c index 18903ab2..3a5aba6d 100644 --- a/src/geoarrow/array_reader.c +++ b/src/geoarrow/array_reader.c @@ -9,7 +9,7 @@ struct GeoArrowArrayReaderPrivate { struct GeoArrowWKBReader wkb_reader; }; -static GeoArrowErrorCode GeoArrowArrayViewVisitWKT( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativeWKT( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowWKTReader* reader, struct GeoArrowVisitor* v) { struct GeoArrowStringView item; @@ -31,7 +31,7 @@ static GeoArrowErrorCode GeoArrowArrayViewVisitWKT( return GEOARROW_OK; } -static GeoArrowErrorCode GeoArrowArrayViewVisitWKB( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativeWKB( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowWKBReader* reader, struct GeoArrowVisitor* v) { struct GeoArrowBufferView item; @@ -160,13 +160,13 @@ GeoArrowErrorCode GeoArrowArrayReaderVisit(struct GeoArrowArrayReader* reader, switch (private_data->array_view.schema_view.type) { case GEOARROW_TYPE_WKT: - return GeoArrowArrayViewVisitWKT(&private_data->array_view, offset, length, - &private_data->wkt_reader, v); + return GeoArrowArrayViewVisitNativeWKT(&private_data->array_view, offset, length, + &private_data->wkt_reader, v); case GEOARROW_TYPE_WKB: - return GeoArrowArrayViewVisitWKB(&private_data->array_view, offset, length, - &private_data->wkb_reader, v); + return GeoArrowArrayViewVisitNativeWKB(&private_data->array_view, offset, length, + &private_data->wkb_reader, v); default: - return GeoArrowArrayViewVisit(&private_data->array_view, offset, length, v); + return GeoArrowArrayViewVisitNative(&private_data->array_view, offset, length, v); } } diff --git a/src/geoarrow/array_view.c b/src/geoarrow/array_view.c index 64f910a4..30264a1d 100644 --- a/src/geoarrow/array_view.c +++ b/src/geoarrow/array_view.c @@ -277,7 +277,7 @@ static inline void GeoArrowCoordViewUpdate(const struct GeoArrowCoordView* src, dst->n_coords = length; } -static GeoArrowErrorCode GeoArrowArrayViewVisitPoint( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativePoint( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowVisitor* v) { struct GeoArrowCoordView coords = array_view->coords; @@ -306,7 +306,7 @@ static GeoArrowErrorCode GeoArrowArrayViewVisitPoint( return GEOARROW_OK; } -static GeoArrowErrorCode GeoArrowArrayViewVisitLinestring( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativeLinestring( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowVisitor* v) { struct GeoArrowCoordView coords = array_view->coords; @@ -336,7 +336,7 @@ static GeoArrowErrorCode GeoArrowArrayViewVisitLinestring( return GEOARROW_OK; } -static GeoArrowErrorCode GeoArrowArrayViewVisitPolygon( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativePolygon( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowVisitor* v) { struct GeoArrowCoordView coords = array_view->coords; @@ -377,7 +377,7 @@ static GeoArrowErrorCode GeoArrowArrayViewVisitPolygon( return GEOARROW_OK; } -static GeoArrowErrorCode GeoArrowArrayViewVisitMultipoint( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativeMultipoint( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowVisitor* v) { struct GeoArrowCoordView coords = array_view->coords; @@ -412,7 +412,7 @@ static GeoArrowErrorCode GeoArrowArrayViewVisitMultipoint( return GEOARROW_OK; } -static GeoArrowErrorCode GeoArrowArrayViewVisitMultilinestring( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativeMultilinestring( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowVisitor* v) { struct GeoArrowCoordView coords = array_view->coords; @@ -454,7 +454,7 @@ static GeoArrowErrorCode GeoArrowArrayViewVisitMultilinestring( return GEOARROW_OK; } -static GeoArrowErrorCode GeoArrowArrayViewVisitMultipolygon( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativeMultipolygon( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowVisitor* v) { struct GeoArrowCoordView coords = array_view->coords; @@ -509,7 +509,7 @@ static GeoArrowErrorCode GeoArrowArrayViewVisitMultipolygon( return GEOARROW_OK; } -static GeoArrowErrorCode GeoArrowArrayViewVisitBox( +static GeoArrowErrorCode GeoArrowArrayViewVisitNativeBox( const struct GeoArrowArrayView* array_view, int64_t offset, int64_t length, struct GeoArrowVisitor* v) { // We aren't going to attempt Z, M, or ZM boxes since there is no canonical @@ -581,24 +581,24 @@ static GeoArrowErrorCode GeoArrowArrayViewVisitBox( return GEOARROW_OK; } -GeoArrowErrorCode GeoArrowArrayViewVisit(const struct GeoArrowArrayView* array_view, - int64_t offset, int64_t length, - struct GeoArrowVisitor* v) { +GeoArrowErrorCode GeoArrowArrayViewVisitNative(const struct GeoArrowArrayView* array_view, + int64_t offset, int64_t length, + struct GeoArrowVisitor* v) { switch (array_view->schema_view.geometry_type) { case GEOARROW_GEOMETRY_TYPE_BOX: - return GeoArrowArrayViewVisitBox(array_view, offset, length, v); + return GeoArrowArrayViewVisitNativeBox(array_view, offset, length, v); case GEOARROW_GEOMETRY_TYPE_POINT: - return GeoArrowArrayViewVisitPoint(array_view, offset, length, v); + return GeoArrowArrayViewVisitNativePoint(array_view, offset, length, v); case GEOARROW_GEOMETRY_TYPE_LINESTRING: - return GeoArrowArrayViewVisitLinestring(array_view, offset, length, v); + return GeoArrowArrayViewVisitNativeLinestring(array_view, offset, length, v); case GEOARROW_GEOMETRY_TYPE_POLYGON: - return GeoArrowArrayViewVisitPolygon(array_view, offset, length, v); + return GeoArrowArrayViewVisitNativePolygon(array_view, offset, length, v); case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: - return GeoArrowArrayViewVisitMultipoint(array_view, offset, length, v); + return GeoArrowArrayViewVisitNativeMultipoint(array_view, offset, length, v); case GEOARROW_GEOMETRY_TYPE_MULTILINESTRING: - return GeoArrowArrayViewVisitMultilinestring(array_view, offset, length, v); + return GeoArrowArrayViewVisitNativeMultilinestring(array_view, offset, length, v); case GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON: - return GeoArrowArrayViewVisitMultipolygon(array_view, offset, length, v); + return GeoArrowArrayViewVisitNativeMultipolygon(array_view, offset, length, v); default: return ENOTSUP; } diff --git a/src/geoarrow/array_view_test.cc b/src/geoarrow/array_view_test.cc index 39270f63..84eb8539 100644 --- a/src/geoarrow/array_view_test.cc +++ b/src/geoarrow/array_view_test.cc @@ -246,8 +246,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidBox) { EXPECT_EQ(array_view.coords.values[3][1], -INFINITY); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); EXPECT_EQ(values[0], "POLYGON ((0 1, 2 1, 2 3, 0 3, 0 1))"); @@ -270,7 +271,7 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidBoxNonXY) { struct GeoArrowVisitor v; GeoArrowVisitorInitVoid(&v); v.error = &error; - ASSERT_EQ(GeoArrowArrayViewVisit(&array_view, 0, 0, &v), ENOTSUP); + ASSERT_EQ(GeoArrowArrayViewVisitNative(&array_view, 0, 0, &v), ENOTSUP); ASSERT_STREQ(error.message, "Can't visit box with non-XY dimensions"); ArrowSchemaRelease(&schema); @@ -307,8 +308,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidPoint) { EXPECT_EQ(array_view.coords.values[1][0], 10); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], "POINT (30 10)"); @@ -349,8 +351,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidInterleavedPoint) { EXPECT_EQ(array_view.coords.values[1][0], 10); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], "POINT (30 10)"); @@ -402,8 +405,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidPointWithOffset) { EXPECT_EQ(array_view.length[0], 2); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], ""); @@ -453,8 +457,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidInterleavedPointWithOffset) { EXPECT_EQ(array_view.length[0], 2); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], ""); @@ -508,8 +513,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidLinestring) { EXPECT_EQ(array_view.coords.values[1][1], 1); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); EXPECT_EQ(values[0], "LINESTRING (30 10, 0 1)"); @@ -569,8 +575,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidLinestringWithOffset) { EXPECT_EQ(array_view.length[1], 3); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], ""); @@ -646,8 +653,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidPolygon) { EXPECT_EQ(array_view.coords.values[1][3], 2); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); EXPECT_EQ(values[0], "POLYGON ((1 2, 2 3, 4 5, 1 2))"); @@ -733,8 +741,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidPolygonWithOffset) { EXPECT_EQ(array_view.length[2], 4); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], ""); @@ -788,8 +797,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidMultipoint) { EXPECT_EQ(array_view.coords.values[1][1], 1); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); EXPECT_EQ(values[0], "MULTIPOINT ((30 10), (0 1))"); @@ -851,8 +861,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidMultiPointWithOffset) { EXPECT_EQ(array_view.length[1], 3); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], ""); @@ -932,8 +943,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidMultilinestring) { EXPECT_EQ(array_view.coords.values[1][3], 2); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); EXPECT_EQ(values[0], "MULTILINESTRING ((30 10, 0 1), (31 11, 1 2))"); @@ -1021,8 +1033,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidMultiLinestringWithOffset) { EXPECT_EQ(array_view.length[2], 4); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], ""); @@ -1114,8 +1127,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidMultipolygon) { EXPECT_EQ(array_view.coords.values[1][3], 2); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); EXPECT_EQ(values[0], "MULTIPOLYGON (((1 2, 2 3, 4 5, 1 2)))"); @@ -1223,8 +1237,9 @@ TEST(ArrayViewTest, ArrayViewTestSetArrayValidMultipolygonWithOffsets) { EXPECT_EQ(array_view.length[3], 4); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 2); EXPECT_EQ(values[0], ""); diff --git a/src/geoarrow/array_writer.c b/src/geoarrow/array_writer.c index d2ec1dd0..cb54ded9 100644 --- a/src/geoarrow/array_writer.c +++ b/src/geoarrow/array_writer.c @@ -6,9 +6,9 @@ #include "geoarrow.h" struct GeoArrowArrayWriterPrivate { + struct GeoArrowNativeWriter native_writer; struct GeoArrowWKTWriter wkt_writer; struct GeoArrowWKBWriter wkb_writer; - struct GeoArrowBuilder builder; enum GeoArrowType type; }; @@ -36,7 +36,7 @@ GeoArrowErrorCode GeoArrowArrayWriterInitFromType(struct GeoArrowArrayWriter* wr result = GeoArrowWKBWriterInit(&private_data->wkb_writer); break; default: - result = GeoArrowBuilderInitFromType(&private_data->builder, type); + result = GeoArrowNativeWriterInit(&private_data->native_writer, type); break; } @@ -98,26 +98,7 @@ GeoArrowErrorCode GeoArrowArrayWriterInitVisitor(struct GeoArrowArrayWriter* wri GeoArrowWKBWriterInitVisitor(&private_data->wkb_writer, v); return GEOARROW_OK; default: - return GeoArrowBuilderInitVisitor(&private_data->builder, v); - } -} - -GeoArrowErrorCode GeoArrowArrayWriterBuilder(struct GeoArrowArrayWriter* writer, - struct GeoArrowBuilder** out) { - NANOARROW_DCHECK(writer != NULL); - NANOARROW_DCHECK(out != NULL); - struct GeoArrowArrayWriterPrivate* private_data = - (struct GeoArrowArrayWriterPrivate*)writer->private_data; - - // One could update the wkt/wkb writers to use the builder so that this could - // return the builder in any case. - switch (private_data->type) { - case GEOARROW_TYPE_WKT: - case GEOARROW_TYPE_WKB: - return ENOTSUP; - default: - *out = &private_data->builder; - return GEOARROW_OK; + return GeoArrowNativeWriterInitVisitor(&private_data->native_writer, v); } } @@ -133,7 +114,7 @@ GeoArrowErrorCode GeoArrowArrayWriterFinish(struct GeoArrowArrayWriter* writer, case GEOARROW_TYPE_WKB: return GeoArrowWKBWriterFinish(&private_data->wkb_writer, array, error); default: - return GeoArrowBuilderFinish(&private_data->builder, array, error); + return GeoArrowNativeWriterFinish(&private_data->native_writer, array, error); } } @@ -149,8 +130,8 @@ void GeoArrowArrayWriterReset(struct GeoArrowArrayWriter* writer) { GeoArrowWKBWriterReset(&private_data->wkb_writer); } - if (private_data->builder.private_data != NULL) { - GeoArrowBuilderReset(&private_data->builder); + if (private_data->native_writer.private_data != NULL) { + GeoArrowNativeWriterReset(&private_data->native_writer); } ArrowFree(private_data); diff --git a/src/geoarrow/array_writer_test.cc b/src/geoarrow/array_writer_test.cc index 54fb736b..0d17cc18 100644 --- a/src/geoarrow/array_writer_test.cc +++ b/src/geoarrow/array_writer_test.cc @@ -9,17 +9,14 @@ TEST(ArrayWriterTest, ArrayWriterTestInitFromType) { struct GeoArrowArrayWriter writer; - struct GeoArrowBuilder* builder = nullptr; ASSERT_EQ(GeoArrowArrayWriterInitFromType(&writer, GEOARROW_TYPE_WKT), GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayWriterBuilder(&writer, &builder), ENOTSUP); - ASSERT_EQ(builder, nullptr); + GeoArrowArrayWriterReset(&writer); + + ASSERT_EQ(GeoArrowArrayWriterInitFromType(&writer, GEOARROW_TYPE_WKB), GEOARROW_OK); GeoArrowArrayWriterReset(&writer); ASSERT_EQ(GeoArrowArrayWriterInitFromType(&writer, GEOARROW_TYPE_POINT), GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayWriterBuilder(&writer, &builder), GEOARROW_OK); - ASSERT_NE(builder, nullptr); - ASSERT_EQ(builder->view.schema_view.type, GEOARROW_TYPE_POINT); GeoArrowArrayWriterReset(&writer); } diff --git a/src/geoarrow/builder.c b/src/geoarrow/builder.c index da88025f..4595df1a 100644 --- a/src/geoarrow/builder.c +++ b/src/geoarrow/builder.c @@ -5,12 +5,6 @@ #include "geoarrow.h" -// Bytes for four quiet (little-endian) NANs -static uint8_t kEmptyPointCoords[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f}; - struct BuilderPrivate { // The ArrowSchema (without extension) for this builder struct ArrowSchema schema; @@ -23,17 +17,6 @@ struct BuilderPrivate { // might be NULL. struct ArrowBitmap* validity; struct ArrowBuffer* buffers[9]; - - // Fields to keep track of state when using the visitor pattern - int visitor_initialized; - int feat_is_null; - int nesting_multipoint; - double empty_coord_values[4]; - struct GeoArrowCoordView empty_coord; - enum GeoArrowDimensions last_dimensions; - int64_t size[32]; - int32_t level; - int64_t null_count; }; static ArrowErrorCode GeoArrowBuilderInitArrayAndCachePointers( @@ -66,31 +49,6 @@ static ArrowErrorCode GeoArrowBuilderInitArrayAndCachePointers( builder->view.coords.values[i] = NULL; } - // Set the null_count to zero - private->null_count = 0; - - // When we use the visitor pattern we initialize some things that need - // to happen exactly once (e.g., append an initial zero to offset buffers) - private->visitor_initialized = 0; - - return GEOARROW_OK; -} - -static GeoArrowErrorCode GeoArrowBuilderPrepareForVisiting( - struct GeoArrowBuilder* builder) { - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - if (!private->visitor_initialized) { - int32_t zero = 0; - for (int i = 0; i < builder->view.n_offsets; i++) { - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, i, &zero, 1)); - } - - builder->view.coords.size_coords = 0; - builder->view.coords.capacity_coords = 0; - - private->visitor_initialized = 1; - } - return GEOARROW_OK; } @@ -143,16 +101,6 @@ static GeoArrowErrorCode GeoArrowBuilderInitInternal(struct GeoArrowBuilder* bui return result; } - // Initialize one empty coordinate for the visitor pattern - memcpy(private->empty_coord_values, kEmptyPointCoords, 4 * sizeof(double)); - private->empty_coord.values[0] = private->empty_coord_values; - private->empty_coord.values[1] = private->empty_coord_values + 1; - private->empty_coord.values[2] = private->empty_coord_values + 2; - private->empty_coord.values[3] = private->empty_coord_values + 3; - private->empty_coord.n_coords = 1; - private->empty_coord.n_values = 4; - private->empty_coord.coords_stride = 1; - return GEOARROW_OK; } @@ -309,11 +257,8 @@ GeoArrowErrorCode GeoArrowBuilderFinish(struct GeoArrowBuilder* builder, NANOARROW_RETURN_NOT_OK( ArrowArrayFinishBuildingDefault(&private->array, (struct ArrowError*)error)); - // If the null_count was incremented, we know what it is; if the first buffer - // is non-null, we don't know what it is - if (private->null_count > 0) { - private->array.null_count = private->null_count; - } else if (private->array.buffers[0] != NULL) { + // If the first buffer is non-null, we don't know what it is + if (private->array.buffers[0] != NULL) { private->array.null_count = -1; } @@ -321,22 +266,13 @@ GeoArrowErrorCode GeoArrowBuilderFinish(struct GeoArrowBuilder* builder, struct ArrowArray tmp; ArrowArrayMove(&private->array, &tmp); - // Prepare for another round of visiting (e.g., append zeroes to the offset arrays) - int need_reinit_visitor = private->visitor_initialized; + // Prepare for another round of building int result = GeoArrowBuilderInitArrayAndCachePointers(builder); if (result != GEOARROW_OK) { tmp.release(&tmp); return result; } - if (need_reinit_visitor) { - result = GeoArrowBuilderPrepareForVisiting(builder); - if (result != GEOARROW_OK) { - tmp.release(&tmp); - return result; - } - } - // Move the result ArrowArrayMove(&tmp, array); return GEOARROW_OK; @@ -477,588 +413,3 @@ static void GeoArrowSetCoordContainerLength(struct GeoArrowBuilder* builder) { break; } } - -static int feat_start_point(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->level = 0; - private->size[0] = 0; - private->feat_is_null = 0; - return GEOARROW_OK; -} - -static int geom_start_point(struct GeoArrowVisitor* v, - enum GeoArrowGeometryType geometry_type, - enum GeoArrowDimensions dimensions) { - NANOARROW_UNUSED(geometry_type); - - // level++, geometry type, dimensions, reset size - // validate dimensions, maybe against some options that indicate - // error for mismatch, fill, or drop behaviour - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->last_dimensions = dimensions; - return GEOARROW_OK; -} - -static int ring_start_point(struct GeoArrowVisitor* v) { - NANOARROW_UNUSED(v); - return GEOARROW_OK; -} - -static int coords_point(struct GeoArrowVisitor* v, - const struct GeoArrowCoordView* coords) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->size[0] += coords->n_coords; - return GeoArrowBuilderCoordsAppend(builder, coords, private->last_dimensions, 0, - coords->n_coords); -} - -static int ring_end_point(struct GeoArrowVisitor* v) { - NANOARROW_UNUSED(v); - return GEOARROW_OK; -} - -static int geom_end_point(struct GeoArrowVisitor* v) { - NANOARROW_UNUSED(v); - return GEOARROW_OK; -} - -static int null_feat_point(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->feat_is_null = 1; - return GEOARROW_OK; -} - -static int feat_end_point(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - // If there weren't any coords (i.e., EMPTY), we need to write some NANs here - // if there was >1 coords, we also need to error or we'll get misaligned output - if (private->size[0] == 0) { - int n_dim = _GeoArrowkNumDimensions[builder->view.schema_view.dimensions]; - private->empty_coord.n_values = n_dim; - NANOARROW_RETURN_NOT_OK(coords_point(v, &private->empty_coord)); - } else if (private->size[0] != 1) { - GeoArrowErrorSet(v->error, "Can't convert feature with >1 coordinate to POINT"); - return EINVAL; - } - - if (private->feat_is_null) { - int64_t current_length = builder->view.coords.size_coords; - if (private->validity->buffer.data == NULL) { - NANOARROW_RETURN_NOT_OK(ArrowBitmapReserve(private->validity, current_length)); - ArrowBitmapAppendUnsafe(private->validity, 1, current_length - 1); - } - - private->null_count++; - NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(private->validity, 0, 1)); - } else if (private->validity->buffer.data != NULL) { - NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(private->validity, 1, 1)); - } - - return GEOARROW_OK; -} - -static void GeoArrowVisitorInitPoint(struct GeoArrowBuilder* builder, - struct GeoArrowVisitor* v) { - struct GeoArrowError* previous_error = v->error; - GeoArrowVisitorInitVoid(v); - v->error = previous_error; - - v->feat_start = &feat_start_point; - v->null_feat = &null_feat_point; - v->geom_start = &geom_start_point; - v->ring_start = &ring_start_point; - v->coords = &coords_point; - v->ring_end = &ring_end_point; - v->geom_end = &geom_end_point; - v->feat_end = &feat_end_point; - v->private_data = builder; -} - -static int feat_start_multipoint(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->level = 0; - private->size[0] = 0; - private->size[1] = 0; - private->feat_is_null = 0; - private->nesting_multipoint = 0; - return GEOARROW_OK; -} - -static int geom_start_multipoint(struct GeoArrowVisitor* v, - enum GeoArrowGeometryType geometry_type, - enum GeoArrowDimensions dimensions) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->last_dimensions = dimensions; - - switch (geometry_type) { - case GEOARROW_GEOMETRY_TYPE_LINESTRING: - private - ->level++; - break; - case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: - private - ->nesting_multipoint = 1; - private->level++; - break; - case GEOARROW_GEOMETRY_TYPE_POINT: - if (private->nesting_multipoint) { - private->nesting_multipoint++; - } - default: - break; - } - - return GEOARROW_OK; -} - -static int ring_start_multipoint(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->level++; - return GEOARROW_OK; -} - -static int coords_multipoint(struct GeoArrowVisitor* v, - const struct GeoArrowCoordView* coords) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->size[1] += coords->n_coords; - return GeoArrowBuilderCoordsAppend(builder, coords, private->last_dimensions, 0, - coords->n_coords); -} - -static int ring_end_multipoint(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - private->level--; - private->size[0]++; - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 0, &n_coord32, 1)); - - return GEOARROW_OK; -} - -static int geom_end_multipoint(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - // Ignore geom_end calls from the end of a POINT nested within a MULTIPOINT - if (private->nesting_multipoint == 2) { - private->nesting_multipoint--; - return GEOARROW_OK; - } - - if (private->level == 1) { - private->size[0]++; - private->level--; - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 0, &n_coord32, 1)); - } - - return GEOARROW_OK; -} - -static int null_feat_multipoint(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->feat_is_null = 1; - return GEOARROW_OK; -} - -static int feat_end_multipoint(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - // If we didn't finish any sequences, finish at least one. This is usually an - // EMPTY but could also be a single point. - if (private->size[0] == 0) { - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 0, &n_coord32, 1)); - } else if (private->size[0] != 1) { - GeoArrowErrorSet(v->error, "Can't convert feature with >1 sequence to LINESTRING"); - return EINVAL; - } - - if (private->feat_is_null) { - int64_t current_length = builder->view.buffers[1].size_bytes / sizeof(int32_t) - 1; - if (private->validity->buffer.data == NULL) { - NANOARROW_RETURN_NOT_OK(ArrowBitmapReserve(private->validity, current_length)); - ArrowBitmapAppendUnsafe(private->validity, 1, current_length - 1); - } - - private->null_count++; - NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(private->validity, 0, 1)); - } else if (private->validity->buffer.data != NULL) { - NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(private->validity, 1, 1)); - } - - return GEOARROW_OK; -} - -static void GeoArrowVisitorInitLinestring(struct GeoArrowBuilder* builder, - struct GeoArrowVisitor* v) { - struct GeoArrowError* previous_error = v->error; - GeoArrowVisitorInitVoid(v); - v->error = previous_error; - - v->feat_start = &feat_start_multipoint; - v->null_feat = &null_feat_multipoint; - v->geom_start = &geom_start_multipoint; - v->ring_start = &ring_start_multipoint; - v->coords = &coords_multipoint; - v->ring_end = &ring_end_multipoint; - v->geom_end = &geom_end_multipoint; - v->feat_end = &feat_end_multipoint; - v->private_data = builder; -} - -static int feat_start_multilinestring(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->level = 0; - private->size[0] = 0; - private->size[1] = 0; - private->feat_is_null = 0; - return GEOARROW_OK; -} - -static int geom_start_multilinestring(struct GeoArrowVisitor* v, - enum GeoArrowGeometryType geometry_type, - enum GeoArrowDimensions dimensions) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->last_dimensions = dimensions; - - switch (geometry_type) { - case GEOARROW_GEOMETRY_TYPE_LINESTRING: - case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: - private - ->level++; - break; - default: - break; - } - - return GEOARROW_OK; -} - -static int ring_start_multilinestring(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->level++; - return GEOARROW_OK; -} - -static int coords_multilinestring(struct GeoArrowVisitor* v, - const struct GeoArrowCoordView* coords) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->size[1] += coords->n_coords; - return GeoArrowBuilderCoordsAppend(builder, coords, private->last_dimensions, 0, - coords->n_coords); -} - -static int ring_end_multilinestring(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - private->level--; - if (private->size[1] > 0) { - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 1, &n_coord32, 1)); - private->size[0]++; - private->size[1] = 0; - } - - return GEOARROW_OK; -} - -static int geom_end_multilinestring(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - if (private->level == 1) { - private->level--; - if (private->size[1] > 0) { - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 1, &n_coord32, 1)); - private->size[0]++; - private->size[1] = 0; - } - } - - return GEOARROW_OK; -} - -static int null_feat_multilinestring(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->feat_is_null = 1; - return GEOARROW_OK; -} - -static int feat_end_multilinestring(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - // If we have an unfinished sequence left over, finish it now. This could have - // occurred if the last geometry that was visited was a POINT. - if (private->size[1] > 0) { - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 1, &n_coord32, 1)); - } - - // Finish off the sequence of sequences. This is a polygon or multilinestring - // so it can any number of them. - int32_t n_seq32 = (int32_t)(builder->view.buffers[2].size_bytes / sizeof(int32_t)) - 1; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 0, &n_seq32, 1)); - - if (private->feat_is_null) { - int64_t current_length = builder->view.buffers[1].size_bytes / sizeof(int32_t) - 1; - if (private->validity->buffer.data == NULL) { - NANOARROW_RETURN_NOT_OK(ArrowBitmapReserve(private->validity, current_length)); - ArrowBitmapAppendUnsafe(private->validity, 1, current_length - 1); - } - - private->null_count++; - NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(private->validity, 0, 1)); - } else if (private->validity->buffer.data != NULL) { - NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(private->validity, 1, 1)); - } - - return GEOARROW_OK; -} - -static void GeoArrowVisitorInitMultiLinestring(struct GeoArrowBuilder* builder, - struct GeoArrowVisitor* v) { - struct GeoArrowError* previous_error = v->error; - GeoArrowVisitorInitVoid(v); - v->error = previous_error; - - v->feat_start = &feat_start_multilinestring; - v->null_feat = &null_feat_multilinestring; - v->geom_start = &geom_start_multilinestring; - v->ring_start = &ring_start_multilinestring; - v->coords = &coords_multilinestring; - v->ring_end = &ring_end_multilinestring; - v->geom_end = &geom_end_multilinestring; - v->feat_end = &feat_end_multilinestring; - v->private_data = builder; -} - -static int feat_start_multipolygon(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->level = 0; - private->size[0] = 0; - private->size[1] = 0; - private->size[2] = 0; - private->feat_is_null = 0; - return GEOARROW_OK; -} - -static int geom_start_multipolygon(struct GeoArrowVisitor* v, - enum GeoArrowGeometryType geometry_type, - enum GeoArrowDimensions dimensions) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->last_dimensions = dimensions; - - switch (geometry_type) { - case GEOARROW_GEOMETRY_TYPE_MULTILINESTRING: - case GEOARROW_GEOMETRY_TYPE_POLYGON: - case GEOARROW_GEOMETRY_TYPE_LINESTRING: - case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: - private - ->level++; - break; - default: - break; - } - - return GEOARROW_OK; -} - -static int ring_start_multipolygon(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->level++; - return GEOARROW_OK; -} - -static int coords_multipolygon(struct GeoArrowVisitor* v, - const struct GeoArrowCoordView* coords) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->size[2] += coords->n_coords; - return GeoArrowBuilderCoordsAppend(builder, coords, private->last_dimensions, 0, - coords->n_coords); -} - -static int ring_end_multipolygon(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - private->level--; - if (private->size[2] > 0) { - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 2, &n_coord32, 1)); - private->size[1]++; - private->size[2] = 0; - } - - return GEOARROW_OK; -} - -static int geom_end_multipolygon(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - if (private->level == 2) { - private->level--; - if (private->size[2] > 0) { - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 2, &n_coord32, 1)); - private->size[1]++; - private->size[2] = 0; - } - } else if (private->level == 1) { - private->level--; - if (private->size[1] > 0) { - int32_t n_seq32 = - (int32_t)(builder->view.buffers[3].size_bytes / sizeof(int32_t)) - 1; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 1, &n_seq32, 1)); - private->size[0]++; - private->size[1] = 0; - } - } - - return GEOARROW_OK; -} - -static int null_feat_multipolygon(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - private->feat_is_null = 1; - return GEOARROW_OK; -} - -static int feat_end_multipolygon(struct GeoArrowVisitor* v) { - struct GeoArrowBuilder* builder = (struct GeoArrowBuilder*)v->private_data; - struct BuilderPrivate* private = (struct BuilderPrivate*)builder->private_data; - - // If we have an unfinished sequence left over, finish it now. This could have - // occurred if the last geometry that was visited was a POINT. - if (private->size[2] > 0) { - if (builder->view.coords.size_coords > 2147483647) { - return EOVERFLOW; - } - int32_t n_coord32 = (int32_t)builder->view.coords.size_coords; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 2, &n_coord32, 1)); - private->size[1]++; - } - - // If we have an unfinished sequence of sequences left over, finish it now. - // This could have occurred if the last geometry that was visited was a POINT. - if (private->size[1] > 0) { - int32_t n_seq32 = - (int32_t)(builder->view.buffers[3].size_bytes / sizeof(int32_t)) - 1; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 1, &n_seq32, 1)); - } - - // Finish off the sequence of sequence of sequences. This is a multipolygon - // so it can be any number of them. - int32_t n_seq_seq32 = - (int32_t)(builder->view.buffers[2].size_bytes / sizeof(int32_t)) - 1; - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, 0, &n_seq_seq32, 1)); - - if (private->feat_is_null) { - int64_t current_length = builder->view.buffers[1].size_bytes / sizeof(int32_t) - 1; - if (private->validity->buffer.data == NULL) { - NANOARROW_RETURN_NOT_OK(ArrowBitmapReserve(private->validity, current_length)); - ArrowBitmapAppendUnsafe(private->validity, 1, current_length - 1); - } - - private->null_count++; - NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(private->validity, 0, 1)); - } else if (private->validity->buffer.data != NULL) { - NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(private->validity, 1, 1)); - } - - return GEOARROW_OK; -} - -static void GeoArrowVisitorInitMultiPolygon(struct GeoArrowBuilder* builder, - struct GeoArrowVisitor* v) { - struct GeoArrowError* previous_error = v->error; - GeoArrowVisitorInitVoid(v); - v->error = previous_error; - - v->feat_start = &feat_start_multipolygon; - v->null_feat = &null_feat_multipolygon; - v->geom_start = &geom_start_multipolygon; - v->ring_start = &ring_start_multipolygon; - v->coords = &coords_multipolygon; - v->ring_end = &ring_end_multipolygon; - v->geom_end = &geom_end_multipolygon; - v->feat_end = &feat_end_multipolygon; - v->private_data = builder; -} - -GeoArrowErrorCode GeoArrowBuilderInitVisitor(struct GeoArrowBuilder* builder, - struct GeoArrowVisitor* v) { - switch (builder->view.schema_view.geometry_type) { - case GEOARROW_GEOMETRY_TYPE_POINT: - GeoArrowVisitorInitPoint(builder, v); - break; - case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: - case GEOARROW_GEOMETRY_TYPE_LINESTRING: - GeoArrowVisitorInitLinestring(builder, v); - break; - case GEOARROW_GEOMETRY_TYPE_MULTILINESTRING: - case GEOARROW_GEOMETRY_TYPE_POLYGON: - GeoArrowVisitorInitMultiLinestring(builder, v); - break; - case GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON: - GeoArrowVisitorInitMultiPolygon(builder, v); - break; - default: - return EINVAL; - } - - NANOARROW_RETURN_NOT_OK(GeoArrowBuilderPrepareForVisiting(builder)); - return GEOARROW_OK; -} diff --git a/src/geoarrow/builder_test.cc b/src/geoarrow/builder_test.cc index 904fcf3e..df293020 100644 --- a/src/geoarrow/builder_test.cc +++ b/src/geoarrow/builder_test.cc @@ -146,8 +146,9 @@ TEST(BuilderTest, BuilderTestAppendCoords) { ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); @@ -158,690 +159,6 @@ TEST(BuilderTest, BuilderTestAppendCoords) { array_out.release(&array_out); } -TEST(BuilderTest, BuilderTestPoint) { - struct GeoArrowBuilder builder; - struct GeoArrowVisitor v; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, GEOARROW_TYPE_POINT), GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); - - TestCoords coords({1}, {2}); - - // Valid - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Null - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Empty - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - struct ArrowArray array_out; - struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); - - EXPECT_EQ(array_out.length, 3); - EXPECT_EQ(array_out.null_count, 1); - - ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_POINT), GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - - WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); - - auto values = tester.WKTValues(""); - ASSERT_EQ(values.size(), 3); - EXPECT_EQ(values[0], "POINT (1 2)"); - EXPECT_EQ(values[1], ""); - EXPECT_EQ(values[2], "POINT (nan nan)"); - - array_out.release(&array_out); -} - -TEST(BuilderTest, BuilderTestPointDims) { - struct GeoArrowBuilder builder; - struct GeoArrowVisitor v; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, GEOARROW_TYPE_POINT_ZM), GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); - - TestCoords coords({1}, {2}, {3}, {4}); - - // XY - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // XYZ - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XYZ), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // XYM - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XYM), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // XYZM - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XYZM), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - struct ArrowArray array_out; - struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); - - EXPECT_EQ(array_out.length, 4); - EXPECT_EQ(array_out.null_count, 0); - - ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_POINT_ZM), - GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - - WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); - - auto values = tester.WKTValues(""); - ASSERT_EQ(values.size(), 4); - EXPECT_EQ(values[0], "POINT ZM (1 2 nan nan)"); - EXPECT_EQ(values[1], "POINT ZM (1 2 3 nan)"); - EXPECT_EQ(values[2], "POINT ZM (1 2 nan 3)"); - EXPECT_EQ(values[3], "POINT ZM (1 2 3 4)"); - - array_out.release(&array_out); -} - -TEST(BuilderTest, BuilderTestInterleavedPoint) { - struct GeoArrowBuilder builder; - struct GeoArrowVisitor v; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, GEOARROW_TYPE_INTERLEAVED_POINT), - GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); - - TestCoords coords({1}, {2}); - - // Valid - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Null - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Empty - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - struct ArrowArray array_out; - struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); - - EXPECT_EQ(array_out.length, 3); - EXPECT_EQ(array_out.null_count, 1); - - ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_INTERLEAVED_POINT), - GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - - WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); - - auto values = tester.WKTValues(""); - ASSERT_EQ(values.size(), 3); - EXPECT_EQ(values[0], "POINT (1 2)"); - EXPECT_EQ(values[1], ""); - EXPECT_EQ(values[2], "POINT (nan nan)"); - - array_out.release(&array_out); -} - -TEST(BuilderTest, BuilderTestMultipoint) { - struct GeoArrowBuilder builder; - struct GeoArrowVisitor v; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, GEOARROW_TYPE_MULTIPOINT), GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); - - TestCoords coords({1, 2, 3, 1}, {2, 3, 4, 2}); - - // Valid point - coords.view()->n_coords = 1; - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid linestring - coords.view()->n_coords = 4; - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid polygon - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid multilinestring - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ( - v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTILINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Null - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Empty - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - struct ArrowArray array_out; - struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); - - EXPECT_EQ(array_out.length, 6); - EXPECT_EQ(array_out.null_count, 1); - - ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_MULTIPOINT), - GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - - WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); - - auto values = tester.WKTValues(""); - ASSERT_EQ(values.size(), 6); - EXPECT_EQ(values[0], "MULTIPOINT ((1 2))"); - EXPECT_EQ(values[1], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); - EXPECT_EQ(values[2], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); - EXPECT_EQ(values[3], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); - EXPECT_EQ(values[4], ""); - EXPECT_EQ(values[5], "MULTIPOINT EMPTY"); - - array_out.release(&array_out); -} - -TEST(BuilderTest, BuilderTestInterleavedMultipoint) { - struct GeoArrowBuilder builder; - struct GeoArrowVisitor v; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, GEOARROW_TYPE_INTERLEAVED_MULTIPOINT), - GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); - - TestCoords coords({1, 2, 3, 1}, {2, 3, 4, 2}); - - // Valid point - coords.view()->n_coords = 1; - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid linestring - coords.view()->n_coords = 4; - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid polygon - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid multilinestring - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ( - v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTILINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Null - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Empty - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - struct ArrowArray array_out; - struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); - - EXPECT_EQ(array_out.length, 6); - EXPECT_EQ(array_out.null_count, 1); - - ASSERT_EQ( - GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_INTERLEAVED_MULTIPOINT), - GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - - WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); - - auto values = tester.WKTValues(""); - ASSERT_EQ(values.size(), 6); - EXPECT_EQ(values[0], "MULTIPOINT ((1 2))"); - EXPECT_EQ(values[1], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); - EXPECT_EQ(values[2], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); - EXPECT_EQ(values[3], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); - EXPECT_EQ(values[4], ""); - EXPECT_EQ(values[5], "MULTIPOINT EMPTY"); - - array_out.release(&array_out); -} - -TEST(BuilderTest, BuilderTestMultiLinestring) { - struct GeoArrowBuilder builder; - struct GeoArrowVisitor v; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, GEOARROW_TYPE_MULTILINESTRING), - GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); - - TestCoords coords({1, 2, 3, 1}, {2, 3, 4, 2}); - - // Valid linestring - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid polygon - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid multilinestring - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ( - v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTILINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid multipolygon - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Null - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Empty - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - struct ArrowArray array_out; - struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); - - EXPECT_EQ(array_out.length, 6); - EXPECT_EQ(array_out.null_count, 1); - - ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_MULTILINESTRING), - GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - - WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); - - auto values = tester.WKTValues(""); - ASSERT_EQ(values.size(), 6); - EXPECT_EQ(values[0], "MULTILINESTRING ((1 2, 2 3, 3 4, 1 2))"); - EXPECT_EQ(values[1], "MULTILINESTRING ((1 2, 2 3, 3 4, 1 2))"); - EXPECT_EQ(values[2], "MULTILINESTRING ((1 2, 2 3, 3 4, 1 2))"); - EXPECT_EQ(values[3], "MULTILINESTRING ((1 2, 2 3, 3 4, 1 2), (1 2, 2 3, 3 4, 1 2))"); - EXPECT_EQ(values[4], ""); - EXPECT_EQ(values[5], "MULTILINESTRING EMPTY"); - - array_out.release(&array_out); -} - -TEST(BuilderTest, BuilderTestMultiPolygon) { - struct GeoArrowBuilder builder; - struct GeoArrowVisitor v; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, GEOARROW_TYPE_MULTIPOLYGON), - GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); - - TestCoords coords({1, 2, 3, 1}, {2, 3, 4, 2}); - - // Valid linestring - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid polygon - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid multilinestring - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ( - v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTILINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Valid multipolygon - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); - EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); - EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Null - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - // Empty - EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); - EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), - GEOARROW_OK); - EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); - EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); - - struct ArrowArray array_out; - struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); - - EXPECT_EQ(array_out.length, 6); - EXPECT_EQ(array_out.null_count, 1); - - ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_MULTIPOLYGON), - GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - - WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); - - auto values = tester.WKTValues(""); - ASSERT_EQ(values.size(), 6); - EXPECT_EQ(values[0], "MULTIPOLYGON (((1 2, 2 3, 3 4, 1 2)))"); - EXPECT_EQ(values[1], "MULTIPOLYGON (((1 2, 2 3, 3 4, 1 2)))"); - EXPECT_EQ(values[2], "MULTIPOLYGON (((1 2, 2 3, 3 4, 1 2)))"); - EXPECT_EQ(values[3], "MULTIPOLYGON (((1 2, 2 3, 3 4, 1 2)), ((1 2, 2 3, 3 4, 1 2)))"); - EXPECT_EQ(values[4], ""); - EXPECT_EQ(values[5], "MULTIPOLYGON EMPTY"); - - array_out.release(&array_out); -} - -class WKTRoundtripParameterizedTestFixture - : public ::testing::TestWithParam> { - protected: - std::string wkt; -}; - -TEST_P(WKTRoundtripParameterizedTestFixture, BuilderTestWKTRoundtrip) { - // Initialize params - const std::string& wkt = GetParam().first; - struct GeoArrowStringView wkt_view; - wkt_view.data = wkt.data(); - wkt_view.size_bytes = wkt.size(); - enum GeoArrowType type = GetParam().second; - - // Initialize the builder - struct GeoArrowBuilder builder; - struct GeoArrowVisitor v; - struct GeoArrowError error; - v.error = &error; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, type), GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); - - // Visit the WKT item - struct GeoArrowWKTReader reader; - GeoArrowWKTReaderInit(&reader); - ASSERT_EQ(GeoArrowWKTReaderVisit(&reader, wkt_view, &v), GEOARROW_OK); - GeoArrowWKTReaderReset(&reader); - - // Finalize the output - struct ArrowArray array_out; - struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); - - // Validate it - ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, type), GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - - // Visit the output - WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); - - auto values = tester.WKTValues(""); - ASSERT_EQ(values.size(), 1); - EXPECT_EQ(values[0], wkt); - - array_out.release(&array_out); -} - -#define WKT_PAIR(a, b) std::pair { a, b } - -INSTANTIATE_TEST_SUITE_P( - BuilderTest, WKTRoundtripParameterizedTestFixture, - ::testing::Values( - // Point - WKT_PAIR("POINT (0 1)", GEOARROW_TYPE_POINT), - WKT_PAIR("POINT Z (0 1 2)", GEOARROW_TYPE_POINT_Z), - WKT_PAIR("POINT M (0 1 2)", GEOARROW_TYPE_POINT_M), - WKT_PAIR("POINT ZM (0 1 2 3)", GEOARROW_TYPE_POINT_ZM), - - // Interleaved Point - WKT_PAIR("POINT (0 1)", GEOARROW_TYPE_INTERLEAVED_POINT), - WKT_PAIR("POINT Z (0 1 2)", GEOARROW_TYPE_INTERLEAVED_POINT_Z), - WKT_PAIR("POINT M (0 1 2)", GEOARROW_TYPE_INTERLEAVED_POINT_M), - WKT_PAIR("POINT ZM (0 1 2 3)", GEOARROW_TYPE_INTERLEAVED_POINT_ZM), - - // Linestring - WKT_PAIR("LINESTRING EMPTY", GEOARROW_TYPE_LINESTRING), - WKT_PAIR("LINESTRING (30 10, 12 16)", GEOARROW_TYPE_LINESTRING), - WKT_PAIR("LINESTRING Z (30 10 11, 12 16 15)", GEOARROW_TYPE_LINESTRING_Z), - WKT_PAIR("LINESTRING M (30 10 11, 12 16 15)", GEOARROW_TYPE_LINESTRING_M), - WKT_PAIR("LINESTRING ZM (30 10 11 10, 12 16 15 98)", GEOARROW_TYPE_LINESTRING_ZM), - - // Interleaved Linestring - WKT_PAIR("LINESTRING EMPTY", GEOARROW_TYPE_INTERLEAVED_LINESTRING), - WKT_PAIR("LINESTRING (30 10, 12 16)", GEOARROW_TYPE_INTERLEAVED_LINESTRING), - WKT_PAIR("LINESTRING Z (30 10 11, 12 16 15)", - GEOARROW_TYPE_INTERLEAVED_LINESTRING_Z), - WKT_PAIR("LINESTRING M (30 10 11, 12 16 15)", - GEOARROW_TYPE_INTERLEAVED_LINESTRING_M), - WKT_PAIR("LINESTRING ZM (30 10 11 10, 12 16 15 98)", - GEOARROW_TYPE_INTERLEAVED_LINESTRING_ZM), - - // Polygon - WKT_PAIR("POLYGON EMPTY", GEOARROW_TYPE_POLYGON), - WKT_PAIR("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", GEOARROW_TYPE_POLYGON), - WKT_PAIR("POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 " - "20, 20 30))", - GEOARROW_TYPE_POLYGON), - - // Interleaved Polygon - WKT_PAIR("POLYGON EMPTY", GEOARROW_TYPE_INTERLEAVED_POLYGON), - WKT_PAIR("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", - GEOARROW_TYPE_INTERLEAVED_POLYGON), - WKT_PAIR("POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 " - "20, 20 30))", - GEOARROW_TYPE_INTERLEAVED_POLYGON), - - // Multipoint - WKT_PAIR("MULTIPOINT EMPTY", GEOARROW_TYPE_MULTIPOINT), - WKT_PAIR("MULTIPOINT ((30 10), (12 16))", GEOARROW_TYPE_MULTIPOINT), - - // Interleaved Multipoint - WKT_PAIR("MULTIPOINT EMPTY", GEOARROW_TYPE_INTERLEAVED_MULTIPOINT), - WKT_PAIR("MULTIPOINT ((30 10), (12 16))", GEOARROW_TYPE_INTERLEAVED_MULTIPOINT), - - // Multilinestring - WKT_PAIR("MULTILINESTRING EMPTY", GEOARROW_TYPE_MULTILINESTRING), - WKT_PAIR("MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))", - GEOARROW_TYPE_MULTILINESTRING), - - // Interleaved Multilinestring - WKT_PAIR("MULTILINESTRING EMPTY", GEOARROW_TYPE_INTERLEAVED_MULTILINESTRING), - WKT_PAIR("MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))", - GEOARROW_TYPE_INTERLEAVED_MULTILINESTRING), - - // Multipolygon - WKT_PAIR("MULTIPOLYGON EMPTY", GEOARROW_TYPE_MULTIPOLYGON), - WKT_PAIR("MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, " - "30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))", - GEOARROW_TYPE_MULTIPOLYGON), - - // Interleaved Multipolygon - WKT_PAIR("MULTIPOLYGON EMPTY", GEOARROW_TYPE_INTERLEAVED_MULTIPOLYGON), - WKT_PAIR("MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, " - "30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))", - GEOARROW_TYPE_INTERLEAVED_MULTIPOLYGON) - - // Comment to keep the last line on its own - )); - TEST(BuilderTest, BuilerTestSetBuffersBox) { struct GeoArrowBuilder builder; struct ArrowArray array_out; @@ -876,8 +193,9 @@ TEST(BuilderTest, BuilerTestSetBuffersBox) { EXPECT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); @@ -916,8 +234,9 @@ TEST(BuilderTest, BuilerTestSetBuffersPoint) { EXPECT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); @@ -961,8 +280,9 @@ TEST(BuilderTest, BuilderTestSetBuffersLinestring) { EXPECT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); @@ -1010,8 +330,9 @@ TEST(BuilderTest, BuilderTestSetBuffersPolygon) { EXPECT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); @@ -1055,8 +376,9 @@ TEST(BuilderTest, BuilderTestSetBuffersMultipoint) { EXPECT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); @@ -1105,8 +427,9 @@ TEST(ArrayTest, ArrayTestSetBuffersMultilinestring) { EXPECT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); @@ -1159,8 +482,9 @@ TEST(BuilderTest, BuilderTestSetBuffersMultipolygon) { EXPECT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); WKXTester tester; - EXPECT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); auto values = tester.WKTValues(""); ASSERT_EQ(values.size(), 3); diff --git a/src/geoarrow/geoarrow.h b/src/geoarrow/geoarrow.h index 87cae2da..edd8e2d6 100644 --- a/src/geoarrow/geoarrow.h +++ b/src/geoarrow/geoarrow.h @@ -298,17 +298,45 @@ GeoArrowErrorCode GeoArrowKernelInit(struct GeoArrowKernel* kernel, const char* /// \brief Initialize a GeoArrowVisitor with a visitor that does nothing void GeoArrowVisitorInitVoid(struct GeoArrowVisitor* v); -/// \brief Populate a GeoArrowVisitor pointing to a GeoArrowBuilder -GeoArrowErrorCode GeoArrowBuilderInitVisitor(struct GeoArrowBuilder* builder, - struct GeoArrowVisitor* v); - -/// \brief Visit the features of a GeoArrowArrayView +/// \brief Visit the features of a native GeoArrowArrayView /// /// The caller must have initialized the GeoArrowVisitor with the appropriate -/// writer before calling this function. -GeoArrowErrorCode GeoArrowArrayViewVisit(const struct GeoArrowArrayView* array_view, - int64_t offset, int64_t length, - struct GeoArrowVisitor* v); +/// writer before calling this function. This only works with GeoArrowArrayView +/// instances pointing to native arrays, even though the GeoArrowArrayView can +/// handle other types of arrays. Use the GeoArrowArrayReader for arbitrary input. +GeoArrowErrorCode GeoArrowArrayViewVisitNative(const struct GeoArrowArrayView* array_view, + int64_t offset, int64_t length, + struct GeoArrowVisitor* v); + +/// \brief GeoArrow native array writer +/// +/// This writer writes the "native" memory layouts (i.e., nested lists of +/// coordinates) implemented as a visitor. +struct GeoArrowNativeWriter { + /// \brief Implementation-specific details + void* private_data; +}; + +/// \brief Initialize the memory of a GeoArrowNativeWriter +/// +/// If GEOARROW_OK is returned, the caller is responsible for calling +/// GeoArrowNativeWriterReset(). +GeoArrowErrorCode GeoArrowNativeWriterInit(struct GeoArrowNativeWriter* writer, + enum GeoArrowType type); + +/// \brief Populate a GeoArrowVisitor pointing to this writer +GeoArrowErrorCode GeoArrowNativeWriterInitVisitor(struct GeoArrowNativeWriter* writer, + struct GeoArrowVisitor* v); + +/// \brief Finish an ArrowArray containing elements from the visited input +/// +/// This function can be called more than once to support multiple batches. +GeoArrowErrorCode GeoArrowNativeWriterFinish(struct GeoArrowNativeWriter* writer, + struct ArrowArray* array, + struct GeoArrowError* error); + +/// \brief Free resources held by a GeoArrowNativeWriter +void GeoArrowNativeWriterReset(struct GeoArrowNativeWriter* writer); /// \brief Well-known text writer /// @@ -515,13 +543,6 @@ GeoArrowErrorCode GeoArrowArrayWriterSetFlatMultipoint(struct GeoArrowArrayWrite GeoArrowErrorCode GeoArrowArrayWriterInitVisitor(struct GeoArrowArrayWriter* writer, struct GeoArrowVisitor* v); -/// \brief Get the underlying GeoArrowBuilder for the GeoArrowArrayWriter -/// -/// Returns a pointer to the builder underlying this GeoArrowArrayWriter if one exists -/// or returns an error code otherwise. -GeoArrowErrorCode GeoArrowArrayWriterBuilder(struct GeoArrowArrayWriter* writer, - struct GeoArrowBuilder** out); - /// \brief Finish an ArrowArray containing elements from the visited input /// /// This function can be called more than once to support multiple batches. diff --git a/src/geoarrow/hpp/array_writer.hpp b/src/geoarrow/hpp/array_writer.hpp index dc071bae..37bf1849 100644 --- a/src/geoarrow/hpp/array_writer.hpp +++ b/src/geoarrow/hpp/array_writer.hpp @@ -9,12 +9,28 @@ namespace geoarrow { class ArrayBuilder { public: - explicit ArrayBuilder(struct GeoArrowBuilder* builder = nullptr) : builder_(builder) {} - void Init(struct GeoArrowBuilder* builder) { builder_ = builder; } + explicit ArrayBuilder(GeoArrowType type) { + GEOARROW_THROW_NOT_OK(nullptr, GeoArrowBuilderInitFromType(&builder_, type)); + } + explicit ArrayBuilder(const GeometryDataType& type) : ArrayBuilder(type.id()) {} + explicit ArrayBuilder(const ArrowSchema* schema) { + struct GeoArrowError error {}; + GEOARROW_THROW_NOT_OK(&error, + GeoArrowBuilderInitFromSchema(&builder_, schema, &error)); + } - bool is_valid() { return builder_ != nullptr && builder_->private_data != nullptr; } + ~ArrayBuilder() { + if (builder_.private_data != nullptr) { + GeoArrowBuilderReset(&builder_); + } + } + + void Finish(struct ArrowArray* out) { + struct GeoArrowError error {}; + GEOARROW_THROW_NOT_OK(&error, GeoArrowBuilderFinish(&builder_, out, &error)); + } - struct GeoArrowBuilder* builder() { return builder_; } + struct GeoArrowBuilder* builder() { return &builder_; } template void SetBufferWrapped(int64_t i, T obj, GeoArrowBufferView value) { @@ -37,7 +53,7 @@ class ArrayBuilder { }; GeoArrowErrorCode result = GeoArrowBuilderSetOwnedBuffer( - builder_, i, value, &internal::FreeWrappedBuffer, obj_moved); + &builder_, i, value, &internal::FreeWrappedBuffer, obj_moved); if (result != GEOARROW_OK) { delete obj_moved; throw ::geoarrow::ErrnoException(result, "GeoArrowBuilderSetOwnedBuffer()", @@ -48,24 +64,24 @@ class ArrayBuilder { template void AppendToBuffer(int64_t i, const T& obj) { GEOARROW_THROW_NOT_OK( - nullptr, GeoArrowBuilderAppendBuffer(builder_, i, internal::BufferView(obj))); + nullptr, GeoArrowBuilderAppendBuffer(&builder_, i, internal::BufferView(obj))); } template void AppendToOffsetBuffer(int64_t i, const T& obj) { GEOARROW_THROW_NOT_OK(nullptr, - GeoArrowBuilderOffsetAppend(builder_, i, obj.data(), + GeoArrowBuilderOffsetAppend(&builder_, i, obj.data(), static_cast(obj.size()))); } void AppendCoords(const GeoArrowCoordView* coords, enum GeoArrowDimensions dimensions, int64_t offset, int64_t length) { GEOARROW_THROW_NOT_OK(nullptr, GeoArrowBuilderCoordsAppend( - builder_, coords, dimensions, offset, length)); + &builder_, coords, dimensions, offset, length)); } protected: - struct GeoArrowBuilder* builder_{}; + struct GeoArrowBuilder builder_ {}; }; class ArrayWriter { @@ -94,10 +110,6 @@ class ArrayWriter { } struct GeoArrowVisitor* visitor() { - if (builder_.is_valid()) { - throw Exception("Can't use GeoArrowVisitor with ArrayBuilder in ArrayWriter"); - } - if (visitor_.coords == nullptr) { GEOARROW_THROW_NOT_OK(nullptr, GeoArrowArrayWriterInitVisitor(&writer_, &visitor_)); } @@ -105,20 +117,6 @@ class ArrayWriter { return &visitor_; } - ArrayBuilder& builder() { - if (visitor_.coords != nullptr) { - throw Exception("Can't use ArrayBuilder with GeoArrowVisitor in ArrayWriter"); - } - - if (!builder_.is_valid()) { - struct GeoArrowBuilder* builder = nullptr; - GEOARROW_THROW_NOT_OK(nullptr, GeoArrowArrayWriterBuilder(&writer_, &builder)); - builder_.Init(builder); - } - - return builder_; - } - void Finish(struct ArrowArray* out) { struct GeoArrowError error {}; GEOARROW_THROW_NOT_OK(&error, GeoArrowArrayWriterFinish(&writer_, out, &error)); @@ -126,7 +124,6 @@ class ArrayWriter { private: struct GeoArrowArrayWriter writer_ {}; - ArrayBuilder builder_; struct GeoArrowVisitor visitor_ {}; }; diff --git a/src/geoarrow/hpp/array_writer_test.cc b/src/geoarrow/hpp/array_writer_test.cc index 124580ca..e93fa445 100644 --- a/src/geoarrow/hpp/array_writer_test.cc +++ b/src/geoarrow/hpp/array_writer_test.cc @@ -10,24 +10,21 @@ #include "wkx_testing.hpp" TEST(GeoArrowHppTest, ArrayWriterByBuffer) { - geoarrow::ArrayWriter writer(geoarrow::Point()); + geoarrow::ArrayBuilder writer(geoarrow::Point()); // Check SetBufferWrapped() overload with a movable C++ object + arbitrary buffer view // (make it long enough that it keeps the same allocation when moved) std::vector validity(1024); std::memset(validity.data(), 0, validity.size()); validity[0] = 0b00001111; - writer.builder().SetBufferWrapped(0, std::move(validity)); + writer.SetBufferWrapped(0, std::move(validity)); // Check SetBufferWrapped() overload with a sequence - writer.builder().SetBufferWrapped(1, std::vector{0, 1, 2, 3}); + writer.SetBufferWrapped(1, std::vector{0, 1, 2, 3}); // Check buffer appender std::vector ys{4, 5, 6, 7}; - writer.builder().AppendToBuffer(2, ys); - - // Make sure we can't also use the visitor - EXPECT_THROW(writer.visitor(), geoarrow::Exception); + writer.AppendToBuffer(2, ys); struct ArrowArray array; writer.Finish(&array); @@ -45,9 +42,9 @@ TEST(GeoArrowHppTest, ArrayWriterByBuffer) { TEST(GeoArrowHppTest, ArrayWriterByOffsetAndCoords) { TestCoords coords({0, 1, 2, 3, 4}, {5, 6, 7, 8, 9}); - geoarrow::ArrayWriter writer(geoarrow::Linestring()); - writer.builder().AppendToOffsetBuffer(0, std::vector{0, 2, 5}); - writer.builder().AppendCoords(coords.view(), GEOARROW_DIMENSIONS_XY, 0, 5); + geoarrow::ArrayBuilder writer(geoarrow::Linestring()); + writer.AppendToOffsetBuffer(0, std::vector{0, 2, 5}); + writer.AppendCoords(coords.view(), GEOARROW_DIMENSIONS_XY, 0, 5); struct ArrowArray array; writer.Finish(&array); @@ -68,8 +65,6 @@ TEST(GeoArrowHppTest, ArrayWriterByVisitor) { geoarrow::ArrayWriter writer(geoarrow::Point()); tester.ReadWKT("POINT (0 4)", writer.visitor()); - EXPECT_THROW(writer.builder(), geoarrow::Exception); - struct ArrowArray array; writer.Finish(&array); ASSERT_EQ(array.length, 1); diff --git a/src/geoarrow/native_writer.c b/src/geoarrow/native_writer.c new file mode 100644 index 00000000..4c7bccc5 --- /dev/null +++ b/src/geoarrow/native_writer.c @@ -0,0 +1,757 @@ + +#include + +#include "nanoarrow/nanoarrow.h" + +#include "geoarrow.h" + +// Bytes for four quiet (little-endian) NANs +static uint8_t kEmptyPointCoords[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f}; + +struct GeoArrowNativeWriterPrivate { + struct GeoArrowBuilder builder; + + struct ArrowBitmap validity; + int64_t null_count; + + // Fields to keep track of state + int output_initialized; + int feat_is_null; + int nesting_multipoint; + double empty_coord_values[4]; + struct GeoArrowCoordView empty_coord; + enum GeoArrowDimensions last_dimensions; + int64_t size[32]; + int32_t level; +}; + +GeoArrowErrorCode GeoArrowNativeWriterInit(struct GeoArrowNativeWriter* writer, + enum GeoArrowType type) { + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)ArrowMalloc( + sizeof(struct GeoArrowNativeWriterPrivate)); + if (private_data == NULL) { + return ENOMEM; + } + + memset(private_data, 0, sizeof(struct GeoArrowNativeWriterPrivate)); + + GeoArrowErrorCode result = GeoArrowBuilderInitFromType(&private_data->builder, type); + if (result != GEOARROW_OK) { + ArrowFree(private_data); + return result; + } + + ArrowBitmapInit(&private_data->validity); + + // Initialize one empty coordinate + memcpy(private_data->empty_coord_values, kEmptyPointCoords, 4 * sizeof(double)); + private_data->empty_coord.values[0] = private_data->empty_coord_values; + private_data->empty_coord.values[1] = private_data->empty_coord_values + 1; + private_data->empty_coord.values[2] = private_data->empty_coord_values + 2; + private_data->empty_coord.values[3] = private_data->empty_coord_values + 3; + private_data->empty_coord.n_coords = 1; + private_data->empty_coord.n_values = 4; + private_data->empty_coord.coords_stride = 1; + + writer->private_data = private_data; + return GEOARROW_OK; +} + +void GeoArrowNativeWriterReset(struct GeoArrowNativeWriter* writer) { + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + GeoArrowBuilderReset(&private_data->builder); + ArrowBitmapReset(&private_data->validity); + ArrowFree(private_data); +} + +static GeoArrowErrorCode GeoArrowNativeWriterEnsureOutputInitialized( + struct GeoArrowNativeWriter* writer) { + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + if (private_data->output_initialized) { + return GEOARROW_OK; + } + + struct GeoArrowBuilder* builder = &private_data->builder; + + int32_t zero = 0; + for (int i = 0; i < private_data->builder.view.n_offsets; i++) { + NANOARROW_RETURN_NOT_OK(GeoArrowBuilderOffsetAppend(builder, i, &zero, 1)); + } + + private_data->null_count = 0; + NANOARROW_RETURN_NOT_OK(ArrowBitmapResize(&private_data->validity, 0, 0)); + + private_data->builder.view.coords.size_coords = 0; + private_data->builder.view.coords.capacity_coords = 0; + + private_data->output_initialized = 1; + return GEOARROW_OK; +} + +GeoArrowErrorCode GeoArrowNativeWriterFinish(struct GeoArrowNativeWriter* writer, + struct ArrowArray* array, + struct GeoArrowError* error) { + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + // We could in theory wrap this buffer instead of copy it + struct GeoArrowBufferView validity_view; + validity_view.data = private_data->validity.buffer.data; + validity_view.size_bytes = private_data->validity.buffer.size_bytes; + if (validity_view.size_bytes > 0) { + GEOARROW_RETURN_NOT_OK( + GeoArrowBuilderAppendBuffer(&private_data->builder, 0, validity_view)); + } + + struct ArrowArray tmp; + GEOARROW_RETURN_NOT_OK(GeoArrowBuilderFinish(&private_data->builder, &tmp, error)); + tmp.null_count = private_data->null_count; + + private_data->output_initialized = 0; + + GeoArrowErrorCode result = GeoArrowNativeWriterEnsureOutputInitialized(writer); + if (result != GEOARROW_OK) { + ArrowArrayRelease(&tmp); + GeoArrowErrorSet(error, "Failed to reinitialize writer"); + return result; + } + + ArrowArrayMove(&tmp, array); + return GEOARROW_OK; +} + +static int feat_start_point(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->level = 0; + private_data->size[0] = 0; + private_data->feat_is_null = 0; + return GEOARROW_OK; +} + +static int geom_start_point(struct GeoArrowVisitor* v, + enum GeoArrowGeometryType geometry_type, + enum GeoArrowDimensions dimensions) { + NANOARROW_UNUSED(geometry_type); + + // level++, geometry type, dimensions, reset size + // validate dimensions, maybe against some options that indicate + // error for mismatch, fill, or drop behaviour + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->last_dimensions = dimensions; + return GEOARROW_OK; +} + +static int ring_start_point(struct GeoArrowVisitor* v) { + NANOARROW_UNUSED(v); + return GEOARROW_OK; +} + +static int coords_point(struct GeoArrowVisitor* v, + const struct GeoArrowCoordView* coords) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->size[0] += coords->n_coords; + return GeoArrowBuilderCoordsAppend(&private_data->builder, coords, + private_data->last_dimensions, 0, coords->n_coords); +} + +static int ring_end_point(struct GeoArrowVisitor* v) { + NANOARROW_UNUSED(v); + return GEOARROW_OK; +} + +static int geom_end_point(struct GeoArrowVisitor* v) { + NANOARROW_UNUSED(v); + return GEOARROW_OK; +} + +static int null_feat_point(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->feat_is_null = 1; + return GEOARROW_OK; +} + +static int feat_end_point(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + // If there weren't any coords (i.e., EMPTY), we need to write some NANs here + // if there was >1 coords, we also need to error or we'll get misaligned output + if (private_data->size[0] == 0) { + int n_dim = + _GeoArrowkNumDimensions[private_data->builder.view.schema_view.dimensions]; + private_data->empty_coord.n_values = n_dim; + NANOARROW_RETURN_NOT_OK(coords_point(v, &private_data->empty_coord)); + } else if (private_data->size[0] != 1) { + GeoArrowErrorSet(v->error, "Can't convert feature with >1 coordinate to POINT"); + return EINVAL; + } + + if (private_data->feat_is_null) { + int64_t current_length = private_data->builder.view.coords.size_coords; + if (private_data->validity.buffer.data == NULL) { + NANOARROW_RETURN_NOT_OK( + ArrowBitmapReserve(&private_data->validity, current_length)); + ArrowBitmapAppendUnsafe(&private_data->validity, 1, current_length - 1); + } + + private_data->null_count++; + NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(&private_data->validity, 0, 1)); + } else if (private_data->validity.buffer.data != NULL) { + NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(&private_data->validity, 1, 1)); + } + + return GEOARROW_OK; +} + +static void GeoArrowVisitorInitPoint(struct GeoArrowVisitor* v) { + struct GeoArrowError* previous_error = v->error; + GeoArrowVisitorInitVoid(v); + v->error = previous_error; + + v->feat_start = &feat_start_point; + v->null_feat = &null_feat_point; + v->geom_start = &geom_start_point; + v->ring_start = &ring_start_point; + v->coords = &coords_point; + v->ring_end = &ring_end_point; + v->geom_end = &geom_end_point; + v->feat_end = &feat_end_point; +} + +static int feat_start_multipoint(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->level = 0; + private_data->size[0] = 0; + private_data->size[1] = 0; + private_data->feat_is_null = 0; + private_data->nesting_multipoint = 0; + return GEOARROW_OK; +} + +static int geom_start_multipoint(struct GeoArrowVisitor* v, + enum GeoArrowGeometryType geometry_type, + enum GeoArrowDimensions dimensions) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->last_dimensions = dimensions; + + switch (geometry_type) { + case GEOARROW_GEOMETRY_TYPE_LINESTRING: + private_data->level++; + break; + case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: + private_data->nesting_multipoint = 1; + private_data->level++; + break; + case GEOARROW_GEOMETRY_TYPE_POINT: + if (private_data->nesting_multipoint) { + private_data->nesting_multipoint++; + } + default: + break; + } + + return GEOARROW_OK; +} + +static int ring_start_multipoint(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->level++; + return GEOARROW_OK; +} + +static int coords_multipoint(struct GeoArrowVisitor* v, + const struct GeoArrowCoordView* coords) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->size[1] += coords->n_coords; + return GeoArrowBuilderCoordsAppend(&private_data->builder, coords, + private_data->last_dimensions, 0, coords->n_coords); +} + +static int ring_end_multipoint(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + private_data->level--; + private_data->size[0]++; + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 0, &n_coord32, 1)); + + return GEOARROW_OK; +} + +static int geom_end_multipoint(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + // Ignore geom_end calls from the end of a POINT nested within a MULTIPOINT + if (private_data->nesting_multipoint == 2) { + private_data->nesting_multipoint--; + return GEOARROW_OK; + } + + if (private_data->level == 1) { + private_data->size[0]++; + private_data->level--; + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 0, &n_coord32, 1)); + } + + return GEOARROW_OK; +} + +static int null_feat_multipoint(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->feat_is_null = 1; + return GEOARROW_OK; +} + +static int feat_end_multipoint(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + // If we didn't finish any sequences, finish at least one. This is usually an + // EMPTY but could also be a single point. + if (private_data->size[0] == 0) { + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 0, &n_coord32, 1)); + } else if (private_data->size[0] != 1) { + GeoArrowErrorSet(v->error, "Can't convert feature with >1 sequence to LINESTRING"); + return EINVAL; + } + + if (private_data->feat_is_null) { + int64_t current_length = + private_data->builder.view.buffers[1].size_bytes / sizeof(int32_t) - 1; + if (private_data->validity.buffer.data == NULL) { + NANOARROW_RETURN_NOT_OK( + ArrowBitmapReserve(&private_data->validity, current_length)); + ArrowBitmapAppendUnsafe(&private_data->validity, 1, current_length - 1); + } + + private_data->null_count++; + NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(&private_data->validity, 0, 1)); + } else if (private_data->validity.buffer.data != NULL) { + NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(&private_data->validity, 1, 1)); + } + + return GEOARROW_OK; +} + +static void GeoArrowVisitorInitLinestring(struct GeoArrowVisitor* v) { + struct GeoArrowError* previous_error = v->error; + GeoArrowVisitorInitVoid(v); + v->error = previous_error; + + v->feat_start = &feat_start_multipoint; + v->null_feat = &null_feat_multipoint; + v->geom_start = &geom_start_multipoint; + v->ring_start = &ring_start_multipoint; + v->coords = &coords_multipoint; + v->ring_end = &ring_end_multipoint; + v->geom_end = &geom_end_multipoint; + v->feat_end = &feat_end_multipoint; +} + +static int feat_start_multilinestring(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->level = 0; + private_data->size[0] = 0; + private_data->size[1] = 0; + private_data->feat_is_null = 0; + return GEOARROW_OK; +} + +static int geom_start_multilinestring(struct GeoArrowVisitor* v, + enum GeoArrowGeometryType geometry_type, + enum GeoArrowDimensions dimensions) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->last_dimensions = dimensions; + + switch (geometry_type) { + case GEOARROW_GEOMETRY_TYPE_LINESTRING: + case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: + private_data->level++; + break; + default: + break; + } + + return GEOARROW_OK; +} + +static int ring_start_multilinestring(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->level++; + return GEOARROW_OK; +} + +static int coords_multilinestring(struct GeoArrowVisitor* v, + const struct GeoArrowCoordView* coords) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->size[1] += coords->n_coords; + return GeoArrowBuilderCoordsAppend(&private_data->builder, coords, + private_data->last_dimensions, 0, coords->n_coords); +} + +static int ring_end_multilinestring(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + private_data->level--; + if (private_data->size[1] > 0) { + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 1, &n_coord32, 1)); + private_data->size[0]++; + private_data->size[1] = 0; + } + + return GEOARROW_OK; +} + +static int geom_end_multilinestring(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + if (private_data->level == 1) { + private_data->level--; + if (private_data->size[1] > 0) { + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 1, &n_coord32, 1)); + private_data->size[0]++; + private_data->size[1] = 0; + } + } + + return GEOARROW_OK; +} + +static int null_feat_multilinestring(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->feat_is_null = 1; + return GEOARROW_OK; +} + +static int feat_end_multilinestring(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + // If we have an unfinished sequence left over, finish it now. This could have + // occurred if the last geometry that was visited was a POINT. + if (private_data->size[1] > 0) { + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 1, &n_coord32, 1)); + } + + // Finish off the sequence of sequences. This is a polygon or multilinestring + // so it can any number of them. + int32_t n_seq32 = + (int32_t)(private_data->builder.view.buffers[2].size_bytes / sizeof(int32_t)) - 1; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 0, &n_seq32, 1)); + + if (private_data->feat_is_null) { + int64_t current_length = + private_data->builder.view.buffers[1].size_bytes / sizeof(int32_t) - 1; + if (private_data->validity.buffer.data == NULL) { + NANOARROW_RETURN_NOT_OK( + ArrowBitmapReserve(&private_data->validity, current_length)); + ArrowBitmapAppendUnsafe(&private_data->validity, 1, current_length - 1); + } + + private_data->null_count++; + NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(&private_data->validity, 0, 1)); + } else if (private_data->validity.buffer.data != NULL) { + NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(&private_data->validity, 1, 1)); + } + + return GEOARROW_OK; +} + +static void GeoArrowVisitorInitMultiLinestring(struct GeoArrowVisitor* v) { + struct GeoArrowError* previous_error = v->error; + GeoArrowVisitorInitVoid(v); + v->error = previous_error; + + v->feat_start = &feat_start_multilinestring; + v->null_feat = &null_feat_multilinestring; + v->geom_start = &geom_start_multilinestring; + v->ring_start = &ring_start_multilinestring; + v->coords = &coords_multilinestring; + v->ring_end = &ring_end_multilinestring; + v->geom_end = &geom_end_multilinestring; + v->feat_end = &feat_end_multilinestring; +} + +static int feat_start_multipolygon(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->level = 0; + private_data->size[0] = 0; + private_data->size[1] = 0; + private_data->size[2] = 0; + private_data->feat_is_null = 0; + return GEOARROW_OK; +} + +static int geom_start_multipolygon(struct GeoArrowVisitor* v, + enum GeoArrowGeometryType geometry_type, + enum GeoArrowDimensions dimensions) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->last_dimensions = dimensions; + + switch (geometry_type) { + case GEOARROW_GEOMETRY_TYPE_MULTILINESTRING: + case GEOARROW_GEOMETRY_TYPE_POLYGON: + case GEOARROW_GEOMETRY_TYPE_LINESTRING: + case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: + private_data->level++; + break; + default: + break; + } + + return GEOARROW_OK; +} + +static int ring_start_multipolygon(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->level++; + return GEOARROW_OK; +} + +static int coords_multipolygon(struct GeoArrowVisitor* v, + const struct GeoArrowCoordView* coords) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->size[2] += coords->n_coords; + return GeoArrowBuilderCoordsAppend(&private_data->builder, coords, + private_data->last_dimensions, 0, coords->n_coords); +} + +static int ring_end_multipolygon(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + private_data->level--; + if (private_data->size[2] > 0) { + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 2, &n_coord32, 1)); + private_data->size[1]++; + private_data->size[2] = 0; + } + + return GEOARROW_OK; +} + +static int geom_end_multipolygon(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + if (private_data->level == 2) { + private_data->level--; + if (private_data->size[2] > 0) { + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 2, &n_coord32, 1)); + private_data->size[1]++; + private_data->size[2] = 0; + } + } else if (private_data->level == 1) { + private_data->level--; + if (private_data->size[1] > 0) { + int32_t n_seq32 = + (int32_t)(private_data->builder.view.buffers[3].size_bytes / sizeof(int32_t)) - + 1; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 1, &n_seq32, 1)); + private_data->size[0]++; + private_data->size[1] = 0; + } + } + + return GEOARROW_OK; +} + +static int null_feat_multipolygon(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + private_data->feat_is_null = 1; + return GEOARROW_OK; +} + +static int feat_end_multipolygon(struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriter* writer = (struct GeoArrowNativeWriter*)v->private_data; + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + // If we have an unfinished sequence left over, finish it now. This could have + // occurred if the last geometry that was visited was a POINT. + if (private_data->size[2] > 0) { + if (private_data->builder.view.coords.size_coords > 2147483647) { + return EOVERFLOW; + } + int32_t n_coord32 = (int32_t)private_data->builder.view.coords.size_coords; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 2, &n_coord32, 1)); + private_data->size[1]++; + } + + // If we have an unfinished sequence of sequences left over, finish it now. + // This could have occurred if the last geometry that was visited was a POINT. + if (private_data->size[1] > 0) { + int32_t n_seq32 = + (int32_t)(private_data->builder.view.buffers[3].size_bytes / sizeof(int32_t)) - 1; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 1, &n_seq32, 1)); + } + + // Finish off the sequence of sequence of sequences. This is a multipolygon + // so it can be any number of them. + int32_t n_seq_seq32 = + (int32_t)(private_data->builder.view.buffers[2].size_bytes / sizeof(int32_t)) - 1; + NANOARROW_RETURN_NOT_OK( + GeoArrowBuilderOffsetAppend(&private_data->builder, 0, &n_seq_seq32, 1)); + + if (private_data->feat_is_null) { + int64_t current_length = + private_data->builder.view.buffers[1].size_bytes / sizeof(int32_t) - 1; + if (private_data->validity.buffer.data == NULL) { + NANOARROW_RETURN_NOT_OK( + ArrowBitmapReserve(&private_data->validity, current_length)); + ArrowBitmapAppendUnsafe(&private_data->validity, 1, current_length - 1); + } + + private_data->null_count++; + NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(&private_data->validity, 0, 1)); + } else if (private_data->validity.buffer.data != NULL) { + NANOARROW_RETURN_NOT_OK(ArrowBitmapAppend(&private_data->validity, 1, 1)); + } + + return GEOARROW_OK; +} + +static void GeoArrowVisitorInitMultiPolygon(struct GeoArrowVisitor* v) { + struct GeoArrowError* previous_error = v->error; + GeoArrowVisitorInitVoid(v); + v->error = previous_error; + + v->feat_start = &feat_start_multipolygon; + v->null_feat = &null_feat_multipolygon; + v->geom_start = &geom_start_multipolygon; + v->ring_start = &ring_start_multipolygon; + v->coords = &coords_multipolygon; + v->ring_end = &ring_end_multipolygon; + v->geom_end = &geom_end_multipolygon; + v->feat_end = &feat_end_multipolygon; +} + +GeoArrowErrorCode GeoArrowNativeWriterInitVisitor(struct GeoArrowNativeWriter* writer, + struct GeoArrowVisitor* v) { + struct GeoArrowNativeWriterPrivate* private_data = + (struct GeoArrowNativeWriterPrivate*)writer->private_data; + + switch (private_data->builder.view.schema_view.geometry_type) { + case GEOARROW_GEOMETRY_TYPE_POINT: + GeoArrowVisitorInitPoint(v); + break; + case GEOARROW_GEOMETRY_TYPE_MULTIPOINT: + case GEOARROW_GEOMETRY_TYPE_LINESTRING: + GeoArrowVisitorInitLinestring(v); + break; + case GEOARROW_GEOMETRY_TYPE_MULTILINESTRING: + case GEOARROW_GEOMETRY_TYPE_POLYGON: + GeoArrowVisitorInitMultiLinestring(v); + break; + case GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON: + GeoArrowVisitorInitMultiPolygon(v); + break; + default: + return EINVAL; + } + + NANOARROW_RETURN_NOT_OK(GeoArrowNativeWriterEnsureOutputInitialized(writer)); + v->private_data = writer; + return GEOARROW_OK; +} diff --git a/src/geoarrow/native_writer_test.cc b/src/geoarrow/native_writer_test.cc new file mode 100644 index 00000000..37af74e6 --- /dev/null +++ b/src/geoarrow/native_writer_test.cc @@ -0,0 +1,631 @@ + +#include + +#include +#include "nanoarrow/nanoarrow.h" + +#include "wkx_testing.hpp" + +TEST(NativeWriterTest, WritePoint) { + struct GeoArrowNativeWriter builder; + struct GeoArrowVisitor v; + ASSERT_EQ(GeoArrowNativeWriterInit(&builder, GEOARROW_TYPE_POINT), GEOARROW_OK); + GeoArrowNativeWriterInitVisitor(&builder, &v); + + TestCoords coords({1}, {2}); + + // Valid + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Null + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Empty + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + struct ArrowArray array_out; + struct GeoArrowArrayView array_view; + EXPECT_EQ(GeoArrowNativeWriterFinish(&builder, &array_out, nullptr), GEOARROW_OK); + GeoArrowNativeWriterReset(&builder); + + EXPECT_EQ(array_out.length, 3); + EXPECT_EQ(array_out.null_count, 1); + + ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_POINT), GEOARROW_OK); + ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); + + WKXTester tester; + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); + + auto values = tester.WKTValues(""); + ASSERT_EQ(values.size(), 3); + EXPECT_EQ(values[0], "POINT (1 2)"); + EXPECT_EQ(values[1], ""); + EXPECT_EQ(values[2], "POINT (nan nan)"); + + ArrowArrayRelease(&array_out); +} + +TEST(NativeWriterTest, WriteInterleavedPoint) { + struct GeoArrowNativeWriter builder; + struct GeoArrowVisitor v; + ASSERT_EQ(GeoArrowNativeWriterInit(&builder, GEOARROW_TYPE_INTERLEAVED_POINT), + GEOARROW_OK); + GeoArrowNativeWriterInitVisitor(&builder, &v); + + TestCoords coords({1}, {2}); + + // Valid + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Null + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Empty + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + struct ArrowArray array_out; + struct GeoArrowArrayView array_view; + EXPECT_EQ(GeoArrowNativeWriterFinish(&builder, &array_out, nullptr), GEOARROW_OK); + GeoArrowNativeWriterReset(&builder); + + EXPECT_EQ(array_out.length, 3); + EXPECT_EQ(array_out.null_count, 1); + + ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_INTERLEAVED_POINT), + GEOARROW_OK); + ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); + + WKXTester tester; + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); + + auto values = tester.WKTValues(""); + ASSERT_EQ(values.size(), 3); + EXPECT_EQ(values[0], "POINT (1 2)"); + EXPECT_EQ(values[1], ""); + EXPECT_EQ(values[2], "POINT (nan nan)"); + + array_out.release(&array_out); +} + +TEST(NativeWriterTest, WriteMultipoint) { + struct GeoArrowNativeWriter builder; + struct GeoArrowVisitor v; + ASSERT_EQ(GeoArrowNativeWriterInit(&builder, GEOARROW_TYPE_MULTIPOINT), GEOARROW_OK); + GeoArrowNativeWriterInitVisitor(&builder, &v); + + TestCoords coords({1, 2, 3, 1}, {2, 3, 4, 2}); + + // Valid point + coords.view()->n_coords = 1; + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid linestring + coords.view()->n_coords = 4; + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid polygon + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid multilinestring + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ( + v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTILINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Null + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Empty + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + struct ArrowArray array_out; + struct GeoArrowArrayView array_view; + EXPECT_EQ(GeoArrowNativeWriterFinish(&builder, &array_out, nullptr), GEOARROW_OK); + GeoArrowNativeWriterReset(&builder); + + EXPECT_EQ(array_out.length, 6); + EXPECT_EQ(array_out.null_count, 1); + + ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_MULTIPOINT), + GEOARROW_OK); + ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); + + WKXTester tester; + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); + + auto values = tester.WKTValues(""); + ASSERT_EQ(values.size(), 6); + EXPECT_EQ(values[0], "MULTIPOINT ((1 2))"); + EXPECT_EQ(values[1], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); + EXPECT_EQ(values[2], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); + EXPECT_EQ(values[3], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); + EXPECT_EQ(values[4], ""); + EXPECT_EQ(values[5], "MULTIPOINT EMPTY"); + + array_out.release(&array_out); +} + +TEST(NativeWriterTest, WriteInterleavedMultipoint) { + struct GeoArrowNativeWriter builder; + struct GeoArrowVisitor v; + ASSERT_EQ(GeoArrowNativeWriterInit(&builder, GEOARROW_TYPE_INTERLEAVED_MULTIPOINT), + GEOARROW_OK); + GeoArrowNativeWriterInitVisitor(&builder, &v); + + TestCoords coords({1, 2, 3, 1}, {2, 3, 4, 2}); + + // Valid point + coords.view()->n_coords = 1; + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POINT, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid linestring + coords.view()->n_coords = 4; + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid polygon + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid multilinestring + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ( + v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTILINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Null + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Empty + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + struct ArrowArray array_out; + struct GeoArrowArrayView array_view; + EXPECT_EQ(GeoArrowNativeWriterFinish(&builder, &array_out, nullptr), GEOARROW_OK); + GeoArrowNativeWriterReset(&builder); + + EXPECT_EQ(array_out.length, 6); + EXPECT_EQ(array_out.null_count, 1); + + ASSERT_EQ( + GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_INTERLEAVED_MULTIPOINT), + GEOARROW_OK); + ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); + + WKXTester tester; + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); + + auto values = tester.WKTValues(""); + ASSERT_EQ(values.size(), 6); + EXPECT_EQ(values[0], "MULTIPOINT ((1 2))"); + EXPECT_EQ(values[1], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); + EXPECT_EQ(values[2], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); + EXPECT_EQ(values[3], "MULTIPOINT ((1 2), (2 3), (3 4), (1 2))"); + EXPECT_EQ(values[4], ""); + EXPECT_EQ(values[5], "MULTIPOINT EMPTY"); + + array_out.release(&array_out); +} + +TEST(NativeWriterTest, WriteMultiLinestring) { + struct GeoArrowNativeWriter builder; + struct GeoArrowVisitor v; + ASSERT_EQ(GeoArrowNativeWriterInit(&builder, GEOARROW_TYPE_MULTILINESTRING), + GEOARROW_OK); + GeoArrowNativeWriterInitVisitor(&builder, &v); + + TestCoords coords({1, 2, 3, 1}, {2, 3, 4, 2}); + + // Valid linestring + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid polygon + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid multilinestring + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ( + v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTILINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid multipolygon + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Null + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Empty + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + struct ArrowArray array_out; + struct GeoArrowArrayView array_view; + EXPECT_EQ(GeoArrowNativeWriterFinish(&builder, &array_out, nullptr), GEOARROW_OK); + GeoArrowNativeWriterReset(&builder); + + EXPECT_EQ(array_out.length, 6); + EXPECT_EQ(array_out.null_count, 1); + + ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_MULTILINESTRING), + GEOARROW_OK); + ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); + + WKXTester tester; + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); + + auto values = tester.WKTValues(""); + ASSERT_EQ(values.size(), 6); + EXPECT_EQ(values[0], "MULTILINESTRING ((1 2, 2 3, 3 4, 1 2))"); + EXPECT_EQ(values[1], "MULTILINESTRING ((1 2, 2 3, 3 4, 1 2))"); + EXPECT_EQ(values[2], "MULTILINESTRING ((1 2, 2 3, 3 4, 1 2))"); + EXPECT_EQ(values[3], "MULTILINESTRING ((1 2, 2 3, 3 4, 1 2), (1 2, 2 3, 3 4, 1 2))"); + EXPECT_EQ(values[4], ""); + EXPECT_EQ(values[5], "MULTILINESTRING EMPTY"); + + array_out.release(&array_out); +} + +TEST(NativeWriterTest, WriteMultiPolygon) { + struct GeoArrowNativeWriter builder; + struct GeoArrowVisitor v; + ASSERT_EQ(GeoArrowNativeWriterInit(&builder, GEOARROW_TYPE_MULTIPOLYGON), GEOARROW_OK); + GeoArrowNativeWriterInitVisitor(&builder, &v); + + TestCoords coords({1, 2, 3, 1}, {2, 3, 4, 2}); + + // Valid linestring + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid polygon + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid multilinestring + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ( + v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTILINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Valid multipolygon + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_MULTIPOLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_POLYGON, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.ring_start(&v), GEOARROW_OK); + EXPECT_EQ(v.coords(&v, coords.view()), GEOARROW_OK); + EXPECT_EQ(v.ring_end(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Null + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.null_feat(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + // Empty + EXPECT_EQ(v.feat_start(&v), GEOARROW_OK); + EXPECT_EQ(v.geom_start(&v, GEOARROW_GEOMETRY_TYPE_LINESTRING, GEOARROW_DIMENSIONS_XY), + GEOARROW_OK); + EXPECT_EQ(v.geom_end(&v), GEOARROW_OK); + EXPECT_EQ(v.feat_end(&v), GEOARROW_OK); + + struct ArrowArray array_out; + struct GeoArrowArrayView array_view; + EXPECT_EQ(GeoArrowNativeWriterFinish(&builder, &array_out, nullptr), GEOARROW_OK); + GeoArrowNativeWriterReset(&builder); + + EXPECT_EQ(array_out.length, 6); + EXPECT_EQ(array_out.null_count, 1); + + ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_MULTIPOLYGON), + GEOARROW_OK); + ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); + + WKXTester tester; + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); + + auto values = tester.WKTValues(""); + ASSERT_EQ(values.size(), 6); + EXPECT_EQ(values[0], "MULTIPOLYGON (((1 2, 2 3, 3 4, 1 2)))"); + EXPECT_EQ(values[1], "MULTIPOLYGON (((1 2, 2 3, 3 4, 1 2)))"); + EXPECT_EQ(values[2], "MULTIPOLYGON (((1 2, 2 3, 3 4, 1 2)))"); + EXPECT_EQ(values[3], "MULTIPOLYGON (((1 2, 2 3, 3 4, 1 2)), ((1 2, 2 3, 3 4, 1 2)))"); + EXPECT_EQ(values[4], ""); + EXPECT_EQ(values[5], "MULTIPOLYGON EMPTY"); + + array_out.release(&array_out); +} + +class WKTRoundtripParameterizedTestFixture + : public ::testing::TestWithParam> { + protected: + std::string wkt; +}; + +TEST_P(WKTRoundtripParameterizedTestFixture, NativeWriterWKTRoundtrip) { + // Initialize params + const std::string& wkt = GetParam().first; + struct GeoArrowStringView wkt_view; + wkt_view.data = wkt.data(); + wkt_view.size_bytes = wkt.size(); + enum GeoArrowType type = GetParam().second; + + // Initialize the builder + struct GeoArrowNativeWriter builder; + struct GeoArrowVisitor v; + struct GeoArrowError error; + v.error = &error; + ASSERT_EQ(GeoArrowNativeWriterInit(&builder, type), GEOARROW_OK); + GeoArrowNativeWriterInitVisitor(&builder, &v); + + // Visit the WKT item + struct GeoArrowWKTReader reader; + GeoArrowWKTReaderInit(&reader); + ASSERT_EQ(GeoArrowWKTReaderVisit(&reader, wkt_view, &v), GEOARROW_OK); + GeoArrowWKTReaderReset(&reader); + + // Finalize the output + struct ArrowArray array_out; + struct GeoArrowArrayView array_view; + EXPECT_EQ(GeoArrowNativeWriterFinish(&builder, &array_out, nullptr), GEOARROW_OK); + GeoArrowNativeWriterReset(&builder); + + // Validate it + ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, type), GEOARROW_OK); + ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); + + // Visit the output + WKXTester tester; + EXPECT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); + + auto values = tester.WKTValues(""); + ASSERT_EQ(values.size(), 1); + EXPECT_EQ(values[0], wkt); + + array_out.release(&array_out); +} + +#define WKT_PAIR(a, b) std::pair { a, b } + +INSTANTIATE_TEST_SUITE_P( + NativeWriterTest, WKTRoundtripParameterizedTestFixture, + ::testing::Values( + // Point + WKT_PAIR("POINT (0 1)", GEOARROW_TYPE_POINT), + WKT_PAIR("POINT Z (0 1 2)", GEOARROW_TYPE_POINT_Z), + WKT_PAIR("POINT M (0 1 2)", GEOARROW_TYPE_POINT_M), + WKT_PAIR("POINT ZM (0 1 2 3)", GEOARROW_TYPE_POINT_ZM), + + // Interleaved Point + WKT_PAIR("POINT (0 1)", GEOARROW_TYPE_INTERLEAVED_POINT), + WKT_PAIR("POINT Z (0 1 2)", GEOARROW_TYPE_INTERLEAVED_POINT_Z), + WKT_PAIR("POINT M (0 1 2)", GEOARROW_TYPE_INTERLEAVED_POINT_M), + WKT_PAIR("POINT ZM (0 1 2 3)", GEOARROW_TYPE_INTERLEAVED_POINT_ZM), + + // Linestring + WKT_PAIR("LINESTRING EMPTY", GEOARROW_TYPE_LINESTRING), + WKT_PAIR("LINESTRING (30 10, 12 16)", GEOARROW_TYPE_LINESTRING), + WKT_PAIR("LINESTRING Z (30 10 11, 12 16 15)", GEOARROW_TYPE_LINESTRING_Z), + WKT_PAIR("LINESTRING M (30 10 11, 12 16 15)", GEOARROW_TYPE_LINESTRING_M), + WKT_PAIR("LINESTRING ZM (30 10 11 10, 12 16 15 98)", GEOARROW_TYPE_LINESTRING_ZM), + + // Interleaved Linestring + WKT_PAIR("LINESTRING EMPTY", GEOARROW_TYPE_INTERLEAVED_LINESTRING), + WKT_PAIR("LINESTRING (30 10, 12 16)", GEOARROW_TYPE_INTERLEAVED_LINESTRING), + WKT_PAIR("LINESTRING Z (30 10 11, 12 16 15)", + GEOARROW_TYPE_INTERLEAVED_LINESTRING_Z), + WKT_PAIR("LINESTRING M (30 10 11, 12 16 15)", + GEOARROW_TYPE_INTERLEAVED_LINESTRING_M), + WKT_PAIR("LINESTRING ZM (30 10 11 10, 12 16 15 98)", + GEOARROW_TYPE_INTERLEAVED_LINESTRING_ZM), + + // Polygon + WKT_PAIR("POLYGON EMPTY", GEOARROW_TYPE_POLYGON), + WKT_PAIR("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", GEOARROW_TYPE_POLYGON), + WKT_PAIR("POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 " + "20, 20 30))", + GEOARROW_TYPE_POLYGON), + + // Interleaved Polygon + WKT_PAIR("POLYGON EMPTY", GEOARROW_TYPE_INTERLEAVED_POLYGON), + WKT_PAIR("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", + GEOARROW_TYPE_INTERLEAVED_POLYGON), + WKT_PAIR("POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 " + "20, 20 30))", + GEOARROW_TYPE_INTERLEAVED_POLYGON), + + // Multipoint + WKT_PAIR("MULTIPOINT EMPTY", GEOARROW_TYPE_MULTIPOINT), + WKT_PAIR("MULTIPOINT ((30 10), (12 16))", GEOARROW_TYPE_MULTIPOINT), + + // Interleaved Multipoint + WKT_PAIR("MULTIPOINT EMPTY", GEOARROW_TYPE_INTERLEAVED_MULTIPOINT), + WKT_PAIR("MULTIPOINT ((30 10), (12 16))", GEOARROW_TYPE_INTERLEAVED_MULTIPOINT), + + // Multilinestring + WKT_PAIR("MULTILINESTRING EMPTY", GEOARROW_TYPE_MULTILINESTRING), + WKT_PAIR("MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))", + GEOARROW_TYPE_MULTILINESTRING), + + // Interleaved Multilinestring + WKT_PAIR("MULTILINESTRING EMPTY", GEOARROW_TYPE_INTERLEAVED_MULTILINESTRING), + WKT_PAIR("MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))", + GEOARROW_TYPE_INTERLEAVED_MULTILINESTRING), + + // Multipolygon + WKT_PAIR("MULTIPOLYGON EMPTY", GEOARROW_TYPE_MULTIPOLYGON), + WKT_PAIR("MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, " + "30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))", + GEOARROW_TYPE_MULTIPOLYGON), + + // Interleaved Multipolygon + WKT_PAIR("MULTIPOLYGON EMPTY", GEOARROW_TYPE_INTERLEAVED_MULTIPOLYGON), + WKT_PAIR("MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, " + "30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))", + GEOARROW_TYPE_INTERLEAVED_MULTIPOLYGON) + + // Comment to keep the last line on its own + )); diff --git a/src/geoarrow/wkt_reader_test.cc b/src/geoarrow/wkt_reader_test.cc index 112ec256..f807f0b3 100644 --- a/src/geoarrow/wkt_reader_test.cc +++ b/src/geoarrow/wkt_reader_test.cc @@ -44,10 +44,10 @@ TEST(WKTReaderTest, WKTReaderTestPoint) { } TEST(WKTReaderTest, WKTReaderTestPointMultipleDims) { - struct GeoArrowBuilder builder; + struct GeoArrowNativeWriter writer; struct GeoArrowVisitor v; - ASSERT_EQ(GeoArrowBuilderInitFromType(&builder, GEOARROW_TYPE_POINT_ZM), GEOARROW_OK); - GeoArrowBuilderInitVisitor(&builder, &v); + ASSERT_EQ(GeoArrowNativeWriterInit(&writer, GEOARROW_TYPE_POINT_ZM), GEOARROW_OK); + GeoArrowNativeWriterInitVisitor(&writer, &v); WKXTester tester; tester.ReadWKT("POINT (1 2)", &v); @@ -57,14 +57,15 @@ TEST(WKTReaderTest, WKTReaderTestPointMultipleDims) { struct ArrowArray array_out; struct GeoArrowArrayView array_view; - EXPECT_EQ(GeoArrowBuilderFinish(&builder, &array_out, nullptr), GEOARROW_OK); - GeoArrowBuilderReset(&builder); + EXPECT_EQ(GeoArrowNativeWriterFinish(&writer, &array_out, nullptr), GEOARROW_OK); + GeoArrowNativeWriterReset(&writer); ASSERT_EQ(GeoArrowArrayViewInitFromType(&array_view, GEOARROW_TYPE_POINT_ZM), GEOARROW_OK); ASSERT_EQ(GeoArrowArrayViewSetArray(&array_view, &array_out, nullptr), GEOARROW_OK); - ASSERT_EQ(GeoArrowArrayViewVisit(&array_view, 0, array_out.length, tester.WKTVisitor()), - GEOARROW_OK); + ASSERT_EQ( + GeoArrowArrayViewVisitNative(&array_view, 0, array_out.length, tester.WKTVisitor()), + GEOARROW_OK); array_out.release(&array_out); auto values = tester.WKTValues("");