diff --git a/AUTHORS b/AUTHORS index 16272bdf28b69..042cc3ee4a4a0 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..d4e60ab52f002 100644 --- a/django/contrib/gis/gdal/raster/source.py +++ b/django/contrib/gis/gdal/raster/source.py @@ -425,6 +425,21 @@ def warp(self, ds_input, resampling='NearestNeighbour', max_error=0.0): return target + def clone(self): + "Return a clone of this GDALRaster object" + return GDALRaster( + capi.copy_ds( + self.driver._ptr, + force_bytes(self.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 +458,10 @@ def transform(self, srs, driver=None, name=None, resampling='NearestNeighbour', 'Transform only accepts SpatialReference, string, and integer ' 'objects.' ) + + if target_srs.srid == self.srid: + return self.clone() + # 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..4090eca01b5f6 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,61 @@ 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): + source = GDALRaster({ + 'datatype': 1, + 'driver': 'MEM', + 'width': 4, + 'height': 4, + 'srid': 3086, + 'origin': (500000, 400000), + 'scale': (100, -100), + 'skew': (0, 0), + 'bands': [{ + 'data': range(16), + 'nodata_value': 23, + }], + }) + + clone = source.clone() + + # Test cloned raster has same attributes + self.assertEqual(source.name, clone.name) + self.assertEqual(source.srs.srid, clone.srs.srid) + self.assertEqual(source.width, clone.width) + self.assertEqual(source.height, clone.height) + self.assertAlmostEqual(source.origin[0], clone.origin[0], 3) + self.assertAlmostEqual(source.origin[1], clone.origin[1], 3) + self.assertAlmostEqual(source.scale[0], clone.scale[0], 3) + self.assertAlmostEqual(source.scale[1], clone.scale[1], 3) + self.assertEqual(source.skew, clone.skew) + + def test_raster_transform_skips_recalculation(self): + with mock.patch.object(GDALRaster, "clone") as clone_func: + rstfile = tempfile.NamedTemporaryFile(suffix='.tif') + ndv = 99 + + 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': ndv, + }], + }) + + source.transform(4326) + + self.assertEqual(clone_func.call_count, 1) + def test_raster_transform(self): tests = [ 3086,