rslearn 0.0.17__tar.gz → 0.0.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rslearn-0.0.17/rslearn.egg-info → rslearn-0.0.18}/PKG-INFO +1 -1
- {rslearn-0.0.17 → rslearn-0.0.18}/pyproject.toml +1 -1
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/config/__init__.py +2 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/config/dataset.py +49 -4
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/dataset/add_windows.py +1 -1
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/dataset/dataset.py +9 -65
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/dataset/materialize.py +5 -5
- rslearn-0.0.18/rslearn/dataset/storage/__init__.py +1 -0
- rslearn-0.0.18/rslearn/dataset/storage/file.py +202 -0
- rslearn-0.0.18/rslearn/dataset/storage/storage.py +140 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/dataset/window.py +26 -80
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/main.py +11 -36
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/anysat.py +11 -9
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/clay/clay.py +8 -9
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/clip.py +18 -15
- rslearn-0.0.18/rslearn/models/component.py +99 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/concatenate_features.py +21 -11
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/conv.py +15 -8
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/croma.py +13 -8
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/detr/detr.py +25 -14
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/dinov3.py +11 -6
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/faster_rcnn.py +19 -9
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/feature_center_crop.py +12 -9
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/fpn.py +19 -8
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/galileo/galileo.py +23 -18
- rslearn-0.0.18/rslearn/models/module_wrapper.py +60 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/molmo.py +16 -14
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/multitask.py +102 -73
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/olmoearth_pretrain/model.py +18 -12
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon.py +8 -7
- rslearn-0.0.18/rslearn/models/pick_features.py +40 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/pooling_decoder.py +22 -14
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/presto/presto.py +16 -10
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/presto/single_file_presto.py +4 -10
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/prithvi.py +12 -8
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/resize_features.py +21 -7
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/sam2_enc.py +11 -9
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/satlaspretrain.py +15 -9
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/simple_time_series.py +31 -17
- rslearn-0.0.18/rslearn/models/singletask.py +58 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/ssl4eo_s12.py +15 -10
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/swin.py +22 -13
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/terramind.py +24 -7
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/trunk.py +6 -3
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/unet.py +18 -9
- rslearn-0.0.18/rslearn/models/upsample.py +48 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/all_patches_dataset.py +22 -18
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/dataset.py +69 -54
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/lightning_module.py +51 -32
- rslearn-0.0.18/rslearn/train/model_context.py +54 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/prediction_writer.py +111 -41
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/classification.py +34 -15
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/detection.py +24 -31
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/embedding.py +33 -29
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/multi_task.py +7 -7
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/per_pixel_regression.py +41 -19
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/regression.py +38 -21
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/segmentation.py +33 -15
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/task.py +3 -2
- {rslearn-0.0.17 → rslearn-0.0.18/rslearn.egg-info}/PKG-INFO +1 -1
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn.egg-info/SOURCES.txt +5 -2
- rslearn-0.0.17/rslearn/dataset/index.py +0 -173
- rslearn-0.0.17/rslearn/models/module_wrapper.py +0 -91
- rslearn-0.0.17/rslearn/models/pick_features.py +0 -46
- rslearn-0.0.17/rslearn/models/registry.py +0 -22
- rslearn-0.0.17/rslearn/models/singletask.py +0 -51
- rslearn-0.0.17/rslearn/models/upsample.py +0 -35
- {rslearn-0.0.17 → rslearn-0.0.18}/LICENSE +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/NOTICE +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/README.md +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/arg_parser.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/const.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/aws_landsat.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/aws_open_data.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/aws_sentinel1.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/climate_data_store.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/copernicus.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/data_source.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/earthdaily.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/earthdata_srtm.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/eurocrops.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/gcp_public_data.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/google_earth_engine.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/local_files.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/openstreetmap.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/planet.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/planet_basemap.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/planetary_computer.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/usda_cdl.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/usgs_landsat.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/utils.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/vector_source.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/worldcereal.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/worldcover.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/worldpop.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/data_sources/xyz_tiles.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/dataset/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/dataset/handler_summaries.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/dataset/manage.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/dataset/remap.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/lightning_cli.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/log_utils.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/clay/configs/metadata.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/detr/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/detr/box_ops.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/detr/matcher.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/detr/position_encoding.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/detr/transformer.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/detr/util.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/galileo/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/galileo/single_file_galileo.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/olmoearth_pretrain/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/olmoearth_pretrain/norm.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/drone.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/enmap.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/goes.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/himawari.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/intuition.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/landsat8.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/modis_terra.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/qb2_ge1.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/sentinel1.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/sentinel2.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/superdove.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/panopticon_data/sensors/wv23.yaml +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/presto/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/task_embedding.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/models/use_croma.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/py.typed +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/template_params.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/tile_stores/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/tile_stores/default.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/tile_stores/tile_store.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/callbacks/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/callbacks/adapters.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/callbacks/freeze_unfreeze.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/callbacks/gradients.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/callbacks/peft.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/data_module.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/optimizer.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/scheduler.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/tasks/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/concatenate.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/crop.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/flip.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/mask.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/normalize.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/pad.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/select_bands.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/sentinel1.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/train/transforms/transform.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/__init__.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/array.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/feature.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/fsspec.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/geometry.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/get_utm_ups_crs.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/grid_index.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/jsonargparse.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/mp.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/raster_format.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/rtree_index.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/spatial_index.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/sqlite_index.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/time.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn/utils/vector_format.py +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn.egg-info/dependency_links.txt +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn.egg-info/entry_points.txt +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn.egg-info/requires.txt +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/rslearn.egg-info/top_level.txt +0 -0
- {rslearn-0.0.17 → rslearn-0.0.18}/setup.cfg +0 -0
|
@@ -31,6 +31,7 @@ from rslearn.utils.vector_format import VectorFormat
|
|
|
31
31
|
|
|
32
32
|
if TYPE_CHECKING:
|
|
33
33
|
from rslearn.data_sources.data_source import DataSource
|
|
34
|
+
from rslearn.dataset.storage.storage import WindowStorageFactory
|
|
34
35
|
|
|
35
36
|
logger = get_logger("__name__")
|
|
36
37
|
|
|
@@ -132,7 +133,11 @@ class BandSetConfig(BaseModel):
|
|
|
132
133
|
bands.
|
|
133
134
|
"""
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
model_config = ConfigDict(extra="forbid")
|
|
137
|
+
|
|
138
|
+
dtype: DType = Field(
|
|
139
|
+
description="Pixel value type to store the data under. This is used during dataset materialize and model predict."
|
|
140
|
+
)
|
|
136
141
|
bands: list[str] = Field(
|
|
137
142
|
default_factory=lambda: [],
|
|
138
143
|
description="List of band names in this BandSetConfig. One of bands or num_bands must be set.",
|
|
@@ -329,7 +334,7 @@ class TimeMode(StrEnum):
|
|
|
329
334
|
class QueryConfig(BaseModel):
|
|
330
335
|
"""A configuration for querying items in a data source."""
|
|
331
336
|
|
|
332
|
-
model_config = ConfigDict(frozen=True)
|
|
337
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
333
338
|
|
|
334
339
|
space_mode: SpaceMode = Field(
|
|
335
340
|
default=SpaceMode.MOSAIC,
|
|
@@ -363,7 +368,7 @@ class QueryConfig(BaseModel):
|
|
|
363
368
|
class DataSourceConfig(BaseModel):
|
|
364
369
|
"""Configuration for a DataSource in a dataset layer."""
|
|
365
370
|
|
|
366
|
-
model_config = ConfigDict(frozen=True)
|
|
371
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
367
372
|
|
|
368
373
|
class_path: str = Field(description="Class path for the data source.")
|
|
369
374
|
init_args: dict[str, Any] = Field(
|
|
@@ -469,7 +474,7 @@ class CompositingMethod(StrEnum):
|
|
|
469
474
|
class LayerConfig(BaseModel):
|
|
470
475
|
"""Configuration of a layer in a dataset."""
|
|
471
476
|
|
|
472
|
-
model_config = ConfigDict(frozen=True)
|
|
477
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
473
478
|
|
|
474
479
|
type: LayerType = Field(description="The LayerType (raster or vector).")
|
|
475
480
|
data_source: DataSourceConfig | None = Field(
|
|
@@ -592,11 +597,51 @@ class LayerConfig(BaseModel):
|
|
|
592
597
|
return vector_format
|
|
593
598
|
|
|
594
599
|
|
|
600
|
+
class StorageConfig(BaseModel):
|
|
601
|
+
"""Configuration for the WindowStorageFactory (window metadata storage backend)."""
|
|
602
|
+
|
|
603
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
604
|
+
|
|
605
|
+
class_path: str = Field(
|
|
606
|
+
default="rslearn.dataset.storage.file.FileWindowStorageFactory",
|
|
607
|
+
description="Class path for the WindowStorageFactory.",
|
|
608
|
+
)
|
|
609
|
+
init_args: dict[str, Any] = Field(
|
|
610
|
+
default_factory=lambda: {},
|
|
611
|
+
description="jsonargparse init args for the WindowStorageFactory.",
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
def instantiate_window_storage_factory(self) -> "WindowStorageFactory":
|
|
615
|
+
"""Instantiate the WindowStorageFactory specified by this config."""
|
|
616
|
+
from rslearn.dataset.storage.storage import WindowStorageFactory
|
|
617
|
+
from rslearn.utils.jsonargparse import init_jsonargparse
|
|
618
|
+
|
|
619
|
+
init_jsonargparse()
|
|
620
|
+
parser = jsonargparse.ArgumentParser()
|
|
621
|
+
parser.add_argument("--wsf", type=WindowStorageFactory)
|
|
622
|
+
cfg = parser.parse_object(
|
|
623
|
+
{
|
|
624
|
+
"wsf": dict(
|
|
625
|
+
class_path=self.class_path,
|
|
626
|
+
init_args=self.init_args,
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
)
|
|
630
|
+
wsf = parser.instantiate_classes(cfg).wsf
|
|
631
|
+
return wsf
|
|
632
|
+
|
|
633
|
+
|
|
595
634
|
class DatasetConfig(BaseModel):
|
|
596
635
|
"""Overall dataset configuration."""
|
|
597
636
|
|
|
637
|
+
model_config = ConfigDict(extra="forbid")
|
|
638
|
+
|
|
598
639
|
layers: dict[str, LayerConfig] = Field(description="Layers in the dataset.")
|
|
599
640
|
tile_store: dict[str, Any] = Field(
|
|
600
641
|
default={"class_path": "rslearn.tile_stores.default.DefaultTileStore"},
|
|
601
642
|
description="jsonargparse configuration for the TileStore.",
|
|
602
643
|
)
|
|
644
|
+
storage: StorageConfig = Field(
|
|
645
|
+
default_factory=lambda: StorageConfig(),
|
|
646
|
+
description="jsonargparse configuration for the WindowStorageFactory.",
|
|
647
|
+
)
|
|
@@ -131,7 +131,7 @@ def add_windows_from_geometries(
|
|
|
131
131
|
f"_{time_range[0].isoformat()}_{time_range[1].isoformat()}"
|
|
132
132
|
)
|
|
133
133
|
window = Window(
|
|
134
|
-
|
|
134
|
+
storage=dataset.storage,
|
|
135
135
|
group=group,
|
|
136
136
|
name=cur_window_name,
|
|
137
137
|
projection=cur_projection,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"""rslearn dataset class."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
import
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
|
-
import tqdm
|
|
7
6
|
from upath import UPath
|
|
8
7
|
|
|
9
8
|
from rslearn.config import DatasetConfig
|
|
@@ -11,7 +10,6 @@ from rslearn.log_utils import get_logger
|
|
|
11
10
|
from rslearn.template_params import substitute_env_vars_in_string
|
|
12
11
|
from rslearn.tile_stores import TileStore, load_tile_store
|
|
13
12
|
|
|
14
|
-
from .index import DatasetIndex
|
|
15
13
|
from .window import Window
|
|
16
14
|
|
|
17
15
|
logger = get_logger(__name__)
|
|
@@ -68,80 +66,26 @@ class Dataset:
|
|
|
68
66
|
self.layers[layer_name] = layer_config
|
|
69
67
|
|
|
70
68
|
self.tile_store_config = config.tile_store
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return DatasetIndex.load_index(self.path)
|
|
69
|
+
self.storage = (
|
|
70
|
+
config.storage.instantiate_window_storage_factory().get_storage(
|
|
71
|
+
self.path
|
|
72
|
+
)
|
|
73
|
+
)
|
|
77
74
|
|
|
78
75
|
def load_windows(
|
|
79
76
|
self,
|
|
80
77
|
groups: list[str] | None = None,
|
|
81
78
|
names: list[str] | None = None,
|
|
82
|
-
|
|
83
|
-
workers: int = 0,
|
|
84
|
-
no_index: bool = False,
|
|
79
|
+
**kwargs: Any,
|
|
85
80
|
) -> list[Window]:
|
|
86
81
|
"""Load the windows in the dataset.
|
|
87
82
|
|
|
88
83
|
Args:
|
|
89
84
|
groups: an optional list of groups to filter loading
|
|
90
85
|
names: an optional list of window names to filter loading
|
|
91
|
-
|
|
92
|
-
workers: number of parallel workers, default 0 (use main thread only to load windows)
|
|
93
|
-
no_index: don't use the dataset index even if it exists.
|
|
86
|
+
kwargs: optional keyword arguments to pass to WindowStorage.get_windows.
|
|
94
87
|
"""
|
|
95
|
-
|
|
96
|
-
# We never use the index if names is set since loading the index will likely be
|
|
97
|
-
# slower than loading a few windows.
|
|
98
|
-
if not no_index and names is None:
|
|
99
|
-
dataset_index = self._get_index()
|
|
100
|
-
if dataset_index is not None:
|
|
101
|
-
return dataset_index.get_windows(groups=groups, names=names)
|
|
102
|
-
|
|
103
|
-
# Avoid directory does not exist errors later.
|
|
104
|
-
if not (self.path / "windows").exists():
|
|
105
|
-
return []
|
|
106
|
-
|
|
107
|
-
window_dirs = []
|
|
108
|
-
if not groups:
|
|
109
|
-
groups = []
|
|
110
|
-
for p in (self.path / "windows").iterdir():
|
|
111
|
-
groups.append(p.name)
|
|
112
|
-
for group in groups:
|
|
113
|
-
group_dir = self.path / "windows" / group
|
|
114
|
-
if not group_dir.exists():
|
|
115
|
-
logger.warning(
|
|
116
|
-
f"Skipping group directory {group_dir} since it does not exist"
|
|
117
|
-
)
|
|
118
|
-
continue
|
|
119
|
-
if names:
|
|
120
|
-
cur_names = names
|
|
121
|
-
else:
|
|
122
|
-
cur_names = []
|
|
123
|
-
for p in group_dir.iterdir():
|
|
124
|
-
cur_names.append(p.name)
|
|
125
|
-
|
|
126
|
-
for window_name in cur_names:
|
|
127
|
-
window_dir = group_dir / window_name
|
|
128
|
-
window_dirs.append(window_dir)
|
|
129
|
-
|
|
130
|
-
if workers == 0:
|
|
131
|
-
windows = [Window.load(window_dir) for window_dir in window_dirs]
|
|
132
|
-
else:
|
|
133
|
-
p = multiprocessing.Pool(workers)
|
|
134
|
-
outputs = p.imap_unordered(Window.load, window_dirs)
|
|
135
|
-
if show_progress:
|
|
136
|
-
outputs = tqdm.tqdm(
|
|
137
|
-
outputs, total=len(window_dirs), desc="Loading windows"
|
|
138
|
-
)
|
|
139
|
-
windows = []
|
|
140
|
-
for window in outputs:
|
|
141
|
-
windows.append(window)
|
|
142
|
-
p.close()
|
|
143
|
-
|
|
144
|
-
return windows
|
|
88
|
+
return self.storage.get_windows(groups=groups, names=names, **kwargs)
|
|
145
89
|
|
|
146
90
|
def get_tile_store(self) -> TileStore:
|
|
147
91
|
"""Get the tile store associated with this dataset.
|
|
@@ -161,7 +161,7 @@ def build_first_valid_composite(
|
|
|
161
161
|
nodata_vals: list[Any],
|
|
162
162
|
bands: list[str],
|
|
163
163
|
bounds: PixelBounds,
|
|
164
|
-
band_dtype:
|
|
164
|
+
band_dtype: npt.DTypeLike,
|
|
165
165
|
tile_store: TileStoreWithLayer,
|
|
166
166
|
projection: Projection,
|
|
167
167
|
remapper: Remapper | None,
|
|
@@ -233,7 +233,7 @@ def read_and_stack_raster_windows(
|
|
|
233
233
|
projection: Projection,
|
|
234
234
|
nodata_vals: list[Any],
|
|
235
235
|
remapper: Remapper | None,
|
|
236
|
-
band_dtype:
|
|
236
|
+
band_dtype: npt.DTypeLike,
|
|
237
237
|
resampling_method: Resampling = Resampling.bilinear,
|
|
238
238
|
) -> npt.NDArray[np.generic]:
|
|
239
239
|
"""Create a stack of extent aligned raster windows.
|
|
@@ -326,7 +326,7 @@ def build_mean_composite(
|
|
|
326
326
|
nodata_vals: list[Any],
|
|
327
327
|
bands: list[str],
|
|
328
328
|
bounds: PixelBounds,
|
|
329
|
-
band_dtype:
|
|
329
|
+
band_dtype: npt.DTypeLike,
|
|
330
330
|
tile_store: TileStoreWithLayer,
|
|
331
331
|
projection: Projection,
|
|
332
332
|
remapper: Remapper | None,
|
|
@@ -383,7 +383,7 @@ def build_median_composite(
|
|
|
383
383
|
nodata_vals: list[Any],
|
|
384
384
|
bands: list[str],
|
|
385
385
|
bounds: PixelBounds,
|
|
386
|
-
band_dtype:
|
|
386
|
+
band_dtype: npt.DTypeLike,
|
|
387
387
|
tile_store: TileStoreWithLayer,
|
|
388
388
|
projection: Projection,
|
|
389
389
|
remapper: Remapper | None,
|
|
@@ -471,7 +471,7 @@ def build_composite(
|
|
|
471
471
|
nodata_vals=nodata_vals,
|
|
472
472
|
bands=band_cfg.bands,
|
|
473
473
|
bounds=bounds,
|
|
474
|
-
band_dtype=band_cfg.dtype.
|
|
474
|
+
band_dtype=band_cfg.dtype.get_numpy_dtype(),
|
|
475
475
|
tile_store=tile_store,
|
|
476
476
|
projection=projection,
|
|
477
477
|
resampling_method=layer_cfg.resampling_method.get_rasterio_resampling(),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Storage backends for rslearn window metadata."""
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""The default file-based window storage backend."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import multiprocessing
|
|
5
|
+
|
|
6
|
+
import tqdm
|
|
7
|
+
from typing_extensions import override
|
|
8
|
+
from upath import UPath
|
|
9
|
+
|
|
10
|
+
from rslearn.dataset.window import (
|
|
11
|
+
LAYERS_DIRECTORY_NAME,
|
|
12
|
+
Window,
|
|
13
|
+
WindowLayerData,
|
|
14
|
+
get_layer_and_group_from_dir_name,
|
|
15
|
+
get_window_layer_dir,
|
|
16
|
+
)
|
|
17
|
+
from rslearn.log_utils import get_logger
|
|
18
|
+
from rslearn.utils.fsspec import open_atomic
|
|
19
|
+
from rslearn.utils.mp import star_imap_unordered
|
|
20
|
+
|
|
21
|
+
from .storage import WindowStorage, WindowStorageFactory
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def load_window(storage: "FileWindowStorage", window_dir: UPath) -> Window:
|
|
27
|
+
"""Load the window from its directory by reading metadata.json.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
storage: the underlying FileWindowStorage.
|
|
31
|
+
window_dir: the path where the window is stored.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
the window object.
|
|
35
|
+
"""
|
|
36
|
+
metadata_fname = window_dir / "metadata.json"
|
|
37
|
+
with metadata_fname.open() as f:
|
|
38
|
+
metadata = json.load(f)
|
|
39
|
+
return Window.from_metadata(storage, metadata)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FileWindowStorage(WindowStorage):
|
|
43
|
+
"""The default file-backed window storage."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, path: UPath):
|
|
46
|
+
"""Create a new FileWindowStorage.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
path: the path to the dataset.
|
|
50
|
+
"""
|
|
51
|
+
self.path = path
|
|
52
|
+
|
|
53
|
+
@override
|
|
54
|
+
def get_window_root(self, group: str, name: str) -> UPath:
|
|
55
|
+
return Window.get_window_root(self.path, group, name)
|
|
56
|
+
|
|
57
|
+
@override
|
|
58
|
+
def get_windows(
|
|
59
|
+
self,
|
|
60
|
+
groups: list[str] | None = None,
|
|
61
|
+
names: list[str] | None = None,
|
|
62
|
+
show_progress: bool = False,
|
|
63
|
+
workers: int = 0,
|
|
64
|
+
) -> list["Window"]:
|
|
65
|
+
"""Load the windows in the dataset.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
groups: an optional list of groups to filter loading
|
|
69
|
+
names: an optional list of window names to filter loading
|
|
70
|
+
show_progress: whether to show tqdm progress bar
|
|
71
|
+
workers: number of parallel workers, default 0 (use main thread only to load windows)
|
|
72
|
+
"""
|
|
73
|
+
# Avoid directory does not exist errors later.
|
|
74
|
+
if not (self.path / "windows").exists():
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
window_dirs = []
|
|
78
|
+
if not groups:
|
|
79
|
+
groups = []
|
|
80
|
+
for p in (self.path / "windows").iterdir():
|
|
81
|
+
groups.append(p.name)
|
|
82
|
+
for group in groups:
|
|
83
|
+
group_dir = self.path / "windows" / group
|
|
84
|
+
if not group_dir.exists():
|
|
85
|
+
logger.warning(
|
|
86
|
+
f"Skipping group directory {group_dir} since it does not exist"
|
|
87
|
+
)
|
|
88
|
+
continue
|
|
89
|
+
if names:
|
|
90
|
+
cur_names = names
|
|
91
|
+
else:
|
|
92
|
+
cur_names = []
|
|
93
|
+
for p in group_dir.iterdir():
|
|
94
|
+
cur_names.append(p.name)
|
|
95
|
+
|
|
96
|
+
for window_name in cur_names:
|
|
97
|
+
window_dir = group_dir / window_name
|
|
98
|
+
window_dirs.append(window_dir)
|
|
99
|
+
|
|
100
|
+
if workers == 0:
|
|
101
|
+
windows = [load_window(self, window_dir) for window_dir in window_dirs]
|
|
102
|
+
else:
|
|
103
|
+
p = multiprocessing.Pool(workers)
|
|
104
|
+
outputs = star_imap_unordered(
|
|
105
|
+
p,
|
|
106
|
+
load_window,
|
|
107
|
+
[
|
|
108
|
+
dict(storage=self, window_dir=window_dir)
|
|
109
|
+
for window_dir in window_dirs
|
|
110
|
+
],
|
|
111
|
+
)
|
|
112
|
+
if show_progress:
|
|
113
|
+
outputs = tqdm.tqdm(
|
|
114
|
+
outputs, total=len(window_dirs), desc="Loading windows"
|
|
115
|
+
)
|
|
116
|
+
windows = []
|
|
117
|
+
for window in outputs:
|
|
118
|
+
windows.append(window)
|
|
119
|
+
p.close()
|
|
120
|
+
|
|
121
|
+
return windows
|
|
122
|
+
|
|
123
|
+
@override
|
|
124
|
+
def create_or_update_window(self, window: Window) -> None:
|
|
125
|
+
window_path = self.get_window_root(window.group, window.name)
|
|
126
|
+
window_path.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
metadata_path = window_path / "metadata.json"
|
|
128
|
+
logger.debug(f"Saving window metadata to {metadata_path}")
|
|
129
|
+
with open_atomic(metadata_path, "w") as f:
|
|
130
|
+
json.dump(window.get_metadata(), f)
|
|
131
|
+
|
|
132
|
+
@override
|
|
133
|
+
def get_layer_datas(self, group: str, name: str) -> dict[str, "WindowLayerData"]:
|
|
134
|
+
window_path = self.get_window_root(group, name)
|
|
135
|
+
items_fname = window_path / "items.json"
|
|
136
|
+
if not items_fname.exists():
|
|
137
|
+
return {}
|
|
138
|
+
|
|
139
|
+
with items_fname.open() as f:
|
|
140
|
+
layer_datas = [
|
|
141
|
+
WindowLayerData.deserialize(layer_data) for layer_data in json.load(f)
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
return {layer_data.layer_name: layer_data for layer_data in layer_datas}
|
|
145
|
+
|
|
146
|
+
@override
|
|
147
|
+
def save_layer_datas(
|
|
148
|
+
self, group: str, name: str, layer_datas: dict[str, "WindowLayerData"]
|
|
149
|
+
) -> None:
|
|
150
|
+
window_path = self.get_window_root(group, name)
|
|
151
|
+
json_data = [layer_data.serialize() for layer_data in layer_datas.values()]
|
|
152
|
+
items_fname = window_path / "items.json"
|
|
153
|
+
logger.info(f"Saving window items to {items_fname}")
|
|
154
|
+
with open_atomic(items_fname, "w") as f:
|
|
155
|
+
json.dump(json_data, f)
|
|
156
|
+
|
|
157
|
+
@override
|
|
158
|
+
def list_completed_layers(self, group: str, name: str) -> list[tuple[str, int]]:
|
|
159
|
+
window_path = self.get_window_root(group, name)
|
|
160
|
+
layers_directory = window_path / LAYERS_DIRECTORY_NAME
|
|
161
|
+
if not layers_directory.exists():
|
|
162
|
+
return []
|
|
163
|
+
|
|
164
|
+
completed_layers = []
|
|
165
|
+
for layer_dir in layers_directory.iterdir():
|
|
166
|
+
layer_name, group_idx = get_layer_and_group_from_dir_name(layer_dir.name)
|
|
167
|
+
if not self.is_layer_completed(group, name, layer_name, group_idx):
|
|
168
|
+
continue
|
|
169
|
+
completed_layers.append((layer_name, group_idx))
|
|
170
|
+
|
|
171
|
+
return completed_layers
|
|
172
|
+
|
|
173
|
+
@override
|
|
174
|
+
def is_layer_completed(
|
|
175
|
+
self, group: str, name: str, layer_name: str, group_idx: int = 0
|
|
176
|
+
) -> bool:
|
|
177
|
+
window_path = self.get_window_root(group, name)
|
|
178
|
+
layer_dir = get_window_layer_dir(
|
|
179
|
+
window_path,
|
|
180
|
+
layer_name,
|
|
181
|
+
group_idx,
|
|
182
|
+
)
|
|
183
|
+
return (layer_dir / "completed").exists()
|
|
184
|
+
|
|
185
|
+
@override
|
|
186
|
+
def mark_layer_completed(
|
|
187
|
+
self, group: str, name: str, layer_name: str, group_idx: int = 0
|
|
188
|
+
) -> None:
|
|
189
|
+
window_path = self.get_window_root(group, name)
|
|
190
|
+
layer_dir = get_window_layer_dir(window_path, layer_name, group_idx)
|
|
191
|
+
# We assume the directory exists because the layer should be materialized before
|
|
192
|
+
# being marked completed.
|
|
193
|
+
(layer_dir / "completed").touch()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class FileWindowStorageFactory(WindowStorageFactory):
|
|
197
|
+
"""Factory class for FileWindowStorage."""
|
|
198
|
+
|
|
199
|
+
@override
|
|
200
|
+
def get_storage(self, ds_path: UPath) -> FileWindowStorage:
|
|
201
|
+
"""Get a FileWindowStorage for the given dataset path."""
|
|
202
|
+
return FileWindowStorage(ds_path)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Abstract classes for window metadata storage."""
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from upath import UPath
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from rslearn.dataset.window import Window, WindowLayerData
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class WindowStorage(abc.ABC):
|
|
13
|
+
"""An abstract class for the storage backend for window metadata.
|
|
14
|
+
|
|
15
|
+
This is instantiated by a WindowStorageFactory for a specific rslearn dataset.
|
|
16
|
+
|
|
17
|
+
Window metadata includes the location and time range of windows (metadata.json),
|
|
18
|
+
the window layer datas (items.json), and the completed (materialized) layers. It
|
|
19
|
+
excludes the actual materialized data. All operations involving window metadata go
|
|
20
|
+
through the WindowStorage, including enumerating windows, creating new windows, and
|
|
21
|
+
updating window layer datas during `rslearn dataset prepare` or the completed
|
|
22
|
+
layers during `rslearn dataset materialize`.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@abc.abstractmethod
|
|
26
|
+
def get_window_root(self, group: str, name: str) -> UPath:
|
|
27
|
+
"""Get the path where the window should be stored."""
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def get_windows(
|
|
32
|
+
self,
|
|
33
|
+
groups: list[str] | None = None,
|
|
34
|
+
names: list[str] | None = None,
|
|
35
|
+
) -> list["Window"]:
|
|
36
|
+
"""Load the windows in the dataset.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
groups: an optional list of groups to filter loading
|
|
40
|
+
names: an optional list of window names to filter loading
|
|
41
|
+
"""
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
44
|
+
@abc.abstractmethod
|
|
45
|
+
def create_or_update_window(self, window: "Window") -> None:
|
|
46
|
+
"""Create or update the window.
|
|
47
|
+
|
|
48
|
+
An existing window is only updated if there is one with the same name and group.
|
|
49
|
+
|
|
50
|
+
If there is a window with the same name but a different group, the behavior is
|
|
51
|
+
undefined.
|
|
52
|
+
"""
|
|
53
|
+
raise NotImplementedError
|
|
54
|
+
|
|
55
|
+
@abc.abstractmethod
|
|
56
|
+
def get_layer_datas(self, group: str, name: str) -> dict[str, "WindowLayerData"]:
|
|
57
|
+
"""Get the window layer datas for the specified window.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
group: the window group.
|
|
61
|
+
name: the window name.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
a dict mapping from the layer name to the layer data for that layer, if one
|
|
65
|
+
was previously saved.
|
|
66
|
+
"""
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
@abc.abstractmethod
|
|
70
|
+
def save_layer_datas(
|
|
71
|
+
self, group: str, name: str, layer_datas: dict[str, "WindowLayerData"]
|
|
72
|
+
) -> None:
|
|
73
|
+
"""Set the window layer datas for the specified window."""
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
@abc.abstractmethod
|
|
77
|
+
def list_completed_layers(self, group: str, name: str) -> list[tuple[str, int]]:
|
|
78
|
+
"""List the layers available for this window that are completed.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
group: the window group.
|
|
82
|
+
name: the window name.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
a list of (layer_name, group_idx) completed layers.
|
|
86
|
+
"""
|
|
87
|
+
raise NotImplementedError
|
|
88
|
+
|
|
89
|
+
@abc.abstractmethod
|
|
90
|
+
def is_layer_completed(
|
|
91
|
+
self, group: str, name: str, layer_name: str, group_idx: int = 0
|
|
92
|
+
) -> bool:
|
|
93
|
+
"""Check whether the specified layer is completed in the given window.
|
|
94
|
+
|
|
95
|
+
Completed means there is data in the layer and the data has been written
|
|
96
|
+
(materialized).
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
group: the window group.
|
|
100
|
+
name: the window name.
|
|
101
|
+
layer_name: the layer name.
|
|
102
|
+
group_idx: the index of the group within the layer.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
whether the layer is completed.
|
|
106
|
+
"""
|
|
107
|
+
raise NotImplementedError
|
|
108
|
+
|
|
109
|
+
@abc.abstractmethod
|
|
110
|
+
def mark_layer_completed(
|
|
111
|
+
self, group: str, name: str, layer_name: str, group_idx: int = 0
|
|
112
|
+
) -> None:
|
|
113
|
+
"""Mark the specified layer completed for the given window.
|
|
114
|
+
|
|
115
|
+
This must be done after the contents of the layer have been written. If a layer
|
|
116
|
+
has multiple groups, the caller should wait until the contents of all groups
|
|
117
|
+
have been written before marking them completed; this is because, when
|
|
118
|
+
materializing a window, we skip materialization if the first group
|
|
119
|
+
(group_idx=0) is marked completed.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
group: the window group.
|
|
123
|
+
name: the window name.
|
|
124
|
+
layer_name: the layer name.
|
|
125
|
+
group_idx: the index of the group within the layer.
|
|
126
|
+
"""
|
|
127
|
+
raise NotImplementedError
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class WindowStorageFactory(abc.ABC):
|
|
131
|
+
"""An abstract class for a configurable storage backend for window metadata.
|
|
132
|
+
|
|
133
|
+
The dataset config includes a StorageConfig that configures a WindowStorageFactory,
|
|
134
|
+
which in turn creates a WindowStorage given a dataset path.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
@abc.abstractmethod
|
|
138
|
+
def get_storage(self, ds_path: UPath) -> WindowStorage:
|
|
139
|
+
"""Get a WindowStorage for the given dataset path."""
|
|
140
|
+
raise NotImplementedError
|