1
0
mirror of https://github.com/django/django.git synced 2024-12-23 01:25:58 +00:00
django/tests/gis_tests/gdal_tests/test_raster.py

993 lines
34 KiB
Python

import os
import shutil
import struct
import tempfile
import zipfile
from pathlib import Path
from unittest import mock
from django.contrib.gis.gdal import GDAL_VERSION, GDALRaster, SpatialReference
from django.contrib.gis.gdal.error import GDALException
from django.contrib.gis.gdal.raster.band import GDALBand
from django.contrib.gis.shortcuts import numpy
from django.core.files.temp import NamedTemporaryFile
from django.test import SimpleTestCase
from ..data.rasters.textrasters import JSON_RASTER
class GDALRasterTests(SimpleTestCase):
"""
Test a GDALRaster instance created from a file (GeoTiff).
"""
def setUp(self):
self.rs_path = os.path.join(
os.path.dirname(__file__), "../data/rasters/raster.tif"
)
self.rs = GDALRaster(self.rs_path)
def test_gdalraster_input_as_path(self):
rs_path = Path(__file__).parent.parent / "data" / "rasters" / "raster.tif"
rs = GDALRaster(rs_path)
self.assertEqual(str(rs_path), rs.name)
def test_rs_name_repr(self):
self.assertEqual(self.rs_path, self.rs.name)
self.assertRegex(repr(self.rs), r"<Raster object at 0x\w+>")
def test_rs_driver(self):
self.assertEqual(self.rs.driver.name, "GTiff")
def test_rs_size(self):
self.assertEqual(self.rs.width, 163)
self.assertEqual(self.rs.height, 174)
def test_rs_srs(self):
self.assertEqual(self.rs.srs.srid, 3086)
self.assertEqual(self.rs.srs.units, (1.0, "metre"))
def test_rs_srid(self):
rast = GDALRaster(
{
"width": 16,
"height": 16,
"srid": 4326,
}
)
self.assertEqual(rast.srid, 4326)
rast.srid = 3086
self.assertEqual(rast.srid, 3086)
def test_geotransform_and_friends(self):
# Assert correct values for file based raster
self.assertEqual(
self.rs.geotransform,
[511700.4680706557, 100.0, 0.0, 435103.3771231986, 0.0, -100.0],
)
self.assertEqual(self.rs.origin, [511700.4680706557, 435103.3771231986])
self.assertEqual(self.rs.origin.x, 511700.4680706557)
self.assertEqual(self.rs.origin.y, 435103.3771231986)
self.assertEqual(self.rs.scale, [100.0, -100.0])
self.assertEqual(self.rs.scale.x, 100.0)
self.assertEqual(self.rs.scale.y, -100.0)
self.assertEqual(self.rs.skew, [0, 0])
self.assertEqual(self.rs.skew.x, 0)
self.assertEqual(self.rs.skew.y, 0)
# Create in-memory rasters and change gtvalues
rsmem = GDALRaster(JSON_RASTER)
# geotransform accepts both floats and ints
rsmem.geotransform = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
self.assertEqual(rsmem.geotransform, [0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
rsmem.geotransform = range(6)
self.assertEqual(rsmem.geotransform, [float(x) for x in range(6)])
self.assertEqual(rsmem.origin, [0, 3])
self.assertEqual(rsmem.origin.x, 0)
self.assertEqual(rsmem.origin.y, 3)
self.assertEqual(rsmem.scale, [1, 5])
self.assertEqual(rsmem.scale.x, 1)
self.assertEqual(rsmem.scale.y, 5)
self.assertEqual(rsmem.skew, [2, 4])
self.assertEqual(rsmem.skew.x, 2)
self.assertEqual(rsmem.skew.y, 4)
self.assertEqual(rsmem.width, 5)
self.assertEqual(rsmem.height, 5)
def test_geotransform_bad_inputs(self):
rsmem = GDALRaster(JSON_RASTER)
error_geotransforms = [
[1, 2],
[1, 2, 3, 4, 5, "foo"],
[1, 2, 3, 4, 5, 6, "foo"],
]
msg = "Geotransform must consist of 6 numeric values."
for geotransform in error_geotransforms:
with (
self.subTest(i=geotransform),
self.assertRaisesMessage(ValueError, msg),
):
rsmem.geotransform = geotransform
def test_rs_extent(self):
self.assertEqual(
self.rs.extent,
(
511700.4680706557,
417703.3771231986,
528000.4680706557,
435103.3771231986,
),
)
def test_rs_bands(self):
self.assertEqual(len(self.rs.bands), 1)
self.assertIsInstance(self.rs.bands[0], GDALBand)
def test_memory_based_raster_creation(self):
# Create uint8 raster with full pixel data range (0-255)
rast = GDALRaster(
{
"datatype": 1,
"width": 16,
"height": 16,
"srid": 4326,
"bands": [
{
"data": range(256),
"nodata_value": 255,
}
],
}
)
# Get array from raster
result = rast.bands[0].data()
if numpy:
result = result.flatten().tolist()
# Assert data is same as original input
self.assertEqual(result, list(range(256)))
def test_file_based_raster_creation(self):
# Prepare tempfile
rstfile = NamedTemporaryFile(suffix=".tif")
# Create file-based raster from scratch
GDALRaster(
{
"datatype": self.rs.bands[0].datatype(),
"driver": "tif",
"name": rstfile.name,
"width": 163,
"height": 174,
"nr_of_bands": 1,
"srid": self.rs.srs.wkt,
"origin": (self.rs.origin.x, self.rs.origin.y),
"scale": (self.rs.scale.x, self.rs.scale.y),
"skew": (self.rs.skew.x, self.rs.skew.y),
"bands": [
{
"data": self.rs.bands[0].data(),
"nodata_value": self.rs.bands[0].nodata_value,
}
],
}
)
# Reload newly created raster from file
restored_raster = GDALRaster(rstfile.name)
# Presence of TOWGS84 depend on GDAL/Proj versions.
self.assertEqual(
restored_raster.srs.wkt.replace("TOWGS84[0,0,0,0,0,0,0],", ""),
self.rs.srs.wkt.replace("TOWGS84[0,0,0,0,0,0,0],", ""),
)
self.assertEqual(restored_raster.geotransform, self.rs.geotransform)
if numpy:
numpy.testing.assert_equal(
restored_raster.bands[0].data(), self.rs.bands[0].data()
)
else:
self.assertEqual(restored_raster.bands[0].data(), self.rs.bands[0].data())
def test_nonexistent_file(self):
msg = 'Unable to read raster source input "nonexistent.tif".'
with self.assertRaisesMessage(GDALException, msg):
GDALRaster("nonexistent.tif")
def test_vsi_raster_creation(self):
# Open a raster as a file object.
with open(self.rs_path, "rb") as dat:
# Instantiate a raster from the file binary buffer.
vsimem = GDALRaster(dat.read())
# The data of the in-memory file is equal to the source file.
result = vsimem.bands[0].data()
target = self.rs.bands[0].data()
if numpy:
result = result.flatten().tolist()
target = target.flatten().tolist()
self.assertEqual(result, target)
def test_vsi_raster_deletion(self):
path = "/vsimem/raster.tif"
# Create a vsi-based raster from scratch.
vsimem = GDALRaster(
{
"name": path,
"driver": "tif",
"width": 4,
"height": 4,
"srid": 4326,
"bands": [
{
"data": range(16),
}
],
}
)
# The virtual file exists.
rst = GDALRaster(path)
self.assertEqual(rst.width, 4)
# Delete GDALRaster.
del vsimem
del rst
# The virtual file has been removed.
msg = 'Could not open the datasource at "/vsimem/raster.tif"'
with self.assertRaisesMessage(GDALException, msg):
GDALRaster(path)
def test_vsi_invalid_buffer_error(self):
msg = "Failed creating VSI raster from the input buffer."
with self.assertRaisesMessage(GDALException, msg):
GDALRaster(b"not-a-raster-buffer")
def test_vsi_buffer_property(self):
# Create a vsi-based raster from scratch.
rast = GDALRaster(
{
"name": "/vsimem/raster.tif",
"driver": "tif",
"width": 4,
"height": 4,
"srid": 4326,
"bands": [
{
"data": range(16),
}
],
}
)
# Do a round trip from raster to buffer to raster.
result = GDALRaster(rast.vsi_buffer).bands[0].data()
if numpy:
result = result.flatten().tolist()
# Band data is equal to nodata value except on input block of ones.
self.assertEqual(result, list(range(16)))
# The vsi buffer is None for rasters that are not vsi based.
self.assertIsNone(self.rs.vsi_buffer)
def test_vsi_vsizip_filesystem(self):
rst_zipfile = NamedTemporaryFile(suffix=".zip")
with zipfile.ZipFile(rst_zipfile, mode="w") as zf:
zf.write(self.rs_path, "raster.tif")
rst_path = "/vsizip/" + os.path.join(rst_zipfile.name, "raster.tif")
rst = GDALRaster(rst_path)
self.assertEqual(rst.driver.name, self.rs.driver.name)
self.assertEqual(rst.name, rst_path)
self.assertIs(rst.is_vsi_based, True)
self.assertIsNone(rst.vsi_buffer)
def test_offset_size_and_shape_on_raster_creation(self):
rast = GDALRaster(
{
"datatype": 1,
"width": 4,
"height": 4,
"srid": 4326,
"bands": [
{
"data": (1,),
"offset": (1, 1),
"size": (2, 2),
"shape": (1, 1),
"nodata_value": 2,
}
],
}
)
# Get array from raster.
result = rast.bands[0].data()
if numpy:
result = result.flatten().tolist()
# Band data is equal to nodata value except on input block of ones.
self.assertEqual(result, [2, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 2])
def test_set_nodata_value_on_raster_creation(self):
# Create raster filled with nodata values.
rast = GDALRaster(
{
"datatype": 1,
"width": 2,
"height": 2,
"srid": 4326,
"bands": [{"nodata_value": 23}],
}
)
# Get array from raster.
result = rast.bands[0].data()
if numpy:
result = result.flatten().tolist()
# All band data is equal to nodata value.
self.assertEqual(result, [23] * 4)
def test_set_nodata_none_on_raster_creation(self):
# Create raster without data and without nodata value.
rast = GDALRaster(
{
"datatype": 1,
"width": 2,
"height": 2,
"srid": 4326,
"bands": [{"nodata_value": None}],
}
)
# Get array from raster.
result = rast.bands[0].data()
if numpy:
result = result.flatten().tolist()
# Band data is equal to zero because no nodata value has been specified.
self.assertEqual(result, [0] * 4)
def test_raster_metadata_property(self):
data = self.rs.metadata
self.assertEqual(data["DEFAULT"], {"AREA_OR_POINT": "Area"})
self.assertEqual(data["IMAGE_STRUCTURE"], {"INTERLEAVE": "BAND"})
# Create file-based raster from scratch
source = GDALRaster(
{
"datatype": 1,
"width": 2,
"height": 2,
"srid": 4326,
"bands": [{"data": range(4), "nodata_value": 99}],
}
)
# Set metadata on raster and on a band.
metadata = {
"DEFAULT": {"OWNER": "Django", "VERSION": "1.0", "AREA_OR_POINT": "Point"},
}
source.metadata = metadata
source.bands[0].metadata = metadata
self.assertEqual(source.metadata["DEFAULT"], metadata["DEFAULT"])
self.assertEqual(source.bands[0].metadata["DEFAULT"], metadata["DEFAULT"])
# Update metadata on raster.
metadata = {
"DEFAULT": {"VERSION": "2.0"},
}
source.metadata = metadata
self.assertEqual(source.metadata["DEFAULT"]["VERSION"], "2.0")
# Remove metadata on raster.
metadata = {
"DEFAULT": {"OWNER": None},
}
source.metadata = metadata
self.assertNotIn("OWNER", source.metadata["DEFAULT"])
def test_raster_info_accessor(self):
infos = self.rs.info
# Data
info_lines = [line.strip() for line in infos.split("\n") if line.strip() != ""]
for line in [
"Driver: GTiff/GeoTIFF",
"Files: {}".format(self.rs_path),
"Size is 163, 174",
"Origin = (511700.468070655711927,435103.377123198588379)",
"Pixel Size = (100.000000000000000,-100.000000000000000)",
"Metadata:",
"AREA_OR_POINT=Area",
"Image Structure Metadata:",
"INTERLEAVE=BAND",
"Band 1 Block=163x50 Type=Byte, ColorInterp=Gray",
"NoData Value=15",
]:
self.assertIn(line, info_lines)
for line in [
r"Upper Left \( 511700.468, 435103.377\) "
r'\( 82d51\'46.1\d"W, 27d55\' 1.5\d"N\)',
r"Lower Left \( 511700.468, 417703.377\) "
r'\( 82d51\'52.0\d"W, 27d45\'37.5\d"N\)',
r"Upper Right \( 528000.468, 435103.377\) "
r'\( 82d41\'48.8\d"W, 27d54\'56.3\d"N\)',
r"Lower Right \( 528000.468, 417703.377\) "
r'\( 82d41\'55.5\d"W, 27d45\'32.2\d"N\)',
r"Center \( 519850.468, 426403.377\) "
r'\( 82d46\'50.6\d"W, 27d50\'16.9\d"N\)',
]:
self.assertRegex(infos, line)
# CRS (skip the name because string depends on the GDAL/Proj versions).
self.assertIn("NAD83 / Florida GDL Albers", infos)
def test_compressed_file_based_raster_creation(self):
rstfile = NamedTemporaryFile(suffix=".tif")
# Make a compressed copy of an existing raster.
compressed = self.rs.warp(
{"papsz_options": {"compress": "packbits"}, "name": rstfile.name}
)
# Check physically if compression worked.
self.assertLess(os.path.getsize(compressed.name), os.path.getsize(self.rs.name))
# Create file-based raster with options from scratch.
papsz_options = {
"compress": "packbits",
"blockxsize": 23,
"blockysize": 23,
}
if GDAL_VERSION < (3, 7):
datatype = 1
papsz_options["pixeltype"] = "signedbyte"
else:
datatype = 14
compressed = GDALRaster(
{
"datatype": datatype,
"driver": "tif",
"name": rstfile.name,
"width": 40,
"height": 40,
"srid": 3086,
"origin": (500000, 400000),
"scale": (100, -100),
"skew": (0, 0),
"bands": [
{
"data": range(40 ^ 2),
"nodata_value": 255,
}
],
"papsz_options": papsz_options,
}
)
# Check if options used on creation are stored in metadata.
# Reopening the raster ensures that all metadata has been written
# to the file.
compressed = GDALRaster(compressed.name)
self.assertEqual(
compressed.metadata["IMAGE_STRUCTURE"]["COMPRESSION"],
"PACKBITS",
)
self.assertEqual(compressed.bands[0].datatype(), datatype)
if GDAL_VERSION < (3, 7):
self.assertEqual(
compressed.bands[0].metadata["IMAGE_STRUCTURE"]["PIXELTYPE"],
"SIGNEDBYTE",
)
self.assertIn("Block=40x23", compressed.info)
def test_raster_warp(self):
# Create in memory raster
source = GDALRaster(
{
"datatype": 1,
"driver": "MEM",
"name": "sourceraster",
"width": 4,
"height": 4,
"nr_of_bands": 1,
"srid": 3086,
"origin": (500000, 400000),
"scale": (100, -100),
"skew": (0, 0),
"bands": [
{
"data": range(16),
"nodata_value": 255,
}
],
}
)
# Test altering the scale, width, and height of a raster
data = {
"scale": [200, -200],
"width": 2,
"height": 2,
}
target = source.warp(data)
self.assertEqual(target.width, data["width"])
self.assertEqual(target.height, data["height"])
self.assertEqual(target.scale, data["scale"])
self.assertEqual(target.bands[0].datatype(), source.bands[0].datatype())
self.assertEqual(target.name, "sourceraster_copy.MEM")
result = target.bands[0].data()
if numpy:
result = result.flatten().tolist()
self.assertEqual(result, [5, 7, 13, 15])
# Test altering the name and datatype (to float)
data = {
"name": "/path/to/targetraster.tif",
"datatype": 6,
}
target = source.warp(data)
self.assertEqual(target.bands[0].datatype(), 6)
self.assertEqual(target.name, "/path/to/targetraster.tif")
self.assertEqual(target.driver.name, "MEM")
result = target.bands[0].data()
if numpy:
result = result.flatten().tolist()
self.assertEqual(
result,
[
0.0,
1.0,
2.0,
3.0,
4.0,
5.0,
6.0,
7.0,
8.0,
9.0,
10.0,
11.0,
12.0,
13.0,
14.0,
15.0,
],
)
def test_raster_warp_nodata_zone(self):
# Create in memory raster.
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,
}
],
}
)
# Warp raster onto a location that does not cover any pixels of the original.
result = source.warp({"origin": (200000, 200000)}).bands[0].data()
if numpy:
result = result.flatten().tolist()
# The result is an empty raster filled with the correct nodata value.
self.assertEqual(result, [23] * 16)
def test_raster_clone(self):
rstfile = 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,
"3086",
SpatialReference(3086),
]
for srs in tests:
with self.subTest(srs=srs):
# Prepare tempfile and nodata value.
rstfile = NamedTemporaryFile(suffix=".tif")
ndv = 99
# Create in file based raster.
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,
}
],
}
)
target = source.transform(srs)
# Reload data from disk.
target = GDALRaster(target.name)
self.assertEqual(target.srs.srid, 3086)
self.assertEqual(target.width, 7)
self.assertEqual(target.height, 7)
self.assertEqual(target.bands[0].datatype(), source.bands[0].datatype())
self.assertAlmostEqual(target.origin[0], 9124842.791079799, 3)
self.assertAlmostEqual(target.origin[1], 1589911.6476407414, 3)
self.assertAlmostEqual(target.scale[0], 223824.82664250192, 3)
self.assertAlmostEqual(target.scale[1], -223824.82664250192, 3)
self.assertEqual(target.skew, [0, 0])
result = target.bands[0].data()
if numpy:
result = result.flatten().tolist()
# The reprojection of a raster that spans over a large area
# skews the data matrix and might introduce nodata values.
self.assertEqual(
result,
[
ndv,
ndv,
ndv,
ndv,
4,
ndv,
ndv,
ndv,
ndv,
2,
3,
9,
ndv,
ndv,
ndv,
1,
2,
8,
13,
19,
ndv,
0,
6,
6,
12,
18,
18,
24,
ndv,
10,
11,
16,
22,
23,
ndv,
ndv,
ndv,
15,
21,
22,
ndv,
ndv,
ndv,
ndv,
20,
ndv,
ndv,
ndv,
ndv,
],
)
def test_raster_transform_clone(self):
with mock.patch.object(GDALRaster, "clone") as mocked_clone:
# Create in file based raster.
rstfile = 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 = 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")
def test_band_data(self):
rs = GDALRaster(self.rs_path)
band = rs.bands[0]
self.assertEqual(band.width, 163)
self.assertEqual(band.height, 174)
self.assertEqual(band.description, "")
self.assertEqual(band.datatype(), 1)
self.assertEqual(band.datatype(as_string=True), "GDT_Byte")
self.assertEqual(band.color_interp(), 1)
self.assertEqual(band.color_interp(as_string=True), "GCI_GrayIndex")
self.assertEqual(band.nodata_value, 15)
if numpy:
data = band.data()
assert_array = numpy.loadtxt(
os.path.join(
os.path.dirname(__file__), "../data/rasters/raster.numpy.txt"
)
)
numpy.testing.assert_equal(data, assert_array)
self.assertEqual(data.shape, (band.height, band.width))
def test_band_statistics(self):
with tempfile.TemporaryDirectory() as tmp_dir:
rs_path = os.path.join(tmp_dir, "raster.tif")
shutil.copyfile(self.rs_path, rs_path)
rs = GDALRaster(rs_path)
band = rs.bands[0]
pam_file = rs_path + ".aux.xml"
smin, smax, smean, sstd = band.statistics(approximate=True)
self.assertEqual(smin, 0)
self.assertEqual(smax, 9)
self.assertAlmostEqual(smean, 2.842331288343558)
self.assertAlmostEqual(sstd, 2.3965567248965356)
smin, smax, smean, sstd = band.statistics(approximate=False, refresh=True)
self.assertEqual(smin, 0)
self.assertEqual(smax, 9)
self.assertAlmostEqual(smean, 2.828326634228898)
self.assertAlmostEqual(sstd, 2.4260526986669095)
self.assertEqual(band.min, 0)
self.assertEqual(band.max, 9)
self.assertAlmostEqual(band.mean, 2.828326634228898)
self.assertAlmostEqual(band.std, 2.4260526986669095)
# Statistics are persisted into PAM file on band close
rs = band = None
self.assertTrue(os.path.isfile(pam_file))
def _remove_aux_file(self):
pam_file = self.rs_path + ".aux.xml"
if os.path.isfile(pam_file):
os.remove(pam_file)
def test_read_mode_error(self):
# Open raster in read mode
rs = GDALRaster(self.rs_path, write=False)
band = rs.bands[0]
self.addCleanup(self._remove_aux_file)
# Setting attributes in write mode raises exception in the _flush method
with self.assertRaises(GDALException):
setattr(band, "nodata_value", 10)
def test_band_data_setters(self):
# Create in-memory raster and get band
rsmem = GDALRaster(
{
"datatype": 1,
"driver": "MEM",
"name": "mem_rst",
"width": 10,
"height": 10,
"nr_of_bands": 1,
"srid": 4326,
}
)
bandmem = rsmem.bands[0]
# Set nodata value
bandmem.nodata_value = 99
self.assertEqual(bandmem.nodata_value, 99)
# Set data for entire dataset
bandmem.data(range(100))
if numpy:
numpy.testing.assert_equal(
bandmem.data(), numpy.arange(100).reshape(10, 10)
)
else:
self.assertEqual(bandmem.data(), list(range(100)))
# Prepare data for setting values in subsequent tests
block = list(range(100, 104))
packed_block = struct.pack("<" + "B B B B", *block)
# Set data from list
bandmem.data(block, (1, 1), (2, 2))
result = bandmem.data(offset=(1, 1), size=(2, 2))
if numpy:
numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
else:
self.assertEqual(result, block)
# Set data from packed block
bandmem.data(packed_block, (1, 1), (2, 2))
result = bandmem.data(offset=(1, 1), size=(2, 2))
if numpy:
numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
else:
self.assertEqual(result, block)
# Set data from bytes
bandmem.data(bytes(packed_block), (1, 1), (2, 2))
result = bandmem.data(offset=(1, 1), size=(2, 2))
if numpy:
numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
else:
self.assertEqual(result, block)
# Set data from bytearray
bandmem.data(bytearray(packed_block), (1, 1), (2, 2))
result = bandmem.data(offset=(1, 1), size=(2, 2))
if numpy:
numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
else:
self.assertEqual(result, block)
# Set data from memoryview
bandmem.data(memoryview(packed_block), (1, 1), (2, 2))
result = bandmem.data(offset=(1, 1), size=(2, 2))
if numpy:
numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
else:
self.assertEqual(result, block)
# Set data from numpy array
if numpy:
bandmem.data(numpy.array(block, dtype="int8").reshape(2, 2), (1, 1), (2, 2))
numpy.testing.assert_equal(
bandmem.data(offset=(1, 1), size=(2, 2)),
numpy.array(block).reshape(2, 2),
)
# Test json input data
rsmemjson = GDALRaster(JSON_RASTER)
bandmemjson = rsmemjson.bands[0]
if numpy:
numpy.testing.assert_equal(
bandmemjson.data(), numpy.array(range(25)).reshape(5, 5)
)
else:
self.assertEqual(bandmemjson.data(), list(range(25)))
def test_band_statistics_automatic_refresh(self):
rsmem = GDALRaster(
{
"srid": 4326,
"width": 2,
"height": 2,
"bands": [{"data": [0] * 4, "nodata_value": 99}],
}
)
band = rsmem.bands[0]
# Populate statistics cache
self.assertEqual(band.statistics(), (0, 0, 0, 0))
# Change data
band.data([1, 1, 0, 0])
# Statistics are properly updated
self.assertEqual(band.statistics(), (0.0, 1.0, 0.5, 0.5))
# Change nodata_value
band.nodata_value = 0
# Statistics are properly updated
self.assertEqual(band.statistics(), (1.0, 1.0, 1.0, 0.0))
def test_band_statistics_empty_band(self):
rsmem = GDALRaster(
{
"srid": 4326,
"width": 1,
"height": 1,
"bands": [{"data": [0], "nodata_value": 0}],
}
)
self.assertEqual(rsmem.bands[0].statistics(), (None, None, None, None))
def test_band_delete_nodata(self):
rsmem = GDALRaster(
{
"srid": 4326,
"width": 1,
"height": 1,
"bands": [{"data": [0], "nodata_value": 1}],
}
)
rsmem.bands[0].nodata_value = None
self.assertIsNone(rsmem.bands[0].nodata_value)
def test_band_data_replication(self):
band = GDALRaster(
{
"srid": 4326,
"width": 3,
"height": 3,
"bands": [{"data": range(10, 19), "nodata_value": 0}],
}
).bands[0]
# Variations for input (data, shape, expected result).
combos = (
([1], (1, 1), [1] * 9),
(range(3), (1, 3), [0, 0, 0, 1, 1, 1, 2, 2, 2]),
(range(3), (3, 1), [0, 1, 2, 0, 1, 2, 0, 1, 2]),
)
for combo in combos:
band.data(combo[0], shape=combo[1])
if numpy:
numpy.testing.assert_equal(
band.data(), numpy.array(combo[2]).reshape(3, 3)
)
else:
self.assertEqual(band.data(), list(combo[2]))