Diff of /R/io.R [000000] .. [413088]

Switch to unified view

a b/R/io.R
1
####
2
# save ####
3
####
4
5
#' saveVoltRon
6
#'
7
#' save VoltRon object in memory or on disk
8
#' 
9
#' @param object a VoltRon object
10
#' @param assay assay name (exp: Assay1) or assay class (exp: Visium, Xenium), see \link{SampleMetadata}. 
11
#' @param format the format the object should be written: InMemoryVoltRon (rds only), HDF5VoltRon (h5), or ZarrVoltRon (zarr).
12
#' @param output When saving, the directory will be created if it doesn't already exist. If the directory already exists and no prefix is specified and replace is set to TRUE, then it's replaced with an empty directory.
13
#' @param replace When no prefix is specified, should a pre-existing directory be replaced with a new empty one? The content of the pre-existing directory will be lost!
14
#' @param chunkdim The dimensions of the chunks to use for writing the assay data to disk.
15
#' @param level The compression level to use for writing the assay data to disk.
16
#' @param as.sparse as.sparse
17
#' @param verbose verbose
18
#'
19
#' @export
20
saveVoltRon <- function (object, 
21
                         assay = NULL,
22
                         format = c("InMemoryVoltRon", "HDF5VoltRon", "ZarrVoltRon"), 
23
                         output = NULL, 
24
                         replace = FALSE, 
25
                         chunkdim = NULL, 
26
                         level = NULL, 
27
                         as.sparse = NA, 
28
                         verbose = TRUE) 
29
{
30
  # check object
31
  if (!is(object, "VoltRon")) 
32
    stop("'object' must be a VoltRon object")
33
  
34
  # check if the object was previously saved on disk
35
  paths <- .get_unique_links(object)
36
  paths <- unique(vapply(paths, file_path_as_absolute, character(1)))
37
  if(length(paths) > 1){
38
    if(is.null(output)){
39
      stop("There are multiple paths that this VoltRon object is saved to, cannot write unless 'output' is specified!")
40
    }
41
    replace <- TRUE
42
  } else if(length(paths) == 1){
43
    if(is.null(output)){
44
      message("Object has existing paths and 'output' is not specified, using those instead of the provided 'ondisk_path'")
45
      format <- ifelse(grepl(".zarr$", paths), "ZarrVoltRon", "HDF5VoltRon")
46
      output <- base::dirname(paths)
47
      replace <- FALSE    
48
    }
49
  } else {
50
    if(length(format) > 1){
51
      message("No paths are found in the object, and no format is chosen, saving as rds only!")
52
      format <- "InMemoryVoltRon"
53
    }
54
  }
55
  
56
  # check output
57
  if (!isSingleString(output)) 
58
    stop("'output' must be a single string specifying the path ", 
59
         "to the directory where to save the ", class(object), 
60
         " object (the directory will be created if needed)")
61
  
62
  # save VoltRon on disk
63
  if(format != "InMemoryVoltRon"){
64
    
65
    # create or replace output folder
66
    if (!isTRUEorFALSE(replace)) 
67
      stop("'replace' must be TRUE or FALSE")
68
    if (!dir.exists(output)) {
69
      create_dir(output)
70
    } else if(replace){
71
      replace_dir(output) 
72
    }
73
    
74
    # determine format
75
    switch(format,
76
           HDF5VoltRon = {
77
             ondisk_path <- file.path(output, "assays.h5")
78
           }, 
79
           ZarrVoltRon = {
80
             ondisk_path <- file.path(output, "assays.zarr")
81
           })
82
    rds_path <- file.path(output, "se.rds")
83
    
84
    
85
    # write on disk
86
    object <- .write_VoltRon(object, assay = assay, format = format, rds_path = rds_path, ondisk_path = ondisk_path, 
87
                             chunkdim = chunkdim, level = level, as.sparse = as.sparse, verbose = verbose, replace = replace)
88
    
89
    # serialize rds file
90
    .serialize_VoltRonObject(object, rds_path, verbose = verbose)
91
    
92
  # save VoltRon on memory
93
  } else {
94
    rds_path <- paste0(output, "_", paste0("se.rds"))
95
    saveRDS(object, file = rds_path)
96
  }
97
  
98
  # return
99
  object
100
}
101
102
####
103
# load ####
104
####
105
106
107
#' loadVoltRon
108
#'
109
#' load VoltRon object from memory or disk
110
#' 
111
#' @param dir the directory that VoltRon object is found.
112
#'
113
#' @export
114
loadVoltRon <- function(dir="my_se")
115
{
116
  if(!requireNamespace('DelayedArray'))
117
    stop("Please install DelayedArray package!: BiocManager::install('DelayedArray')")
118
  
119
  # check dir
120
  if (!isSingleString(dir))
121
    stop("'dir' must be a single string specifying the path ",
122
              "to the directory containing an rds and/or .h5/.zarr file!")
123
  if (!dir.exists(dir)) {
124
    if (file.exists(dir))
125
      stop("\"", dir, "\" is a file, not a directory")
126
    stop("directory \"", dir, "\" not found")
127
  }
128
  
129
  # get rds path
130
  rds_path <- file.path(dir, paste0("se.rds"))
131
  
132
  # load h5/zarr store
133
  ans <- try(.read_VoltRon(rds_path), silent=TRUE)
134
  if (inherits(ans, "try-error"))
135
    stop_if_bad_dir(dir, prefix = "")
136
  ans
137
}
138
139
####
140
# ondisk Methods ####
141
####
142
143
#' .serialize_VoltRonObject
144
#'
145
#' @param object a VoltRon object
146
#' @param rds_path the path to rds
147
#' @param verbose verbose
148
#'
149
#' @noRd
150
.serialize_VoltRonObject <- function(object, rds_path, verbose)
151
{
152
  # assay_names 
153
  assay_names <- vrAssayNames(object, assay = "all")
154
  
155
  # update metadata
156
  object@metadata <- shorten_metadata_links(Metadata(object, type = "all"))
157
    
158
  # update all assays
159
  for(assy in assay_names)
160
    object[[assy]] <- shorten_assay_links(object[[assy]])
161
  
162
  # verbose and save rds
163
  if (verbose)
164
    message("Serialize ", class(object), " object to ",
165
            ifelse(file.exists(rds_path), "existing ", ""),
166
            "RDS file:\n  ", rds_path)
167
  saveRDS(object, file=rds_path)
168
}
169
170
#' modify_seeds
171
#'
172
#' @noRd
173
modify_seeds <- function (x, FUN, ...) 
174
{
175
  if (is(x, "DelayedUnaryOp")) {
176
    x@seed <- modify_seeds(x@seed, FUN, ...)
177
  }
178
  else if (is(x, "DelayedNaryOp")) {
179
    x@seeds <- lapply(x@seeds, modify_seeds, FUN, ...)
180
  }
181
  else {
182
    x <- FUN(x, ...)
183
  }
184
  return(x)
185
}
186
187
#' .write_VoltRon
188
#'
189
#' @noRd
190
.write_VoltRon <- function(object, assay = NULL, format, rds_path, ondisk_path, chunkdim=NULL, level=NULL, as.sparse=NA, verbose=TRUE, replace = FALSE)
191
{
192
  # check object
193
  if (!is(object, "VoltRon"))
194
    stop("'object' must be a VoltRon object")
195
  
196
  # check output and other related arguments
197
  if (!isSingleString(rds_path) || rds_path == "")
198
    stop("'rds_path' must be a a non-empty string ",
199
         "specifying the path to the RDS file ",
200
         "where to write the ", class(object), " object")
201
  if (!isSingleString(ondisk_path) || ondisk_path == "")
202
    stop("'ondisk_path' must be a a non-empty string ",
203
         "specifying the path to the HDF5 file ",
204
         "where to write the assays of the ", class(object), " object")
205
  if (!isTRUEorFALSE(verbose))
206
    stop("'verbose' must be TRUE or FALSE")
207
  
208
  if(format == "HDF5VoltRon"){
209
    object <- write_h5_samples(object, assay = assay, h5_path = ondisk_path, chunkdim, level, as.sparse, verbose, replace)
210
  } else if(format == "ZarrVoltRon"){
211
    object <- write_zarr_samples(object, assay = assay, zarr_path = ondisk_path, chunkdim, level, as.sparse, verbose, replace)
212
  } else {
213
    stop("'format' should be either 'HDF5VoltRon' or 'ZarrVoltRon'")
214
  }
215
  invisible(object)
216
}
217
218
#' .read_VoltRon
219
#'
220
#' @noRd
221
.read_VoltRon <- function(rds_path)
222
{
223
  # check rds file
224
  if (!file.exists(rds_path))
225
    stop("file not found: ", rds_path)
226
  if (dir.exists(rds_path))
227
    stop("'", rds_path, "' is a directory, not a file")
228
  
229
  # check VoltRon object
230
  object <- readRDS(rds_path)
231
  if (!is(object, "VoltRon"))
232
    stop("the object serialized in \"", rds_path, "\" is not ",
233
                "a VoltRon object")
234
  
235
  # get dir name
236
  dir <- dirname(rds_path)
237
  
238
  # assay_names 
239
  assay_names <- vrAssayNames(object, assay = "all")
240
  
241
  # restore metadata links 
242
  object@metadata <- restore_absolute_metadata_links(Metadata(object, type = "all"), dir)
243
  
244
  # restore assay links
245
  for(assy in assay_names)
246
    object[[assy]] <- restore_absolute_assay_links(object[[assy]], dir)
247
  
248
  # return object
249
  object
250
}
251
252
####
253
## HDF5 Support ####
254
####
255
256
#' write_h5_samples
257
#'
258
#' @noRd
259
write_h5_samples <- function(object, assay = NULL, h5_path, chunkdim, level,
260
                             as.sparse, verbose, replace)
