Skip to content

Commit

Permalink
Merge pull request #835 from girder/validate-cog
Browse files Browse the repository at this point in the history
Add validateCOG method to GDALFileTileSource
  • Loading branch information
manthey authored Apr 27, 2022
2 parents bcac438 + cf9658a commit b999c5c
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 6 deletions.
4 changes: 4 additions & 0 deletions large_image/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class TileSourceXYZRangeError(TileSourceError):
pass


class TileSourceInefficientError(TileSourceError):
pass


class TileSourceFileNotFoundError(TileSourceError, FileNotFoundError):
def __init__(self, *args, **kwargs):
return super().__init__(errno.ENOENT, *args, **kwargs)
Expand Down
42 changes: 41 additions & 1 deletion sources/gdal/large_image_source_gdal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
from large_image.constants import (TILE_FORMAT_IMAGE, TILE_FORMAT_NUMPY,
TILE_FORMAT_PIL, SourcePriority,
TileInputUnits, TileOutputMimeTypes)
from large_image.exceptions import TileSourceError, TileSourceFileNotFoundError
from large_image.exceptions import (TileSourceError,
TileSourceFileNotFoundError,
TileSourceInefficientError)
from large_image.tilesource import FileTileSource
from large_image.tilesource.utilities import getPaletteColors

Expand Down Expand Up @@ -1185,6 +1187,44 @@ def getRegion(self, format=(TILE_FORMAT_IMAGE, ), **kwargs):
raise exc
return pathlib.Path(outputPath), TileOutputMimeTypes['TILED']

def validateCOG(self, check_tiled=True, full_check=False, strict=True, warn=True):
"""Check if this image is a valid Cloud Optimized GeoTiff.
This will raise a :class:`large_image.exceptions.TileSourceInefficientError`
if not a valid Cloud Optimized GeoTiff. Otherwise, returns True.
Requires the ``osgeo_utils`` package.
Parameters
----------
check_tiled : bool
Set to False to ignore missing tiling.
full_check : bool
Set to True to check tile/strip leader/trailer bytes.
Might be slow on remote files
strict : bool
Enforce warnings as exceptions. Set to False to only warn and not
raise exceptions.
warn : bool
Log any warnings
"""
from osgeo_utils.samples.validate_cloud_optimized_geotiff import validate

warnings, errors, details = validate(
self._largeImagePath,
check_tiled=check_tiled,
full_check=full_check
)
if errors:
raise TileSourceInefficientError(errors)
if strict and warnings:
raise TileSourceInefficientError(warnings)
if warn:
for warning in warnings:
self.logger.warning(warning)
return True


def open(*args, **kwargs):
"""
Expand Down
5 changes: 5 additions & 0 deletions test/datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@
# Multi source file using different sources
# Source: manually generated.
'multi_source.yml': 'sha512:81d7768b06eca6903082daa5b91706beaac8557ba4cede7f826524303df69a33478d6bb205c56af7ee2b45cd7d75897cc4b5704f743ddbf71bb3537ed3b9e8a8', # noqa
# Geospatial tiff - not cloud optimized
'TC_NG_SFBay_US_Geo.tif': 'sha512:da2e66528f77a5e10af5de9e496074b77277c3da81dafc69790189510e5a7e18dba9e966329d36c979f1b547f0d36a82fbc4cfccc65ae9ef9e2747b5a9ee77b0', # noqa
# Geospatial tiff - cloud optimized
'TC_NG_SFBay_US_Geo_COG.tif': 'sha512:5e56cdb8fb1a02615698a153862c10d5292b1ad42836a6e8bce5627e93a387dc0d3c9b6cfbd539796500bc2d3e23eafd07550f8c214e9348880bbbc6b3b0ea0c', # noqa

}


Expand Down
8 changes: 4 additions & 4 deletions test/test_source_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@
'openjpeg': {'read': r'\.(jp2)$'},
'openslide': {
'read': r'\.(ptif|svs|tif.*)$',
'noread': r'(oahu|DDX58_AXL|huron\.image2_jpeg2k|landcover_sample|d042-353\.crop)',
'noread': r'(oahu|DDX58_AXL|huron\.image2_jpeg2k|landcover_sample|d042-353\.crop|US_Geo\.)',
'skipTiles': r'one_layer_missing'},
'pil': {
'read': r'\.(jpeg|png|tif.*)$',
'noread': r'(G10-3|JK-kidney|d042-353|huron|one_layer_missing)'},
'noread': r'(G10-3|JK-kidney|d042-353|huron|one_layer_missing|US_Geo)'},
'test': {'any': True, 'skipTiles': r''},
'tiff': {
'read': r'\.(ptif|scn|svs|tif.*)$',
'noread': r'(oahu|DDX58_AXL|G10-3_pelvis_crop|'
r'd042-353\.crop\.small\.float|landcover_sample)',
r'd042-353\.crop\.small\.float|landcover_sample|US_Geo\.)',
'skipTiles': r'(sample_image\.ptif|one_layer_missing_tiles)'},
'vips': {
'read': r'',
Expand All @@ -66,7 +66,7 @@
# Python 3.6 has an older version of PIL that won't read some of the
# ome.tif files.
SourceAndFiles['pil']['noread'] = \
r'(G10-3|JK-kidney|d042-353|huron|sample.*ome|one_layer_missing)'
r'(G10-3|JK-kidney|d042-353|huron|sample.*ome|one_layer_missing|US_Geo)'


def testNearPowerOfTwo():
Expand Down
14 changes: 13 additions & 1 deletion test/test_source_gdal.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import pytest

from large_image import constants
from large_image.exceptions import TileSourceError
from large_image.exceptions import TileSourceError, TileSourceInefficientError

from . import utilities
from .datastore import datastore
Expand Down Expand Up @@ -550,3 +550,15 @@ def testHttpVfsPath():
assert tileMetadata['bounds']['ymin'] == pytest.approx(4876273, 1)
assert tileMetadata['bounds']['srs'] == 'epsg:3857'
assert tileMetadata['geospatial']


def testVfsCogValidation():
imagePath = datastore.get_url('TC_NG_SFBay_US_Geo_COG.tif')
source = large_image_source_gdal.open(
imagePath, projection='EPSG:3857', encoding='PNG')
assert source.validateCOG()
imagePath = datastore.get_url('TC_NG_SFBay_US_Geo.tif')
source = large_image_source_gdal.open(
imagePath, projection='EPSG:3857', encoding='PNG')
with pytest.raises(TileSourceInefficientError):
source.validateCOG()

0 comments on commit b999c5c

Please sign in to comment.