diff --git a/DESCRIPTION b/DESCRIPTION index dbd0b636..aa490c48 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -54,6 +54,7 @@ Authors@R: c( Imports: anndataR (>= 1.1.3), BiocGenerics, + DBI, DelayedArray, dplyr, duckspatial, diff --git a/NAMESPACE b/NAMESPACE index 4b99b3f0..3ea95715 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -117,6 +117,7 @@ importFrom(BiocGenerics,as.data.frame) importFrom(BiocGenerics,colnames) importFrom(BiocGenerics,combine) importFrom(BiocGenerics,rownames) +importFrom(DBI,dbIsValid) importFrom(DelayedArray,DelayedArray) importFrom(EBImage,rotate) importFrom(Matrix,sparseMatrix) @@ -164,8 +165,10 @@ importFrom(dplyr,sql) importFrom(dplyr,tally) importFrom(duckspatial,as_duckspatial_df) importFrom(duckspatial,ddbs_bbox) +importFrom(duckspatial,ddbs_create_conn) importFrom(duckspatial,ddbs_intersects) importFrom(duckspatial,ddbs_open_dataset) +importFrom(duckspatial,ddbs_write_table) importFrom(graph,"edgeData<-") importFrom(graph,"edgeDataDefaults<-") importFrom(graph,"nodeData<-") diff --git a/R/mask.R b/R/mask.R index 633b92f8..d82f14e4 100644 --- a/R/mask.R +++ b/R/mask.R @@ -120,6 +120,7 @@ setMethod(".mask", c("SpatialDataImage", "SpatialDataLabel"), \(i, j, how=NULL, "POINT"=mutate(data(j), geometry=ST_Buffer(geometry, radius)), data(j)) ddbs_intersects(df_j, data(i), sparse=TRUE) + } #' @noRd diff --git a/R/read.R b/R/read.R index 355ae1fd..4bc4d06b 100644 --- a/R/read.R +++ b/R/read.R @@ -75,13 +75,26 @@ readPoint <- function(x, ...) { pq <- list.files(x, "\\.parquet$", full.names=TRUE) md <- read_zarr_attributes(x) ax <- unlist(md$axes) - df <- ddbs_open_dataset(pq) |> + df <- ddbs_open_dataset(pq, conn=.conn()) |> mutate(geometry=sql(sprintf("ST_Point(%s, %s)", ax[1], ax[2]))) |> as_duckspatial_df(crs=NA_character_) |> select(-all_of(ax)) SpatialDataPoint(data=df, meta=SpatialDataAttrs(md)) } +# create DuckDB connection +# (to be used everywhere!) +#' @importFrom DBI dbIsValid +#' @importFrom duckspatial ddbs_create_conn +.conn <- \() { + nm <- ".SpatialData_DuckDB_conn" + if (!exists(nm, envir=.GlobalEnv) || + !dbIsValid(.GlobalEnv[[nm]])) { + .GlobalEnv[[nm]] <- ddbs_create_conn() + } + .GlobalEnv[[nm]] +} + #' @rdname readSpatialData #' @importFrom Rarr read_zarr_attributes #' @importFrom duckspatial ddbs_open_dataset @@ -90,7 +103,8 @@ readPoint <- function(x, ...) { readShape <- function(x, ...) { md <- read_zarr_attributes(x) pq <- list.files(x, "\\.parquet$", full.names=TRUE) - SpatialDataShape(data=ddbs_open_dataset(pq), meta=SpatialDataAttrs(md)) + df <- ddbs_open_dataset(pq, conn=.conn(), crs=NA_character_) + SpatialDataShape(data=df, meta=SpatialDataAttrs(md)) } #' @export diff --git a/R/sdFrame.R b/R/sdFrame.R index 5488392a..442dbef2 100644 --- a/R/sdFrame.R +++ b/R/sdFrame.R @@ -114,7 +114,7 @@ SpatialDataPoint <- \(data=NULL, meta=SpatialDataAttrs(type="frame"), metadata=l "found: ", paste(gt, collapse=", ")) # always ensure internal data is 'duckspatial_df' if (!is(data, "duckspatial_df")) - data <- as_duckspatial_df(data, crs=NA) + data <- as_duckspatial_df(data, crs=NA_character_) } # update 'spatialdata_attrs' if keys are provided za <- as.list(meta) @@ -138,13 +138,25 @@ SpatialDataPoint <- \(data=NULL, meta=SpatialDataAttrs(type="frame"), metadata=l #' @rdname SpatialDataFrame #' @importFrom methods is #' @importFrom S4Vectors metadata<- +#' @importFrom duckspatial ddbs_write_table #' @importFrom duckspatial as_duckspatial_df SpatialDataShape <- \(data=NULL, meta=SpatialDataAttrs(type="frame"), metadata=list(), ...) { data <- .df_to_sf(data, "POLYGON") # always ensure internal data is 'duckspatial_df' - if (isTRUE(nrow(data) > 0L) && - !is(data, "duckspatial_df")) - data <- as_duckspatial_df(data, crs=NA) + if (!is(data, "duckspatial_df") && isTRUE(nrow(data) > 0L)) { + suppressMessages( # silent complaint re: missing CRS + ddbs_write_table( + conn=.conn(), + data=data, + name="sdShape", + overwrite=TRUE, + temp_view=FALSE)) + data <- as_duckspatial_df( + x="sdShape", + conn=.conn(), + crs=NA_character_, + geom_col=attr(data, "sf_column")) + } x <- .SpatialDataShape(data=data, meta=meta, ...) metadata(x) <- metadata return(x) diff --git a/tests/testthat/test-mask.R b/tests/testthat/test-mask.R index ba86271c..14acd26a 100644 --- a/tests/testthat/test-mask.R +++ b/tests/testthat/test-mask.R @@ -21,7 +21,7 @@ test_that("mask,unaligned", { # non-existent expect_error( mask(x, i, j, "x"), - "should be \"global\"") + "'arg' should be") # not shared za <- meta(image(x, i)) @@ -157,9 +157,8 @@ test_that("mask,sdShape,sdShape", { y <- setTable(x, i, se) # out-of-bounds masking - t <- translation(s, c(1e3,1e3)) - shape(y, "out") <- t - expect_error(mask(y, i, "out")) + shape(y, "out") <- translation(s, c(1e3,1e3)) + expect_error(mask(y, i, "out", how="sum")) # note: data at "0" are from non-intersecting instances; # here, all data should be aggregated to column "1" diff --git a/vignettes/SpatialData.html b/vignettes/SpatialData.html deleted file mode 100644 index bfa10dc2..00000000 --- a/vignettes/SpatialData.html +++ /dev/null @@ -1,1103 +0,0 @@ - - - - -
- - - - - - - - - -SpatialDataSpatialData 0.99.31
- -The SpatialData package provides an R interface to the
-SpatialData framework, a unified ecosystem
-for handling spatial omics data. Developed as part of the
-scverse project (Virshup et al. 2023), SpatialData aims to solve the
-challenges of integrating diverse spatial datasets—including imaging, spatial
-transcriptomics, and proteomics—by employing the
-OME-NGFF (Next Generation File Format)
-standard (Marconato et al. 2024).
The Python implementation and core specifications can be found at the -official SpatialData website.
-The core of the package is the SpatialData class, which serves as a container
-for multiple layers of spatial information:
ZarrArray objects via the Rarr and ZarrArray packages.ZarrArrays.First, we load the necessary libraries:
-library(SpatialData)
-library(SingleCellExperiment)
-SpatialData objects are typically read from .zarr stores. The package provides
-the readSpatialData() function to ingest an entire store.
# Path to a toy dataset included in the package
-path <- system.file("extdata", "blobs.zarr", package="SpatialData")
-
-# Read the complete SpatialData object
-sd <- readSpatialData(path)
-sd
-## class: SpatialData
-## - images(2):
-## - blobs_image (3,64,64)
-## - blobs_multiscale_image (3,64,64)
-## - labels(2):
-## - blobs_labels (64,64)
-## - blobs_multiscale_labels (64,64)
-## - points(1):
-## - blobs_points (200)
-## - shapes(3):
-## - blobs_circles (5,circle)
-## - blobs_multipolygons (2,polygon)
-## - blobs_polygons (5,polygon)
-## - tables(1):
-## - table (3,10) [blobs_labels]
-## coordinate systems(5):
-## - global(8): blobs_image blobs_multiscale_image ... blobs_polygons
-## blobs_points
-## - scale(1): blobs_labels
-## - translation(1): blobs_labels
-## - affine(1): blobs_labels
-## - sequence(1): blobs_labels
-anndataRFor spatial omics datasets, functional annotations are often stored as
-AnnData objects in Python. The
-anndataR package (Deconinck et al. 2025) is used by
-SpatialData to seamlessly read these .zarr-backed AnnData objects into
-R as SingleCellExperiment objects.
By default, readSpatialData(..., anndataR=TRUE) will use this package to
-ingest any tables found in the Zarr store.
A key feature of the SpatialData framework is its robust handling of
-coordinate systems. Each element can exist in multiple coordinate spaces
-simultaneously, defined by transformations in its metadata.
The relationships between different elements and their respective coordinate
-spaces can be complex. SpatialData provides the CTgraph() function to
-visualize these relationships as a directed graph.
if (requireNamespace("Rgraphviz", quietly = TRUE)) {
- g <- CTgraph(sd)
- CTplot(g)
-}
-
-Figure 1: Coordinate transformation graph for the ‘blobs’ dataset
-
In this graph:
-_) represent the individual data layers (images, labels, etc.).global, sequence).The transform() function resolves the necessary steps to project an element
-into a target coordinate system by traversing this graph:
# Retrieve a point element
-p <- point(sd, 1)
-
-# Project it into the 'global' coordinate space
-q <- SpatialData::transform(p, "global")
-SpatialData supports sophisticated spatial operations such as cropping,
-which are propagated across all layers:
# Define a bounding box for cropping
-bb <- list(xmin=10, xmax=50, ymin=10, ymax=50)
-
-# Crop the entire object
-(sp <- crop(sd, bb, j="global"))
-## class: SpatialData
-## - images(2):
-## - blobs_image (3,40,40)
-## - blobs_multiscale_image (3,40,40)
-## - labels(2):
-## - blobs_labels (40,40)
-## - blobs_multiscale_labels (40,40)
-## - points(1):
-## - blobs_points (92)
-## - shapes(3):
-## - blobs_circles (5,circle)
-## - blobs_multipolygons (2,polygon)
-## - blobs_polygons (5,polygon)
-## - tables(1):
-## - table (3,10) [blobs_labels]
-## coordinate systems(5):
-## - global(8): blobs_image blobs_multiscale_image ... blobs_polygons
-## blobs_points
-## - scale(1): blobs_labels
-## - translation(1): blobs_labels
-## - affine(1): blobs_labels
-## - sequence(1): blobs_labels
-Tables (SCE objects) link to specific labels or shapes using region and
-instance_key identifiers, allowing for seamless integration of spatial and
-feature-level information.
(sce <- SpatialData::table(sd))
-## class: SingleCellExperiment
-## dim: 3 10
-## metadata(0):
-## assays(1): X
-## rownames(3): channel_0_sum channel_1_sum channel_2_sum
-## rowData names(0):
-## colnames(10): 3 4 ... 15 16
-## colData names(0):
-## reducedDimNames(0):
-## mainExpName: NULL
-## altExpNames(0):
-# Check which region this table annotates
-region(sce)
-## [1] "blobs_labels"
-# Check which instances are being annotated
-instances(sce)
-## [1] 3 4 5 8 10 11 12 13 15 16
-The SpatialData package brings the power of the scverse spatial ecosystem to
-R, providing a scalable and interoperable foundation for spatial omics analysis.
-By adhering to the OME-NGFF standard and integrating with established
-Bioconductor objects, it enables seamless cross-platform workflows.
sessionInfo()
-## R version 4.6.0 (2026-04-24)
-## Platform: aarch64-apple-darwin23
-## Running under: macOS Sequoia 15.6.1
-##
-## Matrix products: default
-## BLAS: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRblas.0.dylib
-## LAPACK: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
-##
-## locale:
-## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
-##
-## time zone: Europe/Madrid
-## tzcode source: internal
-##
-## attached base packages:
-## [1] stats4 stats graphics grDevices utils datasets methods
-## [8] base
-##
-## other attached packages:
-## [1] SingleCellExperiment_1.33.2 SummarizedExperiment_1.41.1
-## [3] Biobase_2.71.0 GenomicRanges_1.63.2
-## [5] Seqinfo_1.1.0 IRanges_2.45.0
-## [7] S4Vectors_0.49.3 BiocGenerics_0.57.1
-## [9] generics_0.1.4 MatrixGenerics_1.23.0
-## [11] matrixStats_1.5.0 SpatialData_0.99.31
-## [13] BiocStyle_2.39.0
-##
-## loaded via a namespace (and not attached):
-## [1] DBI_1.3.0 bitops_1.0-9 RBGL_1.87.0
-## [4] httr2_1.2.2 anndataR_1.1.2 rlang_1.2.0
-## [7] magrittr_2.0.5 Rarr_1.99.44 otel_0.2.0
-## [10] e1071_1.7-17 compiler_4.6.0 dir.expiry_1.19.0
-## [13] paws.storage_0.9.0 png_0.1-9 fftwtools_0.9-11
-## [16] vctrs_0.7.3 pkgconfig_2.0.3 wk_0.9.5
-## [19] crayon_1.5.3 fastmap_1.2.0 dbplyr_2.5.2
-## [22] magick_2.9.1 XVector_0.51.0 paws.common_0.8.9
-## [25] rmarkdown_2.31 graph_1.89.1 tinytex_0.59
-## [28] purrr_1.2.2 bit_4.6.0 xfun_0.57
-## [31] cachem_1.1.0 jsonlite_2.0.0 blob_1.3.0
-## [34] DelayedArray_0.37.1 uuid_1.2-2 jpeg_0.1-11
-## [37] tiff_0.1-12 parallel_4.6.0 R6_2.6.1
-## [40] bslib_0.10.0 reticulate_1.46.0 jquerylib_0.1.4
-## [43] Rcpp_1.1.1-1.1 bookdown_0.46 assertthat_0.2.1
-## [46] knitr_1.51 R.utils_2.13.0 Matrix_1.7-5
-## [49] tidyselect_1.2.1 duckspatial_1.0.0 rstudioapi_0.18.0
-## [52] abind_1.4-8 yaml_2.3.12 EBImage_4.53.0
-## [55] curl_7.1.0 lattice_0.22-9 tibble_3.3.1
-## [58] withr_3.0.2 evaluate_1.0.5 sf_1.1-0
-## [61] units_1.0-1 proxy_0.4-29 pillar_1.11.1
-## [64] BiocManager_1.30.27 filelock_1.0.3 KernSmooth_2.23-26
-## [67] RCurl_1.98-1.18 nanoarrow_0.8.0 class_7.3-23
-## [70] glue_1.8.1 tools_4.6.0 locfit_1.5-9.12
-## [73] grid_4.6.0 basilisk_1.23.0 duckdb_1.5.2
-## [76] cli_3.6.6 rappdirs_0.3.4 S4Arrays_1.11.1
-## [79] arrow_23.0.1.2 dplyr_1.2.1 Rgraphviz_2.55.0
-## [82] geoarrow_0.4.2 R.methodsS3_1.8.2 sass_0.4.10
-## [85] digest_0.6.39 classInt_0.4-11 SparseArray_1.11.13
-## [88] ZarrArray_0.99.5 htmlwidgets_1.6.4 htmltools_0.5.9
-## [91] R.oo_1.27.1 lifecycle_1.0.5 bit64_4.8.0
-