From 12d6cae7c0401baa70c934f465bad856afecc847 Mon Sep 17 00:00:00 2001 From: Barton Ip Date: Sun, 30 Aug 2020 11:35:11 +0000 Subject: [PATCH] Fixed #31766 -- Made GDALRaster.transform() return a clone for the same SRID and driver. Thanks Daniel Wiesmann for the review. --- AUTHORS | 1 + django/contrib/gis/gdal/raster/source.py | 24 +++++++ tests/gis_tests/gdal_tests/test_raster.py | 83 +++++++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/AUTHORS b/AUTHORS index 386837aee66a0..7e316cd739cec 100644 --- a/AUTHORS +++ b/AUTHORS @@ -110,6 +110,7 @@ answer newbie questions, and generally made Django that much better: Baptiste Mispelon Barry Pederson Bartolome Sanchez Salado + Barton Ip Bartosz Grabski Bashar Al-Abdulhadi Bastian Kleineidam diff --git a/django/contrib/gis/gdal/raster/source.py b/django/contrib/gis/gdal/raster/source.py index 05bbe4937213a..d3129368e237b 100644 --- a/django/contrib/gis/gdal/raster/source.py +++ b/django/contrib/gis/gdal/raster/source.py @@ -425,6 +425,27 @@ def warp(self, ds_input, resampling='NearestNeighbour', max_error=0.0): return target + def clone(self, name=None): + """Return a clone of this GDALRaster.""" + if name: + clone_name = name + elif self.driver.name != 'MEM': + clone_name = self.name + '_copy.' + self.driver.name + else: + clone_name = os.path.join(VSI_FILESYSTEM_BASE_PATH, str(uuid.uuid4())) + return GDALRaster( + capi.copy_ds( + self.driver._ptr, + force_bytes(clone_name), + self._ptr, + c_int(), + c_char_p(), + c_void_p(), + c_void_p(), + ), + write=self._write, + ) + def transform(self, srs, driver=None, name=None, resampling='NearestNeighbour', max_error=0.0): """ @@ -443,6 +464,9 @@ def transform(self, srs, driver=None, name=None, resampling='NearestNeighbour', 'Transform only accepts SpatialReference, string, and integer ' 'objects.' ) + + if target_srs.srid == self.srid and (not driver or driver == self.driver.name): + return self.clone(name) # Create warped virtual dataset in the target reference system target = capi.auto_create_warped_vrt( self._ptr, self.srs.wkt.encode(), target_srs.wkt.encode(), diff --git a/tests/gis_tests/gdal_tests/test_raster.py b/tests/gis_tests/gdal_tests/test_raster.py index 98ac9d6340872..7f690759f63fe 100644 --- a/tests/gis_tests/gdal_tests/test_raster.py +++ b/tests/gis_tests/gdal_tests/test_raster.py @@ -2,6 +2,7 @@ import shutil import struct import tempfile +from unittest import mock from django.contrib.gis.gdal import GDAL_VERSION, GDALRaster, SpatialReference from django.contrib.gis.gdal.error import GDALException @@ -470,6 +471,40 @@ def test_raster_warp_nodata_zone(self): # The result is an empty raster filled with the correct nodata value. self.assertEqual(result, [23] * 16) + def test_raster_clone(self): + rstfile = tempfile.NamedTemporaryFile(suffix='.tif') + tests = [ + ('MEM', '', 23), # In memory raster. + ('tif', rstfile.name, 99), # In file based raster. + ] + for driver, name, nodata_value in tests: + with self.subTest(driver=driver): + source = GDALRaster({ + 'datatype': 1, + 'driver': driver, + 'name': name, + 'width': 4, + 'height': 4, + 'srid': 3086, + 'origin': (500000, 400000), + 'scale': (100, -100), + 'skew': (0, 0), + 'bands': [{ + 'data': range(16), + 'nodata_value': nodata_value, + }], + }) + clone = source.clone() + self.assertNotEqual(clone.name, source.name) + self.assertEqual(clone._write, source._write) + self.assertEqual(clone.srs.srid, source.srs.srid) + self.assertEqual(clone.width, source.width) + self.assertEqual(clone.height, source.height) + self.assertEqual(clone.origin, source.origin) + self.assertEqual(clone.scale, source.scale) + self.assertEqual(clone.skew, source.skew) + self.assertIsNot(clone, source) + def test_raster_transform(self): tests = [ 3086, @@ -531,6 +566,54 @@ def test_raster_transform(self): ], ) + def test_raster_transform_clone(self): + with mock.patch.object(GDALRaster, 'clone') as mocked_clone: + # Create in file based raster. + rstfile = tempfile.NamedTemporaryFile(suffix='.tif') + source = GDALRaster({ + 'datatype': 1, + 'driver': 'tif', + 'name': rstfile.name, + 'width': 5, + 'height': 5, + 'nr_of_bands': 1, + 'srid': 4326, + 'origin': (-5, 5), + 'scale': (2, -2), + 'skew': (0, 0), + 'bands': [{ + 'data': range(25), + 'nodata_value': 99, + }], + }) + # transform() returns a clone because it is the same SRID and + # driver. + source.transform(4326) + self.assertEqual(mocked_clone.call_count, 1) + + def test_raster_transform_clone_name(self): + # Create in file based raster. + rstfile = tempfile.NamedTemporaryFile(suffix='.tif') + source = GDALRaster({ + 'datatype': 1, + 'driver': 'tif', + 'name': rstfile.name, + 'width': 5, + 'height': 5, + 'nr_of_bands': 1, + 'srid': 4326, + 'origin': (-5, 5), + 'scale': (2, -2), + 'skew': (0, 0), + 'bands': [{ + 'data': range(25), + 'nodata_value': 99, + }], + }) + clone_name = rstfile.name + '_respect_name.GTiff' + target = source.transform(4326, name=clone_name) + self.assertEqual(target.name, clone_name) + class GDALBandTests(SimpleTestCase): rs_path = os.path.join(os.path.dirname(__file__), '../data/rasters/raster.tif')