tests/testthat/test-compatibility.R

testthat::skip_on_cran()

test_that("Compatibility: Arrow Views behave like Persistent Tables", {
    skip_if_not_installed("duckdb")

    # Setup
    conn <- ddbs_temp_conn()

    # Create test data with a NON-STANDARD geometry column name
    # This tests if register/read respects column naming
    data_sf <- sf::st_as_sf(
        data.frame(id = 1:5, x = 0, y = 0, val = letters[1:5]),
        coords = c("x", "y"),
        crs = 4326
    )
    # sf::st_geometry(data_sf) <- "my_custom_geom"

    # 1. Register as Arrow View
    expect_no_error(
        ddbs_register_table(conn, data_sf, "view_test", overwrite = TRUE)
    )

    # 2. Test ddbs_crs on View
    expect_no_error(crs_out <- ddbs_crs(conn, "view_test"))
    expect_equal(crs_out, sf::st_crs(4326))

    # 3. Test ddbs_read_table on View
    # Should handle WKB conversion automatically and preserve "my_custom_geom"
    read_view <- ddbs_read_table(conn, "view_test")

    expect_s3_class(read_view, "sf")
    # expect_equal(attr(read_view, "sf_column"), "my_custom_geom")
    expect_equal(nrow(read_view), 5)

    # 4. Verify Data Integrity vs Persistent Table
    ddbs_write_table(conn, data_sf, "table_test", overwrite = TRUE)
    read_table <- ddbs_read_table(conn, "table_test")

    # Compare View result vs Table result
    # (Ignore attribute order if necessary, but data should match)
    expect_equal(sf::st_drop_geometry(read_view), sf::st_drop_geometry(read_table))
    expect_equal(sf::st_geometry(read_view), sf::st_geometry(read_table))
})


test_that("Round trip: write -> read for various geometry types", {
    skip_if_not_installed("duckdb")
    conn <- ddbs_temp_conn()

    # Test data
    line <- sf::st_as_sfc("LINESTRING(0 0, 1 1)") |> sf::st_sf(id = 1, geom = _, crs = 4326)
    polygon <- sf::st_as_sfc("POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))") |> sf::st_sf(id = 1, geom = _, crs = 4326)
    multipoint <- sf::st_as_sfc("MULTIPOINT(0 0, 1 1)") |> sf::st_sf(id = 1, geom = _, crs = 4326)

    datasets <- list(line = line, polygon = polygon, multipoint = multipoint)

    for (name in names(datasets)) {
        data <- datasets[[name]]
        table_name <- paste0("rt_write_", name)

        # Write -> Read
        ddbs_write_table(conn, data, table_name, overwrite = TRUE)
        result <- ddbs_read_table(conn, table_name)

        # Verification
        expect_s3_class(result, "sf")
        expect_equal(nrow(result), 1)
        expect_equal(sf::st_crs(result), sf::st_crs(data))
        # use all.equal for comparing geometries to handle potential precision issues
        expect_true(all.equal(sf::st_geometry(result), sf::st_geometry(data), check.attributes = FALSE))
    }
})

test_that("Round trip: register -> read for various geometry types", {
    skip_if_not_installed("duckdb")
    conn <- ddbs_temp_conn()

    # Test data
    line <- sf::st_as_sfc("LINESTRING(0 0, 1 1)") |> sf::st_sf(id = 1, geom = _, crs = 4326)
    polygon <- sf::st_as_sfc("POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))") |> sf::st_sf(id = 1, geom = _, crs = 4326)
    multipoint <- sf::st_as_sfc("MULTIPOINT(0 0, 1 1)") |> sf::st_sf(id = 1, geom = _, crs = 4326)

    datasets <- list(line = line, polygon = polygon, multipoint = multipoint)

    for (name in names(datasets)) {
        data <- datasets[[name]]
        view_name <- paste0("rt_register_", name)

        # Register -> Read
        ddbs_register_table(conn, data, view_name, overwrite = TRUE)
        result <- ddbs_read_table(conn, view_name)

        # Verification
        expect_s3_class(result, "sf")
        expect_equal(nrow(result), 1)
        expect_equal(sf::st_crs(result), sf::st_crs(data))
        expect_true(all.equal(sf::st_geometry(result), sf::st_geometry(data), check.attributes = FALSE))
    }
})

test_that("Compatibility: Writing from file path and reading back", {
    skip_if_not_installed("duckdb")
    conn <- ddbs_temp_conn()

    file_path <- system.file("spatial/countries.geojson", package = "duckspatial")
    table_name <- "countries_from_file_compat"

    # Write from file, then read back
    ddbs_write_table(conn, file_path, table_name, overwrite = TRUE)
    read_data <- ddbs_read_table(conn, table_name)

    # Read the original file directly with sf for comparison
    original_sf <- sf::st_read(file_path, quiet = TRUE)

    # Basic checks
    expect_s3_class(read_data, "sf")
    expect_equal(nrow(read_data), nrow(original_sf))

    # CRS check (read_data will have CRS)
    expect_equal(sf::st_crs(read_data)$srid, sf::st_crs(original_sf)$srid)

    expect_true("NAME_ENGL" %in% names(read_data))
    expect_true(all.equal(
        sf::st_geometry(read_data),
        sf::st_geometry(original_sf),
        check.attributes = FALSE
    ))
})

Try the duckspatial package in your browser

Any scripts or data that you put into this service are public.

duckspatial documentation built on June 22, 2026, 9:08 a.m.