From f8c03233a54610d529ce77ff50cc06cde4ccc10c Mon Sep 17 00:00:00 2001 From: David Manthey Date: Mon, 25 Jul 2022 11:57:54 -0400 Subject: [PATCH 1/2] Copy tilesource instances to add styles Instead of reopening a tilesource when adding a style, copy the unstyled class and add the style information. --- large_image/cache_util/cache.py | 12 ++++++++++++ large_image/tilesource/base.py | 10 +++++++++- sources/gdal/large_image_source_gdal/__init__.py | 10 ++++++++++ sources/mapnik/large_image_source_mapnik/__init__.py | 4 +++- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/large_image/cache_util/cache.py b/large_image/cache_util/cache.py index 8ae303a2c..706b452a9 100644 --- a/large_image/cache_util/cache.py +++ b/large_image/cache_util/cache.py @@ -1,3 +1,4 @@ +import copy import functools import threading @@ -193,6 +194,17 @@ def __call__(cls, *args, **kwargs): # noqa - N805 return result except KeyError: pass + # This conditionally copies a non-styled class and add a style. + if kwargs.get('style') and hasattr(cls, '_setStyle'): + subkwargs = kwargs.copy() + subkwargs.pop('style') + subresult = cls(*args, **subkwargs) + result = copy.copy(subresult) + result._setStyle(kwargs['style']) + result._classkey = key + with cacheLock: + cache[key] = result + return result try: instance = super().__call__(*args, **kwargs) except Exception as exc: diff --git a/large_image/tilesource/base.py b/large_image/tilesource/base.py index a55c89a80..2cd665f98 100644 --- a/large_image/tilesource/base.py +++ b/large_image/tilesource/base.py @@ -129,7 +129,6 @@ def __init__(self, encoding='JPEG', jpegQuality=95, jpegSubsampling=0, self.sizeX = None self.sizeY = None self._styleLock = threading.RLock() - self._bandRanges = {} if encoding not in TileOutputMimeTypes: raise ValueError('Invalid encoding "%s"' % encoding) @@ -139,6 +138,15 @@ def __init__(self, encoding='JPEG', jpegQuality=95, jpegSubsampling=0, self.jpegSubsampling = int(jpegSubsampling) self.tiffCompression = tiffCompression self.edge = edge + self._setStyle(style) + + def _setStyle(self, style): + """ + Check and set the specified style from a json string or a dictionary. + + :param style: The new style. + """ + self._bandRanges = {} self._jsonstyle = style if style: if isinstance(style, dict): diff --git a/sources/gdal/large_image_source_gdal/__init__.py b/sources/gdal/large_image_source_gdal/__init__.py index 4ddf51e0b..b59f5e049 100644 --- a/sources/gdal/large_image_source_gdal/__init__.py +++ b/sources/gdal/large_image_source_gdal/__init__.py @@ -183,6 +183,16 @@ def __init__(self, path, projection=None, unitsPerPixel=None, **kwargs): self._getTileLock = threading.Lock() self._setDefaultStyle() + def _setStyle(self, style): + """ + Check and set the specified style from a json string or a dictionary. + + :param style: The new style. + """ + super()._setStyle(style) + if hasattr(self, '_getTileLock'): + self._setDefaultStyle() + def _getLargeImagePath(self): """Get GDAL-compatible image path. diff --git a/sources/mapnik/large_image_source_mapnik/__init__.py b/sources/mapnik/large_image_source_mapnik/__init__.py index 5852f803b..00e58ecc8 100644 --- a/sources/mapnik/large_image_source_mapnik/__init__.py +++ b/sources/mapnik/large_image_source_mapnik/__init__.py @@ -176,7 +176,9 @@ def _checkNetCDF(self): def _setDefaultStyle(self): """Don't inherit from GDAL tilesource.""" - pass + with self._getTileLock: + if hasattr(self, '_mapnikMap'): + del self._mapnikMap @staticmethod def interpolateMinMax(start, stop, count): From 7c609188df1ef457440543beb9dc4dbf286abd87 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Thu, 4 Aug 2022 16:08:05 -0400 Subject: [PATCH 2/2] Add some source cache tests. --- test/test_cache_source.py | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 test/test_cache_source.py diff --git a/test/test_cache_source.py b/test/test_cache_source.py new file mode 100644 index 000000000..e268709f5 --- /dev/null +++ b/test/test_cache_source.py @@ -0,0 +1,44 @@ +import pytest + +import large_image +from large_image.cache_util import cachesClear + +from .datastore import datastore + + +@pytest.mark.singular +def testCacheSourceStyle(): + cachesClear() + imagePath = datastore.fetch('sample_image.ptif') + ts1 = large_image.open(imagePath) + ts2 = large_image.open(imagePath, style={'max': 128}) + ts3 = large_image.open(imagePath, style={'max': 160}) + tile1 = ts1.getTile(0, 0, 4) + assert ts1.getTile(0, 0, 4) is not None + assert ts2.getTile(0, 0, 4) is not None + assert ts3.getTile(0, 0, 4) is not None + cachesClear() + assert ts1.getTile(0, 0, 4) == tile1 + del ts1 + assert ts2.getTile(1, 0, 4) is not None + cachesClear() + assert ts2.getTile(2, 0, 4) is not None + ts1 = large_image.open(imagePath) + assert ts1.getTile(0, 0, 4) == tile1 + + +@pytest.mark.singular +def testCacheSourceStyleFirst(): + cachesClear() + imagePath = datastore.fetch('sample_image.ptif') + ts2 = large_image.open(imagePath, style={'max': 128}) + ts1 = large_image.open(imagePath) + tile1 = ts1.getTile(0, 0, 4) + assert ts1.getTile(0, 0, 4) is not None + assert ts2.getTile(0, 0, 4) is not None + del ts1 + assert ts2.getTile(1, 0, 4) is not None + cachesClear() + assert ts2.getTile(2, 0, 4) is not None + ts1 = large_image.open(imagePath) + assert ts1.getTile(0, 0, 4) == tile1