diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d31e967d..95ff3e43c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add the image converter to the extra requires ([#677](../../pull/677)) - All file tile sources can take either strings or pathlib.Path values ([#683](../../pull/683)) - On Girder with read-only assetstores, return results even if caching fails ([#684](../../pull/684)) +- Handle geospatial files with no explicit projection ([#686](../../pull/686)) ## Version 1.8.6 diff --git a/large_image/tilesource/__init__.py b/large_image/tilesource/__init__.py index 310703679..6a3a43549 100644 --- a/large_image/tilesource/__init__.py +++ b/large_image/tilesource/__init__.py @@ -35,6 +35,8 @@ def isGeospatial(path): return True if ds.GetProjection(): return True + if ds.GetGeoTransform(can_return_null=True): + return True if ds.GetDriver().ShortName in {'NITF', 'netCDF'}: return True return False diff --git a/sources/gdal/large_image_source_gdal/__init__.py b/sources/gdal/large_image_source_gdal/__init__.py index 514396391..db1ce299d 100644 --- a/sources/gdal/large_image_source_gdal/__init__.py +++ b/sources/gdal/large_image_source_gdal/__init__.py @@ -372,7 +372,8 @@ def getProj4String(self): else: wkt = self.dataset.GetProjection() if not wkt: - if hasattr(self, '_netcdf') or self._getDriver() in {'NITF'}: + if (self.dataset.GetGeoTransform(can_return_null=True) or + hasattr(self, '_netcdf') or self._getDriver() in {'NITF'}): return NeededInitPrefix + 'epsg:4326' return proj = osr.SpatialReference() @@ -569,6 +570,7 @@ def getMetadata(self): 'geospatial': bool( self.dataset.GetProjection() or (self.dataset.GetGCPProjection() and self.dataset.GetGCPs()) or + self.dataset.GetGeoTransform(can_return_null=True) or hasattr(self, '_netcdf')), 'levels': self.levels, 'sizeX': self.sizeX, diff --git a/test/datastore.py b/test/datastore.py index c49e189cb..0eb10049e 100644 --- a/test/datastore.py +++ b/test/datastore.py @@ -69,6 +69,9 @@ # One layer tiff with missing tiles # Source: one_layer_missing_tiles.tiff 'one_layer_missing_tiles.tiff': 'sha512:53d52abbb82d312b114407d2974fa0a4116c3b50875c5b360798b5d529cbb67596b1dff7d8feceb319d0b4377d12d023b64e374ea3df37f795c5a940afaa4cd5', # noqa + # Geospatial file without a projection + # Source: generated from geojs oahu sample and a script + 'oahu-dense.tiff': 'sha512:414b7807f14991d6f8229134ad6ccbc2cc2d4b05423ccebfd3ede7d7323dfcf04ef1a7b2c2b4c45c31f8a36bccd390782af3e7d3e99f01f1b95650c5da1f122b', # noqa } diff --git a/test/test_source_base.py b/test/test_source_base.py index 2b8fa8e58..d1693ccc0 100644 --- a/test/test_source_base.py +++ b/test/test_source_base.py @@ -35,7 +35,7 @@ 'openjpeg': {'read': r'\.(jp2)$'}, 'openslide': { 'read': r'\.(ptif|svs|tif.*)$', - 'noread': r'(DDX58_AXL|huron\.image2_jpeg2k|landcover_sample|d042-353\.crop)', + 'noread': r'(oahu|DDX58_AXL|huron\.image2_jpeg2k|landcover_sample|d042-353\.crop)', 'skipTiles': r'one_layer_missing'}, 'pil': { 'read': r'\.(jpeg|png|tif.*)$', @@ -43,7 +43,8 @@ 'test': {'any': True, 'skipTiles': r''}, 'tiff': { 'read': r'\.(ptif|scn|svs|tif.*)$', - 'noread': r'(DDX58_AXL|G10-3_pelvis_crop|d042-353\.crop\.small\.float|landcover_sample)', + 'noread': r'(oahu|DDX58_AXL|G10-3_pelvis_crop|' + r'd042-353\.crop\.small\.float|landcover_sample)', 'skipTiles': r'(sample_image\.ptif|one_layer_missing_tiles)'}, } diff --git a/test/test_source_gdal.py b/test/test_source_gdal.py index bcdb4e532..ec298d5d5 100644 --- a/test/test_source_gdal.py +++ b/test/test_source_gdal.py @@ -496,3 +496,14 @@ def testGetTiledRegion16BitWithStyle(): assert tileMetadata['bounds']['ymin'] == pytest.approx(3899358, 1) assert '+proj=merc' in tileMetadata['bounds']['srs'] region.unlink() + + +def testFileWithoutProjection(): + imagePath = datastore.fetch('oahu-dense.tiff') + ts = large_image_source_gdal.open(imagePath, projection='EPSG:3857') + tileMetadata = ts.getMetadata() + assert tileMetadata['bounds']['xmax'] == pytest.approx(-17548722, 1) + assert tileMetadata['bounds']['xmin'] == pytest.approx(-17620245, 1) + assert tileMetadata['bounds']['ymax'] == pytest.approx(2477890, 1) + assert tileMetadata['bounds']['ymin'] == pytest.approx(2420966, 1) + assert 'epsg:3857' in tileMetadata['bounds']['srs']