261
{
262
  if(!requireNamespace('rhdf5'))
263
    stop("Please install rhdf5 package!: BiocManager::install('rhdf5')")
264
  
265
  # sample metadata
266
  sample_metadata <- SampleMetadata(object)
267
  
268
  # open h5 file
269
  if(verbose)
270
    message("HDF5 file: ", h5_path)
271
  if(!file.exists("h5_path"))
272
    rhdf5::h5createFile(h5_path)
273
  
274
  # create metadata
275
  rhdf5::h5createGroup(h5_path, group = "metadata")
276
  object@metadata <- writeHDF5ArrayInMetadata(object = Metadata(object, type = "all"), 
277
                                              h5_path,
278
                                              name = "metadata",
279
                                              chunkdim=chunkdim, 
280
                                              level=level,
281
                                              as.sparse=as.sparse,
282
                                              with.dimnames=TRUE,
283
                                              verbose=verbose, 
284
                                              replace=replace)
285
  
286
  # iterate over assays
287
  assay_names <- vrAssayNames(object, assay = "all")
288
  for (assy in assay_names) {
289
    
290
    # get assay object
291
    assay_object <- object[[assy]]
292
    
293
    # create assay group in h5
294
    rhdf5::h5createGroup(h5_path, group = assy)
295
    
296
    # get data and write
297
    assay_object <- writeHDF5ArrayInVrData(object = assay_object, 
298
                                           h5_path,
299
                                           name = assy,
300
                                           chunkdim=chunkdim, 
301
                                           level=level,
302
                                           as.sparse=as.sparse,
303
                                           with.dimnames=TRUE,
304
                                           verbose=verbose, 
305
                                           replace=replace)
306
    
307
    # get image data and write
308
    assay_object <- writeHDF5ArrayInImage(object = assay_object, 
309
                                          h5_path,
310
                                          name = assy,
311
                                          chunkdim=chunkdim, 
312
                                          level=level,
313
                                          as.sparse=as.sparse,
314
                                          with.dimnames=FALSE,
315
                                          verbose=verbose, 
316
                                          replace=replace)
317
    
318
    # write assay back
319
    object[[assy]] <- assay_object
320
    
321
  }
322
  object
323
}
324
325
#' writeHDF5ArrayInMetadata
326
#'
327
#' @noRd
328
writeHDF5ArrayInMetadata <- function(object, 
329
                                     h5_path,
330
                                     name,
331
                                     chunkdim, 
332
                                     level,
333
                                     as.sparse,
334
                                     with.dimnames=FALSE,
335
                                     verbose, 
336
                                     replace = FALSE){
337
  
338
  # check HDF5DataFrame
339
  if(!requireNamespace('HDF5DataFrame'))
340
    stop("Please install HDF5DataFrame package!: devtools::install_github('BIMSBbioinfo/HDF5DataFrame')")
341
  if(!requireNamespace('HDF5Array'))
342
    stop("Please install HDF5Array package!: BiocManager::install('HDF5Array')")
343
  if(!requireNamespace('rhdf5'))
344
    stop("Please install rhdf5 package!: BiocManager::install('rhdf5')")
345
  
346
  # iterate over all metadata slots
347
  slot_names <- slotNames(object)
348
  for(sn in slot_names){
349
    meta.data <- methods::slot(object, name = sn)
350
    if(!inherits(meta.data, c("DataFrame", "HDF5DataFrame")) || replace){
351
      if(nrow(meta.data) > 0){
352
        meta.data_list <- list()
353
        rhdf5::h5createGroup(h5_path, group = paste0(name, "/", sn))
354
        if(verbose)
355
          message("Writing ", sn, " Metadata")
356
        
357
        # write rownames first if they exist, and there is no id column
358
        if(!is.null(rownames(meta.data)) && !("id" %in% colnames(meta.data))){
359
          cur_column <- as.array(rownames(meta.data))
360
          meta.data_list[["id"]] <- 
361
            HDF5Array::writeHDF5Array(cur_column, 
362
                                      h5_path, 
363
                                      name = paste0(name, "/", sn, "/id"),
364
                                      chunkdim=chunkdim, 
365
                                      level=level,
366
                                      as.sparse=as.sparse,
367
                                      with.dimnames=FALSE,
368
                                      verbose=FALSE)
369
        }
370
        
371
        # write rest of the columns
372
        for(i in seq_len(ncol(meta.data))){
373
          column_name <- paste0(name, "/", sn, "/", colnames(meta.data)[i])
374
          if(inherits(meta.data,"data.table")){
375
            cur_column <- as.array(as.vector(subset(meta.data, select = colnames(meta.data)[i]))[[1]])
376
          } else {
377
            cur_column <- as.array(meta.data[,i])
378
          }
379
          if(is.factor(cur_column))
380
            cur_column <- as.array(as.character(cur_column))
381
          meta.data_list[[colnames(meta.data)[i]]] <- 
382
            HDF5Array::writeHDF5Array(cur_column, 
383
                                      h5_path, 
384
                                      name = column_name,
385
                                      chunkdim=chunkdim, 
386
                                      level=level,
387
                                      as.sparse=as.sparse,
388
                                      with.dimnames=FALSE,
389
                                      verbose=FALSE)
390
        }
391
        methods::slot(object, name = sn) <- 
392
          HDF5DataFrame::HDF5DataFrame(meta.data_list)
393
      }
394
    } else {
395
      meta.data_list <- list()
396
      for(i in seq_len(ncol(meta.data))){
397
        column_name <- paste0(name, "/", sn, "/", colnames(meta.data)[i])
398
        if(!h5Dexists(h5_path, column_name)){
399
          if(inherits(meta.data,"data.table")){
400
            cur_column <- as.array(as.vector(subset(meta.data, select = colnames(meta.data)[i]))[[1]])
401
          } else {
402
            cur_column <- as.array(meta.data[,i])
403
          }
404
          if(is.factor(cur_column))
405
            cur_column <- as.array(as.character(cur_column))
406
          new_column <- HDF5Array::writeHDF5Array(cur_column, 
407
                                                  h5_path, 
408
                                                  name = column_name,
409
                                                  chunkdim=chunkdim, 
410
                                                  level=level,
411
                                                  as.sparse=as.sparse,
412
                                                  with.dimnames=FALSE,
413
                                                  verbose=FALSE)
414
          new_column <- HDF5DataFrame::HDF5ColumnVector(DelayedArray::path(new_column), 
415
                                                        name = paste0(name, "/", sn), 
416
                                                        column = colnames(meta.data)[i])
417
          meta.data[[colnames(meta.data)[i]]] <- new_column
418
        } else {
419
          # meta.data_list[[colnames(meta.data)[i]]] <- meta.data[[colnames(meta.data)[i]]]
420
        } 
421
      }
422
      methods::slot(object, name = sn) <- meta.data
423
      # methods::slot(object, name = sn) <- 
424
      #   HDF5DataFrame::HDF5DataFrame(meta.data_list)
425
    }
426
  }
427
  
428
  return(object)
429
}
430
431
#' writeHDF5ArrayInVrData
432
#'
433
#' @noRd
434
writeHDF5ArrayInVrData <- function(object, 
435
                                   h5_path,
436
                                   name,
437
                                   chunkdim, 
438
                                   level,
439
                                   as.sparse,
440
                                   with.dimnames=FALSE,
441
                                   verbose, 
442
                                   replace = FALSE){
443
  
444
  # check packages
445
  if(!requireNamespace('BPCells'))
446
    stop("Please install BPCells package!: remotes::install_github('bnprks/BPCells/r')")
447
  
448
  # check if there is a data or rawdata slot in assay object
449
  catch_connect1 <- try(slot(object, name = "data"), silent = TRUE)
450
  catch_connect2 <- try(slot(object, name = "rawdata"), silent = TRUE)
451
  
452
  # get data with a specific feature
453
  if(!is(catch_connect1, 'try-error') && !methods::is(catch_connect1,'error')){
454
    
455
    feature_types <- vrFeatureTypeNames(object)
456
    for(feat in feature_types){
457
      
458
      # raw data
459
      a <- vrData(object, feat_type = feat, norm = FALSE)
460
      if(!inherits(a, c("DelayedArray", "IterableMatrix")) || replace){
461
        if(!inherits(a, "dgCMatrix"))
462
          a <- as(a, "dgCMatrix")
463
        if(verbose)
464
          message("Writing '", vrAssayNames(object), "' ", feat, " data")
465
        a <- BPCells::write_matrix_hdf5(a, 
466
                                        path = h5_path, 
467
                                        group = paste0(name, "/", feat), 
468
                                        overwrite = TRUE)
469
        # chunk_size = chunkdim)
470
        object@data[[feat]] <- a   
471
        
472
      }
473
      
474
      # normalized data
475
      a <- vrData(object, feat_type = feat, norm = TRUE)
476
      if(!inherits(a, c("DelayedArray", "IterableMatrix")) || replace){
477
        if(!inherits(a, "dgCMatrix"))
478
          a <- as(a, "dgCMatrix")
479
        if(verbose)
480
          message("Writing '", vrAssayNames(object), "' normalized ", feat, " data")
481
        a <- BPCells::write_matrix_hdf5(a, 
482
                                        path = h5_path, 
483
                                        group = paste0(name, "/", feat, "_norm"), 
484
                                        overwrite = TRUE)
485
        # chunk_size = chunkdim)
486
        object@data[[paste0(feat, "_norm")]] <- a  
487
      }
488
      
489
    }
490
    
491
  } else if(!is(catch_connect2, 'try-error') && !methods::is(catch_connect2,'error')){
492
    
493
    # raw data
494
    a <- vrData(object, norm = FALSE)
495
    if(!inherits(a, "DelayedArray") || replace){
496
      if(!inherits(a, "dgCMatrix"))
497
        a <- as(a, "dgCMatrix")
498
      if(verbose)
499
        message("Writing '", vrAssayNames(object), "' data")
500
      a <- BPCells::write_matrix_hdf5(a, 
501
                                      path = h5_path, 
502
                                      group = paste0(name, "/rawdata"), 
503
                                      overwrite = TRUE)
504
      object@rawdata <- a 
505
    }
506
    
507
    # normalized data
508
    a <- vrData(object, norm = TRUE)
509
    if(!inherits(a, "DelayedArray") || replace){
510
      if(!inherits(a, "dgCMatrix"))
511
        a <- as(a, "dgCMatrix")
512
      if(verbose)
513
        message("Writing '", vrAssayNames(object), "' normalized data")
514
      a <- BPCells::write_matrix_hdf5(a, 
515
                                      path = h5_path, 
516
                                      group = paste0(name, "/normdata"), 
517
                                      overwrite = TRUE)
518
      object@normdata <- a
519
    }
520
    
521
  }
522
  
523
  return(object)
524
}
525
526
#' writeHDF5ArrayInImage
527
#'
528
#' @noRd
529
writeHDF5ArrayInImage <- function(object, 
530
                                  h5_path,
531
                                  name,
532
                                  chunkdim, 
533
                                  level,
534
                                  as.sparse,
535
                                  with.dimnames,
536
                                  verbose, 
537
                                  replace = FALSE){
538
  
539
  # check packages
540
  if(!requireNamespace('ImageArray'))
541
    stop("Please install ImageArray package!: devtools::install_github('BIMSBbioinfo/ImageArray')")
542
  if(!requireNamespace('rhdf5'))
543
    stop("Please install rhdf5 package!: BiocManager::install('rhdf5')")
544
  if(!requireNamespace('BPCells'))
545
    stop("Please install BPCells package!: remotes::install_github('bnprks/BPCells/r')")
546
  
547
  # for each spatial system
548
  spatial_names <- vrSpatialNames(object)
549
  for(spat in spatial_names){
550
    
551
    # open group
552
    rhdf5::h5createGroup(h5_path, group = paste0(name, "/", spat))
553
    
554
    # write coordinates 
555
    coords <- vrCoordinates(object, spatial_name = spat)
556
    if(!inherits(coords, c("DelayedArray", "IterableMatrix")) || replace){
557
      if(!inherits(coords, "dgCMatrix"))
558
        coords <- as(coords, "dgCMatrix")
559
      if(verbose)
560
        message("Writing '", name, "' coordinates")
561
      coords <- BPCells::write_matrix_hdf5(coords, 
562
                                           path = h5_path, 
563
                                           group = paste0(name, "/", spat, "/coords"), 
564
                                           overwrite = TRUE)
565
      # chunk_size = chunkdim)
566
      vrCoordinates(object, spatial_name = spat) <- coords
567
    }
568
    
569
    # for each channel
570
    channels <- vrImageChannelNames(object, name = spat)
571
    if(!all(grepl("No Channels", channels))){
572
      for(ch in channels){
573
        
574
        # get image and write to h5
575
        img <- vrImages(object, name = spat, channel = ch, as.raster = TRUE)
576
        
577
        # write image
578
        if(!inherits(img, "Image_Array") || replace){
579
          if(verbose)
580
            message("Writing '", name, "' image channel '", ch, "' for spatial system '", spat,"'")
581
          img <- ImageArray::writeImageArray(img,
582
                                             output = gsub(".h5$", "", h5_path),
583
                                             name = paste0(name, "/", spat, "/", ch), 
584
                                             format = "HDF5ImageArray", 
585
                                             replace = FALSE, 
586
                                             chunkdim=chunkdim,
587
                                             level=level,
588
                                             as.sparse=as.sparse,
589
                                             verbose=FALSE)
590
          suppressWarnings({
591
            vrImages(object, name = spat, channel = ch) <- img 
592
          })
593
        }
594
      } 
595
    }
596
  }
597
  
598
  return(object)
599
}
600
601
####
602
## ZARR Support ####
603
####
604
605
#' write_zarr_samples
606
#'
607
#' @noRd
608
write_zarr_samples <- function(object, assay = NULL, zarr_path, chunkdim, level,
609
                               as.sparse, verbose, replace)
