diff --git a/CHANGELOG.md b/CHANGELOG.md index 285360026..d1e65a589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ ## 1.27.5 +### Features +- Zarr tile sink ([#1446](../../pull/1446)) + ### Improvements +- Prioritize tile sinks ([#1478](../../pull/1478)) - Add a dependency to the zarr source to read more compression types ([#1480](../../pull/1480)) ### Bug Fixes diff --git a/large_image/tilesource/__init__.py b/large_image/tilesource/__init__.py index 503cee457..ec7f52d18 100644 --- a/large_image/tilesource/__init__.py +++ b/large_image/tilesource/__init__.py @@ -3,7 +3,7 @@ import uuid from importlib.metadata import entry_points from pathlib import PosixPath -from typing import Dict, List, Optional, Tuple, Type, Union +from typing import Dict, List, Optional, Tuple, Type, Union, cast from .. import config from ..constants import NEW_IMAGE_PATH_FLAG, SourcePriority @@ -86,11 +86,14 @@ def getSortedSourceList( properties = { '_geospatial_source': isGeospatial(pathOrUri), } + isNew = str(pathOrUri).startswith(NEW_IMAGE_PATH_FLAG) sourceList = [] for sourceName in availableSources: sourceExtensions = availableSources[sourceName].extensions priority = sourceExtensions.get(None, SourcePriority.MANUAL) fallback = True + if isNew and getattr(availableSources[sourceName], 'newPriority', None) is not None: + priority = min(priority, cast(SourcePriority, availableSources[sourceName].newPriority)) if (mimeType and getattr(availableSources[sourceName], 'mimeTypes', None) and mimeType in availableSources[sourceName].mimeTypes): fallback = False @@ -130,7 +133,8 @@ def getSourceNameFromDict( there is no such source. """ sourceList = getSortedSourceList(availableSources, pathOrUri, mimeType, *args, **kwargs) - for _clash, _fallback, _priority, sourceName in sorted(sourceList): + for entry in sorted(sourceList): + sourceName = entry[-1] if availableSources[sourceName].canRead(pathOrUri, *args, **kwargs): return sourceName return None @@ -217,7 +221,8 @@ def canReadList( sourceList = getSortedSourceList( AvailableTileSources, pathOrUri, mimeType, *args, **kwargs) result = [] - for _clash, _fallback, _priority, sourceName in sorted(sourceList): + for entry in sorted(sourceList): + sourceName = entry[-1] result.append((sourceName, AvailableTileSources[sourceName].canRead( pathOrUri, *args, **kwargs))) return result diff --git a/large_image/tilesource/base.py b/large_image/tilesource/base.py index 7c9b898f6..af92cc0ce 100644 --- a/large_image/tilesource/base.py +++ b/large_image/tilesource/base.py @@ -57,6 +57,10 @@ class TileSource(IPyLeafletMixin): nameMatches: Dict[str, SourcePriority] = { } + # If a source supports creating new tiled images, specify its basic + # priority based on expected feature set + newPriority: Optional[SourcePriority] = None + # When getting tiles for otherwise empty levels (missing powers of two), we # composite the tile from higher resolution levels. This can use excessive # memory if there are too many missing levels. For instance, if there are diff --git a/sources/vips/large_image_source_vips/__init__.py b/sources/vips/large_image_source_vips/__init__.py index 65ecbea2a..e747c7133 100644 --- a/sources/vips/large_image_source_vips/__init__.py +++ b/sources/vips/large_image_source_vips/__init__.py @@ -50,6 +50,7 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass): mimeTypes = { None: SourcePriority.FALLBACK, } + newPriority = SourcePriority.MEDIUM _tileSize = 256 diff --git a/sources/zarr/large_image_source_zarr/__init__.py b/sources/zarr/large_image_source_zarr/__init__.py index fe85441ec..d6eeea485 100644 --- a/sources/zarr/large_image_source_zarr/__init__.py +++ b/sources/zarr/large_image_source_zarr/__init__.py @@ -41,6 +41,7 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass): 'zarray': SourcePriority.PREFERRED, 'db': SourcePriority.MEDIUM, } + newPriority = SourcePriority.HIGH _tileSize = 512 _minTileSize = 128 diff --git a/test/test_source_vips.py b/test/test_source_vips.py index 17e6cf6a1..ee0c18ef5 100644 --- a/test/test_source_vips.py +++ b/test/test_source_vips.py @@ -151,8 +151,7 @@ def testNewAndWriteMinSize(tmp_path): def testNewAndWriteJPEG(tmp_path): imagePath = datastore.fetch('sample_image.ptif') source = large_image.open(imagePath) - # Update this if it doesn't direct to vips source - out = large_image.new() + out = large_image_source_vips.new() for tile in source.tileIterator( format=large_image.constants.TILE_FORMAT_NUMPY, region=dict(right=4000, bottom=2000),