610
{
611
  if(!requireNamespace('pizzarr'))
612
    stop("Please install pizzarr package!: devtools::install_github('keller-mark/pizzarr')")
613
  
614
  # sample metadata
615
  sample_metadata <- SampleMetadata(object)
616
  
617
  # create zarr
618
  if(verbose)
619
    message("Zarr store: ", zarr_path)
620
  zarr.array <- pizzarr::zarr_open(store = zarr_path)
621
  
622
  # create metadata
623
  zarr.array$create_group("metadata")
624
  object@metadata <- writeZarrArrayInMetadata(object = Metadata(object, type = "all"), 
625
                                              zarr_path,
626
                                              name = "metadata",
627
                                              chunkdim=chunkdim, 
628
                                              level=level,
629
                                              as.sparse=as.sparse,
630
                                              with.dimnames=TRUE,
631
                                              verbose=verbose, 
632
                                              replace=replace)
633
  
634
  # iterate over assays
635
  assay_names <- vrAssayNames(object, assay = assay)
636
  for(assy in assay_names){
637
    
638
    # get assay object
639
    assay_object <- object[[assy]]
640
    
641
    # create assay group in h5
642
    zarr.array$create_group(assy)
643
    
644
    # get data and write
645
    assay_object <- writeZarrArrayInVrData(object = assay_object, 
646
                                           zarr_path,
647
                                           name = assy,
648
                                           chunkdim=chunkdim, 
649
                                           level=level,
650
                                           as.sparse=as.sparse,
651
                                           with.dimnames=TRUE,
652
                                           verbose=verbose,
653
                                           replace=replace)
654
    
655
    # get image data and write
656
    assay_object <- writeZarrArrayInImage(object = assay_object,
657
                                          zarr_path,
658
                                          name = assy, 
659
                                          chunkdim=chunkdim, 
660
                                          level=level,
661
                                          as.sparse=as.sparse,
662
                                          with.dimnames=FALSE,
663
                                          verbose=verbose,
664
                                          replace=replace)
665
    
666
    # write assay back
667
    object[[assy]] <- assay_object
668
    
669
  }
670
  object
671
}
672
673
#' writeZarrArrayInMetadata
674
#'
675
#' @noRd
676
writeZarrArrayInMetadata <- function(object, 
677
                                     zarr_path,
678
                                     name,
679
                                     chunkdim, 
680
                                     level,
681
                                     as.sparse,
682
                                     with.dimnames=FALSE,
683
                                     verbose, 
684
                                     replace = FALSE){
685
  
686
  # check DelayedDataFrame
687
  if(!requireNamespace('ZarrDataFrame'))
688
    stop("Please install ZarrDataFrame package!: devtools::install_github('BIMSBbioinfo/ZarrDataFrame')")
689
  if(!requireNamespace('ZarrArray'))
690
    stop("Please install ZarrArray package!: devtools::install_github('BIMSBbioinfo/ZarrArray')")
691
  if(!requireNamespace('pizzarr'))
692
    stop("Please install pizzarr package!: devtools::install_github('keller-mark/pizzarr')")
693
  
694
  # iterate over all metadata slots
695
  slot_names <- slotNames(object)
696
  for(sn in slot_names){
697
    meta.data <- methods::slot(object, name = sn)
698
    if(!inherits(meta.data, c("DataFrame", "ZarrDataFrame")) || replace){
699
      if(nrow(meta.data) > 0){
700
        meta.data_list <- list()
701
        zarr.array <- pizzarr::zarr_open(store = zarr_path)
702
        if(verbose)
703
          message("Writing ", sn, " Metadata")
704
        zarr.array$create_group(paste0(name, "/", sn))
705
        
706
        # write rownames first if they exist, and there is no id column
707
        if(!is.null(rownames(meta.data)) && !("id" %in% colnames(meta.data))){
708
          cur_column <- as.array(rownames(meta.data))
709
          meta.data_list[["id"]] <- 
710
            ZarrArray::writeZarrArray(cur_column, 
711
                                      zarr_path, 
712
                                      name = paste0(name, "/", sn, "/id"),
713
                                      chunkdim=chunkdim, 
714
                                      level=level,
715
                                      as.sparse=as.sparse,
716
                                      with.dimnames=FALSE,
717
                                      verbose=FALSE)
718
        }
719
        
720
        # write rest of the columns
721
        for(i in seq_len(ncol(meta.data))){
722
          if(inherits(meta.data,"data.table")){
723
            cur_column <- as.array(as.vector(subset(meta.data, select = colnames(meta.data)[i]))[[1]])
724
          } else {
725
            cur_column <- as.array(meta.data[,i])
726
          }
727
          meta.data_list[[colnames(meta.data)[i]]] <- 
728
            ZarrArray::writeZarrArray(cur_column, 
729
                                      zarr_path, 
730
                                      name = paste0(name, "/", sn, "/", colnames(meta.data)[i]),
731
                                      chunkdim=chunkdim, 
732
                                      level=level,
733
                                      as.sparse=as.sparse,
734
                                      with.dimnames=FALSE,
735
                                      verbose=FALSE)
736
        }
737
        methods::slot(object, name = sn) <- 
738
          ZarrDataFrame::ZarrDataFrame(meta.data_list)
739
      }
740
    }
741
  }
742
  
743
  return(object)
744
}
745
746
#' writeZarrArrayInVrData
747
#'
748
#' @noRd
749
writeZarrArrayInVrData <- function(object, 
750
                                   zarr_path,
751
                                   name,
752
                                   chunkdim, 
753
                                   level,
754
                                   as.sparse,
755
                                   with.dimnames=FALSE,
756
                                   verbose, 
757
                                   replace = FALSE){
758
  
759
  # check packages
760
  if(!requireNamespace('ZarrArray'))
761
    stop("Please install ZarrArray package!: devtools::install_github('BIMSBbioinfo/ZarrArray')")
762
  
763
  # check if there is a data or rawdata slot in assay object
764
  catch_connect1 <- try(slot(object, name = "data"), silent = TRUE)
765
  catch_connect2 <- try(slot(object, name = "rawdata"), silent = TRUE)
766
  
767
  # get data with a specific feature
768
  if(!is(catch_connect1, 'try-error') && !methods::is(catch_connect1,'error')){
769
    
770
    feature_types <- vrFeatureTypeNames(object)
771
    for(feat in feature_types){
772
      
773
      # raw data
774
      a <- vrData(object, feat_type = feat, norm = FALSE)
775
      if(!inherits(a, "DelayedArray") || replace){
776
        if(verbose)
777
          message("Writing '", vrAssayNames(object), "' data")
778
        a <- ZarrArray::writeZarrArray(a, 
779
                                       zarr_path, 
780
                                       name = paste0(name, "/", feat),
781
                                       chunkdim=chunkdim, 
782
                                       level=level,
783
                                       as.sparse=as.sparse,
784
                                       with.dimnames=with.dimnames,
785
                                       verbose=FALSE)
786
        object@data[[feat]] <- a   
787
      }
788
      
789
      # normalized data
790
      a <- vrData(object, feat_type = feat, norm = TRUE)
791
      if(!inherits(a, "DelayedArray") || replace){
792
        if(verbose)
793
          message("Writing '", vrAssayNames(object), "' normalized data")
794
        a <- ZarrArray::writeZarrArray(a, 
795
                                       zarr_path, 
796
                                       name = paste0(name, "/", feat, "_norm"),
797
                                       chunkdim=chunkdim, 
798
                                       level=level,
799
                                       as.sparse=as.sparse,
800
                                       with.dimnames=with.dimnames,
801
                                       verbose=FALSE)
802
        object@data[[paste0(feat, "_norm")]] <- a  
803
      }
804
    }
805
    
806
  } else if(!is(catch_connect2, 'try-error') && !methods::is(catch_connect2,'error')){
807
    
808
    # raw data
809
    a <- vrData(object, norm = FALSE)
810
    if(!inherits(a, "DelayedArray") || replace){
811
      if(verbose)
812
        message("Writing '", vrAssayNames(object), "' data")
813
      a <- ZarrArray::writeZarrArray(a, 
814
                                     zarr_path, 
815
                                     name = paste0(name, "/rawdata"),
816
                                     chunkdim=chunkdim, 
817
                                     level=level,
818
                                     as.sparse=as.sparse,
819
                                     with.dimnames=TRUE,
820
                                     verbose=FALSE)
821
      object@rawdata <- a   
822
    }
823
    
824
    # normalized data
825
    a <- vrData(object, norm = TRUE)
826
    if(!inherits(a, "DelayedArray") || replace){
827
      if(verbose)
828
        message("Writing '", vrAssayNames(object), "' normalized data")
829
      a <- ZarrArray::writeZarrArray(a, 
830
                                     zarr_path, 
831
                                     name = paste0(name, "/normdata"),
832
                                     chunkdim=chunkdim, 
833
                                     level=level,
834
                                     as.sparse=as.sparse,
835
                                     with.dimnames=TRUE,
836
                                     verbose=FALSE)
837
      object@normdata <- a 
838
    }
839
    
840
  }
841
  
842
  return(object)
843
}
844
845
#' writeZarrArrayInImage
846
#' 
847
#' @noRd
848
writeZarrArrayInImage <- function(object, 
849
                                  zarr_path,
850
                                  name ,
851
                                  chunkdim, 
852
                                  level,
853
                                  as.sparse,
854
                                  with.dimnames=FALSE,
855
                                  verbose, 
856
                                  replace = FALSE){
857
  
858
  # check packages
859
  if(!requireNamespace('ImageArray'))
860
    stop("Please install ImageArray package!: devtools::install_github('BIMSBbioinfo/ImageArray')")
861
  if(!requireNamespace('ZarrArray'))
862
    stop("Please install ZarrArray package!: devtools::install_github('BIMSBbioinfo/ZarrArray')")
863
  if(!requireNamespace('pizzarr'))
864
    stop("Please install pizzarr package!: devtools::install_github('keller-mark/pizzarr')")
865
  
866
  # for each spatial system
867
  spatial_names <- vrSpatialNames(object)
868
  for(spat in spatial_names){
869
    
870
    # open group for spatial system
871
    zarr.array <- pizzarr::zarr_open(store = zarr_path)
872
    zarr.array$create_group(paste0(name, "/", spat))
873
    
874
    # write coordinates 
875
    coords <- vrCoordinates(object, spatial_name = spat)
876
    if(!inherits(coords, c("DelayedArray", "IterableMatrix")) || replace){
877
      if(verbose)
878
        message("Writing '", name, "' coordinates")
879
      coords <- ZarrArray::writeZarrArray(coords,
880
                                          zarr_path,
881
                                          name = paste0(name, "/", spat, "/coords"),
882
                                          chunkdim=chunkdim,
883
                                          level=level,
884
                                          as.sparse=as.sparse,
885
                                          with.dimnames=TRUE,
886
                                          verbose=FALSE)
887
      vrCoordinates(object, spatial_name = spat) <- coords
888
    }
889
    
890
    # for each channel
891
    channels <- vrImageChannelNames(object, name = spat)
892
    if(!all(grepl("No Channels", channels))){
893
      for(ch in channels){
894
        
895
        # get image and write to h5
896
        img <- vrImages(object, name = spat, channel = ch, as.raster = TRUE)
897
        
898
        # write image
899
        if(!inherits(img, "Image_Array") || replace){
900
          if(verbose)
901
            message("Writing '", name, "' image channel '", ch, "' for spatial system '", spat,"'")
902
          img <- ImageArray::writeImageArray(img,
903
                                             output = gsub(".zarr$", "", zarr_path),
904
                                             name = paste0(name, "/", spat, "/", ch), 
905
                                             format = "ZarrImageArray", 
906
                                             replace = FALSE, 
907
                                             chunkdim=chunkdim,
908
                                             level=level,
909
                                             as.sparse=as.sparse,
910
                                             verbose=FALSE)
911
          suppressWarnings({
912
            vrImages(object, name = spat, channel = ch) <- img 
913
          })
914
        }
915
      } 
916
    }
917
  }
918
  
919
  return(object)
920
}
921
922
####
923
## get links ####
924
####
925
926
#' .get_unique_links
927
#'
928
#' @noRd
929
.get_unique_links <- function(object, assay = NULL)
930
{
931
  # assay names
932
  assay_names <- vrAssayNames(object, assay = assay)
933
  
934
  # set links
935
  all_links <- NULL
936
  
937
  # get metadata path 
938
  all_links <- c(all_links, .get_unique_metadata_links(Metadata(object, type = "all")))
939
  
940
  # iterate over assays
941
  for(assy in assay_names){
942
    
943
    # get data and image links
944
    all_links <- c(all_links, .get_unique_data_links(object[[assy]]))
945
    all_links <- c(all_links, .get_unique_image_links(object[[assy]]))
946
    
947
  }
948
  
949
  # return
950
  unique(all_links[all_links != "try-error"])
951
}
952
953
#' .get_unique_metadata_links
954
#'
955
#' @noRd
956
.get_unique_metadata_links <- function(object){
957
  slot_names <- slotNames(object)
958
  path_list <- NULL
959
  for(sn in slot_names){
960
    cur_path <- try(getPath(methods::slot(object, name = sn)), silent = TRUE)
961
    path_list <- c(path_list, ifelse(is(cur_path, "try-error"), "try-error", cur_path))
962
  }
963
  path_list
964
}
965
966
#' .get_unique_data_links
967
#'
968
#' @noRd
969
.get_unique_data_links <- function(object){
970
  
971
  # check if there is a data or rawdata slot in assay object
972
  catch_connect1 <- try(slot(object, name = "data"), silent = TRUE)
973
  catch_connect2 <- try(slot(object, name = "rawdata"), silent = TRUE)
974
  
975
  # get data with a specific feature
976
  all_links <- NULL
977
  if(!is(catch_connect1, 'try-error') && !methods::is(catch_connect1,'error')){
978
    
979
    feature_types <- vrFeatureTypeNames(object)
980
    for(feat in feature_types){
981
      cur_path <- try(getPath(vrData(object, feat_type = feat, norm = FALSE)), silent = TRUE)
982
      all_links <- c(all_links, ifelse(is(cur_path, "try-error"), "try-error", cur_path))
983
      cur_path <- try(getPath(vrData(object, feat_type = feat, norm = TRUE)), silent = TRUE)
984
      all_links <- c(all_links, ifelse(is(cur_path, "try-error"), "try-error", cur_path))
985
    }
986
  } else if(!is(catch_connect2, 'try-error') && !methods::is(catch_connect2,'error')){
987
    cur_path <- try(getPath(vrData(object, norm = FALSE)), silent = TRUE)
988
    all_links <- c(all_links, ifelse(is(cur_path, "try-error"), "try-error", cur_path))
989
    cur_path <- try(getPath(vrData(object, norm = TRUE)), silent = TRUE)
990
    all_links <- c(all_links, ifelse(is(cur_path, "try-error"), "try-error", cur_path))
991
    
992
  }
993
  
994
  # return
995
  return(all_links)
996
}
997
998
#' .get_unique_image_links
999
#'
1000
#' @noRd
1001
.get_unique_image_links <- function(object){
1002
  
1003
  # links
1004
  all_links <- NULL
1005
  
1006
  # for each spatial system
1007
  spatial_names <- vrSpatialNames(object)
1008
  for(spat in spatial_names){
1009
    
1010
    # for each channel
1011
    channels <- vrImageChannelNames(object, name = spat)
1012
    for(ch in channels){
1013
      cur_path <- try(DelayedArray::path(vrImages(object, name = spat, channel = ch, as.raster = TRUE)), silent = TRUE)
1014
      all_links <- c(all_links, ifelse(is(cur_path, "try-error"), "try-error", cur_path))
1015
    }
1016
  }
1017
  
1018
  # return
1019
  return(all_links)
1020
}
1021
1022
getPath <- function(object){
1023
  if(inherits(object, "DelayedArray")){
1024
    return(DelayedArray::path(object))
1025
  } else if(inherits(object, c("DataFrame","HDF5DataFrame", "ZarrDataFrame"))){
1026
    return(getDataFramePath(object))
1027
  } else if(inherits(object, "IterableMatrix")){
1028
    return(getIterableMatrixPath(object))
1029
  } else {
1030
    stop()
1031
  }
1032
}
1033
1034
getIterableMatrixPath <- function(object){
1035
  if(!inherits(object, "IterableMatrix")){
1036
    stop("object should be an object of IterableMatrix")
1037
  }
1038
  slot_names <- slotNames(object)
1039
  if("path" %in% slot_names){
1040
    return(object@path)
1041
  } else if("matrix" %in% slot_names){
1042
    return(getIterableMatrixPath(object@matrix))
1043
  } else if("matrix_list" %in% slot_names){
1044
    return(unlist(lapply(object@matrix_list, getIterableMatrixPath)))
1045
  }
1046
}
1047
1048
getDataFramePath <- function(object){
1049
  slot_names <- slotNames(object)
1050
  if("listData" %in% slot_names){
1051
    return(unlist(DelayedArray::seedApply(object@listData, getDataFramePath)))
1052
  } else if("seed" %in% slotNames(object)){
1053
    return(getDataFramePath(object@seed))
1054
  } else if("path" %in% slotNames(object)){
1055
    return(DelayedArray::path(object))
1056
  } else if("seeds" %in% slotNames(object)){
1057
    return(unlist(DelayedArray::seedApply(object@seeds, getDataFramePath)))
1058
  } else {
1059
    return(NULL)
1060
  }
1061
}
1062
1063
####
1064
## shorten links ####
1065
####
1066
1067
#' shorten_assay_links
1068
#'
1069
#' @noRd
1070
shorten_metadata_links <- function(object)
1071
{
1072
  # iterate over all metadata slots
1073
  slot_names <- slotNames(object)
1074
  for(sn in slot_names){
1075
    meta.data <- methods::slot(object, name = sn)
1076
    if(nrow(meta.data) > 0){
1077
      for(i in seq_len(ncol(meta.data))){
1078
        meta.data[[colnames(meta.data)[i]]] <- 
1079
          modify_seeds(meta.data[[colnames(meta.data)[i]]],
1080
                       function(x) {
1081
                         x@path <- basename(DelayedArray::path(x))
1082
                         x
1083
                       })
1084
      }
1085
    }
1086
    methods::slot(object, name = sn) <- meta.data
1087
  }
1088
  object
1089
}
1090
1091
#' shorten_assay_links
1092
#'
1093
#' @noRd
1094
shorten_assay_links <- function(object)
1095
{
1096
  # data
1097
  
1098
  # check if there is a data or rawdata slot in assay object
1099
  catch_connect1 <- try(slot(object, name = "data"), silent = TRUE)
1100
  catch_connect2 <- try(slot(object, name = "rawdata"), silent = TRUE)
1101
  
1102
  # get data with a specific feature
1103
  if(!is(catch_connect1, 'try-error') && !methods::is(catch_connect1,'error')){
1104
    
1105
    feature_types <- vrFeatureTypeNames(object)
1106
    for(feat in feature_types){
1107
      
1108
      object@data[[feat]] <- modify_seeds(object@data[[feat]],
1109
                                     function(x) {
1110
                                       shorten_assay_links_data(x)
1111
                                     })
1112
      object@data[[paste0(feat, "_norm")]] <- modify_seeds(object@data[[paste0(feat, "_norm")]],
1113
                                      function(x) {
1114
                                        shorten_assay_links_data(x)
1115
                                      })  
1116
      
1117
    }
1118
    
1119
  } else if(!is(catch_connect2, 'try-error') && !methods::is(catch_connect2,'error')){
1120
    object@rawdata <- modify_seeds(object@rawdata,
1121
                                   function(x) {
1122
                                     shorten_assay_links_data(x)
1123
                                   })
1124
    object@normdata <- modify_seeds(object@normdata,
1125
                                    function(x) {
1126
                                      shorten_assay_links_data(x)
1127
                                    })  
1128
  }
1129
  
1130
  # images
1131
  object <- shorten_assay_links_images(object)
1132
  
1133
  # return
1134
  object
1135
}
1136
1137
#' shorten_assay_links_images
1138
#'
1139
#' @noRd
1140
shorten_assay_links_images <- function(object){
1141
  
1142
  # for each spatial system
1143
  spatial_names <- vrSpatialNames(object)
1144
  for(spat in spatial_names){
1145
    
1146
    # coordinates
1147
    # TODO: replace method for vrCoordinates fail with ZarrArray
1148
    object@image[[spat]]@coords <-
1149
      modify_seeds(vrCoordinates(object, spatial_name = spat),
1150
                   function(x) {
1151
                     shorten_assay_links_data(x)
1152
                   })
1153
    
1154
    
1155
    # for each channel
1156
    channels <- vrImageChannelNames(object, name = spat)
1157
    if(!all(grepl("No Channels", channels))){
1158
      for(ch in channels){
1159
        
1160
        img <- vrImages(object, name = spat, channel = ch, as.raster = TRUE)
1161
        img <- modify_seeds(img,
1162
                            function(x) {
1163
                              ImageArray::filepath(x) <- basename(ImageArray::filepath(x))
1164
                              x
1165
                            })
1166
        suppressWarnings({
1167
          vrImages(object, name = spat, channel = ch) <- img 
1168
        })
1169
      }
1170
    }
1171
  }
1172
  
1173
  # return
1174
  return(object)
1175
}
1176
1177
shorten_assay_links_data <- function(object){
1178
  if(inherits(object, "DelayedArray")){
1179
    object@filepath <- basename(object@filepath)
1180
  } else if(inherits(object, "IterableMatrix")){
1181
    object <- shorten_assay_links_bpcells(object)
1182
  } else if("filepath" %in% slotNames(object)){
1183
    object@filepath <- basename(object@filepath)
1184
  }
1185
  return(object)
1186
}
1187
1188
shorten_assay_links_bpcells <- function(object){
1189
  if(!inherits(object, "IterableMatrix")){
1190
    stop("object should be an object of IterableMatrix")
1191
  }
1192
  slot_names <- slotNames(object)
1193
  if("path" %in% slot_names){
1194
    object@path <- basename(object@path)
1195
  } else if("matrix" %in% slot_names){
1196
    object@matrix <- shorten_assay_links_bpcells(object@matrix)
1197
  } else if("matrix_list" %in% slot_names){
1198
    object_list <- object@matrix_list
1199
    for(i in seq_len(length(object_list)))
1200
      object_list[[i]] <- shorten_assay_links_bpcells(object_list[[i]])
1201
  }
1202
  return(object)
1203
}
1204
1205
####
1206
## restore links ####
1207
####
1208
1209
#' restore_absolute_metadata_links
1210
#'
1211
#' @noRd
1212
restore_absolute_metadata_links <- function(object, dir){
1213
  
1214
  # iterate over all metadata slots
1215
  slot_names <- slotNames(object)
1216
  for(sn in slot_names){
1217
    meta.data <- methods::slot(object, name = sn)
1218
    if(nrow(meta.data) > 0){
1219
      # meta.data <- modify_seeds(meta.data,
1220
      #                           function(x) {
1221
      #                             restore_absolute_links(x,dir)
1222
      #                           })
1223
      for(i in seq_len(ncol(meta.data))){
1224
        meta.data[[colnames(meta.data)[i]]] <- 
1225
          modify_seeds(meta.data[[colnames(meta.data)[i]]],
1226
                       function(x) {
1227
                         restore_absolute_links(x,dir)
1228
                       })
1229
      }
1230
    }
1231
    methods::slot(object, name = sn) <- meta.data
1232
  }
1233
  
1234
  # return
1235
  object
1236
}
1237
1238
#' restore_absolute_assay_links
1239
#'
1240
#' @noRd
1241
restore_absolute_assay_links <- function(object, dir){
1242
  
1243
  # check if there is a data or rawdata slot in assay object
1244
  catch_connect1 <- try(slot(object, name = "data"), silent = TRUE)
1245
  catch_connect2 <- try(slot(object, name = "rawdata"), silent = TRUE)
1246
  
1247
  # get data with a specific feature
1248
  if(!is(catch_connect1, 'try-error') && !methods::is(catch_connect1,'error')){
1249
    
1250
    feature_types <- vrFeatureTypeNames(object)
1251
    for(feat in feature_types){
1252
      
1253
      object@data[[feat]] <- modify_seeds(object@data[[feat]],
1254
                                          function(x) {
1255
                                            restore_absolute_links(x, dir)
1256
                                          })
1257
      object@data[[paste0(feat, "_norm")]] <- modify_seeds(object@data[[paste0(feat, "_norm")]],
1258
                                                           function(x) {
1259
                                                             restore_absolute_links(x, dir)
1260
                                                           })  
1261
      
1262
    }
1263
    
1264
  } else if(!is(catch_connect2, 'try-error') && !methods::is(catch_connect2,'error')){
1265
    object@rawdata <- modify_seeds(object@rawdata,
1266
                                   function(x) {
1267
                                     restore_absolute_links(x, dir)
1268
                                   })
1269
    object@normdata <- modify_seeds(object@normdata,
1270
                                    function(x) {
1271
                                      restore_absolute_links(x, dir)
1272
                                    })  
1273
  }
1274
  
1275
  # images
1276
  object <- restore_absolute_assay_links_images(object, dir)
1277
  
1278
  # return
1279
  object
1280
}
1281
1282
#' restore_absolute_assay_links_images
1283
#'
1284
#' @noRd
1285
restore_absolute_assay_links_images <- function(object, dir){
1286
  
1287
  # for each spatial system
1288
  spatial_names <- vrSpatialNames(object)
1289
  for(spat in spatial_names){
1290
    
1291
    # coordinates
1292
    # TODO: replace method for vrCoordinates fail with ZarrArray
1293
    object@image[[spat]]@coords <-
1294
      modify_seeds(vrCoordinates(object, spatial_name = spat),
1295
                   function(x) {
1296
                     restore_absolute_links(x, dir)
1297
                   })
1298
    
1299
    # for each channel
1300
    channels <- vrImageChannelNames(object, name = spat)
1301
    if(!all(grepl("No Channels", channels))){
1302
      for(ch in channels){
1303
        
1304
        img <- vrImages(object, name = spat, channel = ch, as.raster = TRUE)
1305
        img <- modify_seeds(img,
1306
                            function(x) {
1307
                              ImageArray::filepath(x) <- restore_absolute_links_images(ImageArray::filepath(x), dir)
1308
                              x
1309
                            })
1310
        suppressWarnings({
1311
          vrImages(object, name = spat, channel = ch) <- img 
1312
        })
1313
      } 
1314
    }
1315
  }
1316
  
1317
  # return
1318
  return(object)
1319
}
1320
1321
#' restore_absolute_links
1322
#'
1323
#' @noRd
1324
restore_absolute_links <- function(x, dir){
1325
  if(inherits(x, "DelayedArray")){
1326
    x@filepath <- basename(x@filepath)
1327
  } else if(inherits(x, "IterableMatrix")){
1328
    x <- updateIterableMatrixPath(x, basename)
1329
  } else if("filepath" %in% slotNames(x)){
1330
    x@filepath <- basename(x@filepath)
1331
  } else if("path" %in% slotNames(x)){
1332
    x@path <- basename(x@path)
1333
  } else if(inherits(x, "IterableMatrix")){
1334
    x <- updateDataFramePath(x, basename)
1335
  }
1336
1337
  # check object
1338
  if (!is(x, c("Array")) && !is(x, c("IterableMatrix")) && !is(x, c("HDF5DataFrame")) && !is(x, c("ZarrDataFrame")) && !is(x, "HDF5ColumnSeed") && !is(x, "ZarrColumnSeed"))
1339
    stop("object is not DelayedArray or DelayedArraySeed")
1340
  
1341
  # get path
1342
  if(inherits(x, "DelayedArray") || "filepath" %in% slotNames(x)){
1343
    file_path <- file.path(dir, x@filepath)
1344
  } else if(inherits(x, c("HDF5ColumnSeed", "ZarrColumnSeed"))){
1345
    file_path <- file.path(dir, x@path)
1346
  } else  if(inherits(x, "IterableMatrix")){
1347
    file_path <- file.path(dir, getIterableMatrixPath(x))
1348
  } else if(inherits(x, c("HDF5DataFrame", "ZarrDataFrame"))){
1349
    file_path <- file.path(dir, getDataFramePath(x))
1350
  }
1351
1352
  ## file_path_as_absolute() will fail if the file does
1353
  ## not exist.
1354
  if (!file.exists(file_path))
1355
    stop("Object points to an HDF5 file ",
1356
              "that does not exist: ", file_path)
1357
  if(inherits(x, "DelayedArray") || "filepath" %in% slotNames(x)){
1358
    x@filepath <- file_path_as_absolute(file_path)
1359
    msg <- validate_absolute_path(x@filepath, paste0("'filepath' slot of Object"))
1360
  } else if(inherits(x, c("HDF5ColumnSeed", "ZarrColumnSeed"))){
1361
    x@path <- file_path_as_absolute(file_path)
1362
    msg <- validate_absolute_path(x@path, paste0("'path' slot of Object"))
1363
  } else if(inherits(x, "IterableMatrix")){
1364
    x <- updateIterableMatrixPath(x, file_path_as_absolute(file_path))
1365
    msg <- validate_absolute_path(getIterableMatrixPath(x), paste0("'filepath' slot of Object"))
1366
  } else if(inherits(x, c("HDF5DataFrame", "ZarrDataFrame"))){
1367
    x@path <- file_path_as_absolute(file_path)
1368
    msg <- validate_absolute_path(getDataFramePath(x), paste0("'path' slot of Object"))
1369
  }
1370
1371
  # validate
1372
  if (!isTRUE(msg))
1373
    stop(msg)
1374
  
1375
  # return
1376
  x
1377
}
1378
1379
#' restore_absolute_links_images
1380
#'
1381
#' @noRd
1382
restore_absolute_links_images <- function(file_path, dir){
1383
  file_path <- basename(file_path)
1384
  
1385
  # get path
1386
  file_path <- file.path(dir, file_path)
1387
  
1388
  ## file_path_as_absolute() will fail if the file does
1389
  ## not exist.
1390
  if (!file.exists(file_path))
1391
    stop("file_path doesnt exist")
1392
  file_path <- file_path_as_absolute(file_path)
1393
  
1394
  # validate
1395
  msg <- validate_absolute_path(file_path, paste0("'filepath' slot of object"))
1396
  if (!isTRUE(msg))
1397
    stop(msg)
1398
  file_path
1399
}
1400
1401
updateIterableMatrixPath <- function(object, FUN, ...){
1402
  if(!inherits(object, "IterableMatrix")){
1403
    stop("object should be an object of IterableMatrix")
1404
  }
1405
  slot_names <- slotNames(object)
1406
  if("path" %in% slot_names){
1407
    if(is.function(FUN)){
1408
      object@path <- FUN(object@path, ...)
1409
    } else {
1410
      object@path <- FUN
1411
    }
1412
  } else if("matrix" %in% slot_names){
1413
    object@matrix <- updateIterableMatrixPath(object@matrix, FUN, ...)
1414
  } else if("matrix_list" %in% slot_names){
1415
    object_list <- object@matrix_list
1416
    for(i in seq_len(length(object_list))){
1417
      object_list[[i]] <- updateIterableMatrixPath(object_list[[i]], FUN, ...)
1418
    }
1419
    object@matrix_list <- object_list
1420
  }
1421
  return(object)
1422
}
1423
1424
updateDataFramePath <- function(object, FUN, ...){
1425
  slot_names <- slotNames(object)
1426
  if("path" %in% slot_names){
1427
    if(is.function(FUN)){
1428
      object@path <- FUN(object@path, ...)
1429
    } else {
1430
      object@path <- FUN
1431
    }
1432
  } else if("seed" %in% slot_names){
1433
    object@seed <- updateIterableMatrixPath(object@seed, FUN, ...)
1434
  } else if("listData" %in% slot_names){
1435
    object_list <- object@listData
1436
    for(i in seq_len(length(object_list))){
1437
      object_list[[i]] <- updateDataFramePath(object_list[[i]], FUN, ...)
1438
    }
1439
    object@listData <- object_list
1440
  } else if("seeds" %in% slot_names){
1441
    object_list <- object@seeds
1442
    for(i in seq_len(length(object_list))){
1443
      object_list[[i]] <- updateDataFramePath(object_list[[i]], FUN, ...)
1444
    }
1445
    object@seeds <- object_list
1446
  }
1447
  return(object)
1448
}
1449
1450
####
1451
# Auxiliary ####
1452
####
1453
1454
#' isTRUEorFALSE
1455
#'
1456
#' @noRd
1457
isTRUEorFALSE <- function (x) {
1458
  is.logical(x) && length(x) == 1L && !is.na(x)
1459
}
1460
1461
#' isSingleString
1462
#'
1463
#' @noRd
1464
isSingleString <- function (x) {
1465
  is.character(x) && length(x) == 1L && !is.na(x)
1466
}
1467
1468
#' create_dir
1469
#'
1470
#' @noRd
1471
create_dir <- function (dir){
1472
  if (file.exists(dir)) 
1473
    stop("\"", dir, "\" already exists and is a file, ", 
1474
         "not a directory")
1475
  if (!suppressWarnings(dir.create(dir))) 
1476
    stop("cannot create directory \"", dir, "\"")
1477
}
1478
1479
#' replace_dir
1480
#'
1481
#' @noRd
1482
replace_dir <- function(dir){
1483
  if (unlink(dir, recursive = TRUE) != 0L) 
1484
    stop("failed to delete directory \"", dir, "\"")
1485
  if (!suppressWarnings(dir.create(dir))) 
1486
    stop("cannot create directory \"", dir, "\"")
1487
}
1488
1489
#' check_and_delete_files
1490
#'
1491
#' @noRd
1492
check_and_delete_files <- function (rds_path, h5_path, replace) 
1493
{
1494
  if (dir.exists(rds_path) || dir.exists(h5_path)) 
1495
    stop("\"", rds_path, "\" and/or \"", h5_path, "\" ", 
1496
         "already exist and are directories, not files")
1497
  if (!(file.exists(rds_path) || file.exists(h5_path))) 
1498
    return()
1499
  if (!replace) 
1500
    stop("Files \"", rds_path, "\" and/or \"", h5_path, 
1501
         "\" ", "already exist. Use a different 'prefix' or use ", 
1502
         "'replace=TRUE' if you really want to replace them.")
1503
  if (unlink(rds_path, recursive = TRUE) != 0L) 
1504
    stop("failed to delete file \"", rds_path, "\"")
1505
  if (unlink(h5_path, recursive = TRUE) != 0L) 
1506
    stop("failed to delete file \"", h5_path, "\"")
1507
}
1508
1509
#' stop_if_bad_dir
1510
#'
1511
#' @noRd
1512
stop_if_bad_dir <- function(dir, prefix = "")
1513
{
1514
  .THE_EXPECTED_STUFF <- c(
1515
    "an OnDisk based VoltRon object ",
1516
    "previously saved with saveVoltRon",
1517
    "()"
1518
  )
1519
  if (prefix == "") {
1520
    msg <- c("directory \"", dir, "\" does not seem to contain ",
1521
             .THE_EXPECTED_STUFF)
1522
  } else {
1523
    msg <- c("Directory \"", dir, "\" does not seem to contain ",
1524
             head(.THE_EXPECTED_STUFF, n=-1L),
1525
             "(..., prefix=\"", prefix, "\"). ",
1526
             "Make sure you're using the same 'prefix' ",
1527
             "that was used when the object was saved.")
1528
  }
1529
  stop(msg)
1530
}
1531
1532
#' file_path_as_absolute
1533
#'
1534
#' @noRd
1535
file_path_as_absolute <- function (x) 
1536
{
1537
  if (length(x) != 1L) 
1538
    stop("'x' must be a single character string")
1539
  if (!file.exists(epath <- path.expand(x))) 
1540
    stop(gettextf("file '%s' does not exist", x), domain = NA)
1541
  normalizePath(epath, "/", TRUE)
1542
}
1543
1544
#' validate_absolute_path
1545
#'
1546
#' @noRd
1547
validate_absolute_path <- function(path, what="'path'")
1548
{
1549
  if (!(isSingleString(path) && nzchar(path)))
1550
    return(paste0(what, " must be a single non-empty string"))
1551
  ## Check that 'path' points to an HDF5 file that is accessible.
1552
  if (!file.exists(path))
1553
    return(paste0(what, " (\"", path, "\") must be the path to ",
1554
                  "an existing HDF5 file"))
1555
  if (dir.exists(path) && !grepl(".zarr$", path))
1556
    return(paste0(what, " (\"", path, "\") must be the path to ",
1557
                  "an HDF5 file, not a directory"))
1558
  if (path != file_path_as_absolute(path))
1559
    return(paste0(what, " (\"", path, "\") must be the absolute ",
1560
                  "canonical path the HDF5 file"))
1561
  TRUE
1562
}
1563
1564
h5Gexists <- function (file, group) 
1565
{
1566
  if(!requireNamespace('rhdf5'))
1567
    stop("Please install rhdf5 package!: BiocManager::install('rhdf5')")
1568
  
1569
  loc = rhdf5::H5Fopen(file)
1570
  on.exit(rhdf5::H5close())
1571
  if (is.character(group)) {
1572
    return(rhdf5::H5Lexists(loc, group))
1573
  } else {
1574
    stop("\"dataset\" argument must be a character vector of length one.")
1575
  }
1576
}
1577
1578
h5Dexists <- function (file, dataset) 
1579
{
1580
  if(!requireNamespace('rhdf5'))
1581
    stop("Please install rhdf5 package!: BiocManager::install('rhdf5')")
1582
  
1583
  loc = rhdf5::H5Fopen(file)
1584
  on.exit(rhdf5::H5close())
1585
  if (is.character(dataset)) {
1586
    return(rhdf5::H5Lexists(loc, dataset))
1587
  } else {
1588
    stop("\"dataset\" argument must be a character vector of length one.")
1589
  }
1590
}