test_raster.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. import os
  2. import struct
  3. import tempfile
  4. from django.contrib.gis.gdal import GDAL_VERSION, GDALRaster
  5. from django.contrib.gis.gdal.error import GDALException
  6. from django.contrib.gis.gdal.raster.band import GDALBand
  7. from django.contrib.gis.shortcuts import numpy
  8. from django.test import SimpleTestCase
  9. from ..data.rasters.textrasters import JSON_RASTER
  10. class GDALRasterTests(SimpleTestCase):
  11. """
  12. Test a GDALRaster instance created from a file (GeoTiff).
  13. """
  14. def setUp(self):
  15. self.rs_path = os.path.join(os.path.dirname(__file__), '../data/rasters/raster.tif')
  16. self.rs = GDALRaster(self.rs_path)
  17. def test_rs_name_repr(self):
  18. self.assertEqual(self.rs_path, self.rs.name)
  19. self.assertRegex(repr(self.rs), r"<Raster object at 0x\w+>")
  20. def test_rs_driver(self):
  21. self.assertEqual(self.rs.driver.name, 'GTiff')
  22. def test_rs_size(self):
  23. self.assertEqual(self.rs.width, 163)
  24. self.assertEqual(self.rs.height, 174)
  25. def test_rs_srs(self):
  26. self.assertEqual(self.rs.srs.srid, 3086)
  27. self.assertEqual(self.rs.srs.units, (1.0, 'metre'))
  28. def test_rs_srid(self):
  29. rast = GDALRaster({
  30. 'width': 16,
  31. 'height': 16,
  32. 'srid': 4326,
  33. })
  34. self.assertEqual(rast.srid, 4326)
  35. rast.srid = 3086
  36. self.assertEqual(rast.srid, 3086)
  37. def test_geotransform_and_friends(self):
  38. # Assert correct values for file based raster
  39. self.assertEqual(
  40. self.rs.geotransform,
  41. [511700.4680706557, 100.0, 0.0, 435103.3771231986, 0.0, -100.0]
  42. )
  43. self.assertEqual(self.rs.origin, [511700.4680706557, 435103.3771231986])
  44. self.assertEqual(self.rs.origin.x, 511700.4680706557)
  45. self.assertEqual(self.rs.origin.y, 435103.3771231986)
  46. self.assertEqual(self.rs.scale, [100.0, -100.0])
  47. self.assertEqual(self.rs.scale.x, 100.0)
  48. self.assertEqual(self.rs.scale.y, -100.0)
  49. self.assertEqual(self.rs.skew, [0, 0])
  50. self.assertEqual(self.rs.skew.x, 0)
  51. self.assertEqual(self.rs.skew.y, 0)
  52. # Create in-memory rasters and change gtvalues
  53. rsmem = GDALRaster(JSON_RASTER)
  54. # geotransform accepts both floats and ints
  55. rsmem.geotransform = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
  56. self.assertEqual(rsmem.geotransform, [0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
  57. rsmem.geotransform = range(6)
  58. self.assertEqual(rsmem.geotransform, [float(x) for x in range(6)])
  59. self.assertEqual(rsmem.origin, [0, 3])
  60. self.assertEqual(rsmem.origin.x, 0)
  61. self.assertEqual(rsmem.origin.y, 3)
  62. self.assertEqual(rsmem.scale, [1, 5])
  63. self.assertEqual(rsmem.scale.x, 1)
  64. self.assertEqual(rsmem.scale.y, 5)
  65. self.assertEqual(rsmem.skew, [2, 4])
  66. self.assertEqual(rsmem.skew.x, 2)
  67. self.assertEqual(rsmem.skew.y, 4)
  68. self.assertEqual(rsmem.width, 5)
  69. self.assertEqual(rsmem.height, 5)
  70. def test_geotransform_bad_inputs(self):
  71. rsmem = GDALRaster(JSON_RASTER)
  72. error_geotransforms = [
  73. [1, 2],
  74. [1, 2, 3, 4, 5, 'foo'],
  75. [1, 2, 3, 4, 5, 6, 'foo'],
  76. ]
  77. msg = 'Geotransform must consist of 6 numeric values.'
  78. for geotransform in error_geotransforms:
  79. with self.subTest(i=geotransform), self.assertRaisesMessage(ValueError, msg):
  80. rsmem.geotransform = geotransform
  81. def test_rs_extent(self):
  82. self.assertEqual(
  83. self.rs.extent,
  84. (511700.4680706557, 417703.3771231986, 528000.4680706557, 435103.3771231986)
  85. )
  86. def test_rs_bands(self):
  87. self.assertEqual(len(self.rs.bands), 1)
  88. self.assertIsInstance(self.rs.bands[0], GDALBand)
  89. def test_memory_based_raster_creation(self):
  90. # Create uint8 raster with full pixel data range (0-255)
  91. rast = GDALRaster({
  92. 'datatype': 1,
  93. 'width': 16,
  94. 'height': 16,
  95. 'srid': 4326,
  96. 'bands': [{
  97. 'data': range(256),
  98. 'nodata_value': 255,
  99. }],
  100. })
  101. # Get array from raster
  102. result = rast.bands[0].data()
  103. if numpy:
  104. result = result.flatten().tolist()
  105. # Assert data is same as original input
  106. self.assertEqual(result, list(range(256)))
  107. def test_file_based_raster_creation(self):
  108. # Prepare tempfile
  109. rstfile = tempfile.NamedTemporaryFile(suffix='.tif')
  110. # Create file-based raster from scratch
  111. GDALRaster({
  112. 'datatype': self.rs.bands[0].datatype(),
  113. 'driver': 'tif',
  114. 'name': rstfile.name,
  115. 'width': 163,
  116. 'height': 174,
  117. 'nr_of_bands': 1,
  118. 'srid': self.rs.srs.wkt,
  119. 'origin': (self.rs.origin.x, self.rs.origin.y),
  120. 'scale': (self.rs.scale.x, self.rs.scale.y),
  121. 'skew': (self.rs.skew.x, self.rs.skew.y),
  122. 'bands': [{
  123. 'data': self.rs.bands[0].data(),
  124. 'nodata_value': self.rs.bands[0].nodata_value,
  125. }],
  126. })
  127. # Reload newly created raster from file
  128. restored_raster = GDALRaster(rstfile.name)
  129. self.assertEqual(restored_raster.srs.wkt, self.rs.srs.wkt)
  130. self.assertEqual(restored_raster.geotransform, self.rs.geotransform)
  131. if numpy:
  132. numpy.testing.assert_equal(
  133. restored_raster.bands[0].data(),
  134. self.rs.bands[0].data()
  135. )
  136. else:
  137. self.assertEqual(restored_raster.bands[0].data(), self.rs.bands[0].data())
  138. def test_offset_size_and_shape_on_raster_creation(self):
  139. rast = GDALRaster({
  140. 'datatype': 1,
  141. 'width': 4,
  142. 'height': 4,
  143. 'srid': 4326,
  144. 'bands': [{
  145. 'data': (1,),
  146. 'offset': (1, 1),
  147. 'size': (2, 2),
  148. 'shape': (1, 1),
  149. 'nodata_value': 2,
  150. }],
  151. })
  152. # Get array from raster.
  153. result = rast.bands[0].data()
  154. if numpy:
  155. result = result.flatten().tolist()
  156. # Band data is equal to nodata value except on input block of ones.
  157. self.assertEqual(
  158. result,
  159. [2, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 2]
  160. )
  161. def test_set_nodata_value_on_raster_creation(self):
  162. # Create raster filled with nodata values.
  163. rast = GDALRaster({
  164. 'datatype': 1,
  165. 'width': 2,
  166. 'height': 2,
  167. 'srid': 4326,
  168. 'bands': [{'nodata_value': 23}],
  169. })
  170. # Get array from raster.
  171. result = rast.bands[0].data()
  172. if numpy:
  173. result = result.flatten().tolist()
  174. # All band data is equal to nodata value.
  175. self.assertEqual(result, [23, ] * 4)
  176. def test_set_nodata_none_on_raster_creation(self):
  177. if GDAL_VERSION < (2, 1):
  178. self.skipTest("GDAL >= 2.1 is required for this test.")
  179. # Create raster without data and without nodata value.
  180. rast = GDALRaster({
  181. 'datatype': 1,
  182. 'width': 2,
  183. 'height': 2,
  184. 'srid': 4326,
  185. 'bands': [{'nodata_value': None}],
  186. })
  187. # Get array from raster.
  188. result = rast.bands[0].data()
  189. if numpy:
  190. result = result.flatten().tolist()
  191. # Band data is equal to zero becaues no nodata value has been specified.
  192. self.assertEqual(result, [0] * 4)
  193. def test_raster_metadata_property(self):
  194. # Check for required gdal version.
  195. if GDAL_VERSION < (1, 11):
  196. msg = 'GDAL ≥ 1.11 is required for using the metadata property.'
  197. with self.assertRaisesMessage(ValueError, msg):
  198. self.rs.metadata
  199. return
  200. self.assertEqual(
  201. self.rs.metadata,
  202. {'DEFAULT': {'AREA_OR_POINT': 'Area'}, 'IMAGE_STRUCTURE': {'INTERLEAVE': 'BAND'}},
  203. )
  204. # Create file-based raster from scratch
  205. source = GDALRaster({
  206. 'datatype': 1,
  207. 'width': 2,
  208. 'height': 2,
  209. 'srid': 4326,
  210. 'bands': [{'data': range(4), 'nodata_value': 99}],
  211. })
  212. # Set metadata on raster and on a band.
  213. metadata = {
  214. 'DEFAULT': {'OWNER': 'Django', 'VERSION': '1.0', 'AREA_OR_POINT': 'Point', },
  215. }
  216. source.metadata = metadata
  217. source.bands[0].metadata = metadata
  218. self.assertEqual(source.metadata['DEFAULT'], metadata['DEFAULT'])
  219. self.assertEqual(source.bands[0].metadata['DEFAULT'], metadata['DEFAULT'])
  220. # Update metadata on raster.
  221. metadata = {
  222. 'DEFAULT': {'VERSION': '2.0', },
  223. }
  224. source.metadata = metadata
  225. self.assertEqual(source.metadata['DEFAULT']['VERSION'], '2.0')
  226. # Remove metadata on raster.
  227. metadata = {
  228. 'DEFAULT': {'OWNER': None, },
  229. }
  230. source.metadata = metadata
  231. self.assertNotIn('OWNER', source.metadata['DEFAULT'])
  232. def test_raster_info_accessor(self):
  233. if GDAL_VERSION < (2, 1):
  234. msg = 'GDAL ≥ 2.1 is required for using the info property.'
  235. with self.assertRaisesMessage(ValueError, msg):
  236. self.rs.info
  237. return
  238. gdalinfo = """
  239. Driver: GTiff/GeoTIFF
  240. Files: {0}
  241. Size is 163, 174
  242. Coordinate System is:
  243. PROJCS["NAD83 / Florida GDL Albers",
  244. GEOGCS["NAD83",
  245. DATUM["North_American_Datum_1983",
  246. SPHEROID["GRS 1980",6378137,298.257222101,
  247. AUTHORITY["EPSG","7019"]],
  248. TOWGS84[0,0,0,0,0,0,0],
  249. AUTHORITY["EPSG","6269"]],
  250. PRIMEM["Greenwich",0,
  251. AUTHORITY["EPSG","8901"]],
  252. UNIT["degree",0.0174532925199433,
  253. AUTHORITY["EPSG","9122"]],
  254. AUTHORITY["EPSG","4269"]],
  255. PROJECTION["Albers_Conic_Equal_Area"],
  256. PARAMETER["standard_parallel_1",24],
  257. PARAMETER["standard_parallel_2",31.5],
  258. PARAMETER["latitude_of_center",24],
  259. PARAMETER["longitude_of_center",-84],
  260. PARAMETER["false_easting",400000],
  261. PARAMETER["false_northing",0],
  262. UNIT["metre",1,
  263. AUTHORITY["EPSG","9001"]],
  264. AXIS["X",EAST],
  265. AXIS["Y",NORTH],
  266. AUTHORITY["EPSG","3086"]]
  267. Origin = (511700.468070655711927,435103.377123198588379)
  268. Pixel Size = (100.000000000000000,-100.000000000000000)
  269. Metadata:
  270. AREA_OR_POINT=Area
  271. Image Structure Metadata:
  272. INTERLEAVE=BAND
  273. Corner Coordinates:
  274. Upper Left ( 511700.468, 435103.377) ( 82d51'46.16"W, 27d55' 1.53"N)
  275. Lower Left ( 511700.468, 417703.377) ( 82d51'52.04"W, 27d45'37.50"N)
  276. Upper Right ( 528000.468, 435103.377) ( 82d41'48.81"W, 27d54'56.30"N)
  277. Lower Right ( 528000.468, 417703.377) ( 82d41'55.54"W, 27d45'32.28"N)
  278. Center ( 519850.468, 426403.377) ( 82d46'50.64"W, 27d50'16.99"N)
  279. Band 1 Block=163x50 Type=Byte, ColorInterp=Gray
  280. NoData Value=15
  281. """.format(self.rs_path)
  282. # Data
  283. info_dyn = [line.strip() for line in self.rs.info.split('\n') if line.strip() != '']
  284. info_ref = [line.strip() for line in gdalinfo.split('\n') if line.strip() != '']
  285. self.assertEqual(info_dyn, info_ref)
  286. def test_compressed_file_based_raster_creation(self):
  287. rstfile = tempfile.NamedTemporaryFile(suffix='.tif')
  288. # Make a compressed copy of an existing raster.
  289. compressed = self.rs.warp({'papsz_options': {'compress': 'packbits'}, 'name': rstfile.name})
  290. # Check physically if compression worked.
  291. self.assertLess(os.path.getsize(compressed.name), os.path.getsize(self.rs.name))
  292. if GDAL_VERSION > (1, 11):
  293. # Create file-based raster with options from scratch.
  294. compressed = GDALRaster({
  295. 'datatype': 1,
  296. 'driver': 'tif',
  297. 'name': rstfile.name,
  298. 'width': 40,
  299. 'height': 40,
  300. 'srid': 3086,
  301. 'origin': (500000, 400000),
  302. 'scale': (100, -100),
  303. 'skew': (0, 0),
  304. 'bands': [{
  305. 'data': range(40 ^ 2),
  306. 'nodata_value': 255,
  307. }],
  308. 'papsz_options': {
  309. 'compress': 'packbits',
  310. 'pixeltype': 'signedbyte',
  311. 'blockxsize': 23,
  312. 'blockysize': 23,
  313. }
  314. })
  315. # Check if options used on creation are stored in metadata.
  316. # Reopening the raster ensures that all metadata has been written
  317. # to the file.
  318. compressed = GDALRaster(compressed.name)
  319. self.assertEqual(compressed.metadata['IMAGE_STRUCTURE']['COMPRESSION'], 'PACKBITS',)
  320. self.assertEqual(compressed.bands[0].metadata['IMAGE_STRUCTURE']['PIXELTYPE'], 'SIGNEDBYTE')
  321. if GDAL_VERSION >= (2, 1):
  322. self.assertIn('Block=40x23', compressed.info)
  323. def test_raster_warp(self):
  324. # Create in memory raster
  325. source = GDALRaster({
  326. 'datatype': 1,
  327. 'driver': 'MEM',
  328. 'name': 'sourceraster',
  329. 'width': 4,
  330. 'height': 4,
  331. 'nr_of_bands': 1,
  332. 'srid': 3086,
  333. 'origin': (500000, 400000),
  334. 'scale': (100, -100),
  335. 'skew': (0, 0),
  336. 'bands': [{
  337. 'data': range(16),
  338. 'nodata_value': 255,
  339. }],
  340. })
  341. # Test altering the scale, width, and height of a raster
  342. data = {
  343. 'scale': [200, -200],
  344. 'width': 2,
  345. 'height': 2,
  346. }
  347. target = source.warp(data)
  348. self.assertEqual(target.width, data['width'])
  349. self.assertEqual(target.height, data['height'])
  350. self.assertEqual(target.scale, data['scale'])
  351. self.assertEqual(target.bands[0].datatype(), source.bands[0].datatype())
  352. self.assertEqual(target.name, 'sourceraster_copy.MEM')
  353. result = target.bands[0].data()
  354. if numpy:
  355. result = result.flatten().tolist()
  356. self.assertEqual(result, [5, 7, 13, 15])
  357. # Test altering the name and datatype (to float)
  358. data = {
  359. 'name': '/path/to/targetraster.tif',
  360. 'datatype': 6,
  361. }
  362. target = source.warp(data)
  363. self.assertEqual(target.bands[0].datatype(), 6)
  364. self.assertEqual(target.name, '/path/to/targetraster.tif')
  365. self.assertEqual(target.driver.name, 'MEM')
  366. result = target.bands[0].data()
  367. if numpy:
  368. result = result.flatten().tolist()
  369. self.assertEqual(
  370. result,
  371. [0.0, 1.0, 2.0, 3.0,
  372. 4.0, 5.0, 6.0, 7.0,
  373. 8.0, 9.0, 10.0, 11.0,
  374. 12.0, 13.0, 14.0, 15.0]
  375. )
  376. def test_raster_warp_nodata_zone(self):
  377. # Create in memory raster.
  378. source = GDALRaster({
  379. 'datatype': 1,
  380. 'driver': 'MEM',
  381. 'width': 4,
  382. 'height': 4,
  383. 'srid': 3086,
  384. 'origin': (500000, 400000),
  385. 'scale': (100, -100),
  386. 'skew': (0, 0),
  387. 'bands': [{
  388. 'data': range(16),
  389. 'nodata_value': 23,
  390. }],
  391. })
  392. # Warp raster onto a location that does not cover any pixels of the original.
  393. result = source.warp({'origin': (200000, 200000)}).bands[0].data()
  394. if numpy:
  395. result = result.flatten().tolist()
  396. # The result is an empty raster filled with the correct nodata value.
  397. self.assertEqual(result, [23] * 16)
  398. def test_raster_transform(self):
  399. # Prepare tempfile and nodata value
  400. rstfile = tempfile.NamedTemporaryFile(suffix='.tif')
  401. ndv = 99
  402. # Create in file based raster
  403. source = GDALRaster({
  404. 'datatype': 1,
  405. 'driver': 'tif',
  406. 'name': rstfile.name,
  407. 'width': 5,
  408. 'height': 5,
  409. 'nr_of_bands': 1,
  410. 'srid': 4326,
  411. 'origin': (-5, 5),
  412. 'scale': (2, -2),
  413. 'skew': (0, 0),
  414. 'bands': [{
  415. 'data': range(25),
  416. 'nodata_value': ndv,
  417. }],
  418. })
  419. # Transform raster into srid 4326.
  420. target = source.transform(3086)
  421. # Reload data from disk
  422. target = GDALRaster(target.name)
  423. self.assertEqual(target.srs.srid, 3086)
  424. self.assertEqual(target.width, 7)
  425. self.assertEqual(target.height, 7)
  426. self.assertEqual(target.bands[0].datatype(), source.bands[0].datatype())
  427. self.assertAlmostEqual(target.origin[0], 9124842.791079799)
  428. self.assertAlmostEqual(target.origin[1], 1589911.6476407414)
  429. self.assertAlmostEqual(target.scale[0], 223824.82664250192)
  430. self.assertAlmostEqual(target.scale[1], -223824.82664250192)
  431. self.assertEqual(target.skew, [0, 0])
  432. result = target.bands[0].data()
  433. if numpy:
  434. result = result.flatten().tolist()
  435. # The reprojection of a raster that spans over a large area
  436. # skews the data matrix and might introduce nodata values.
  437. self.assertEqual(
  438. result,
  439. [
  440. ndv, ndv, ndv, ndv, 4, ndv, ndv,
  441. ndv, ndv, 2, 3, 9, ndv, ndv,
  442. ndv, 1, 2, 8, 13, 19, ndv,
  443. 0, 6, 6, 12, 18, 18, 24,
  444. ndv, 10, 11, 16, 22, 23, ndv,
  445. ndv, ndv, 15, 21, 22, ndv, ndv,
  446. ndv, ndv, 20, ndv, ndv, ndv, ndv,
  447. ]
  448. )
  449. class GDALBandTests(SimpleTestCase):
  450. def setUp(self):
  451. self.rs_path = os.path.join(os.path.dirname(__file__), '../data/rasters/raster.tif')
  452. rs = GDALRaster(self.rs_path)
  453. self.band = rs.bands[0]
  454. def test_band_data(self):
  455. pam_file = self.rs_path + '.aux.xml'
  456. self.assertEqual(self.band.width, 163)
  457. self.assertEqual(self.band.height, 174)
  458. self.assertEqual(self.band.description, '')
  459. self.assertEqual(self.band.datatype(), 1)
  460. self.assertEqual(self.band.datatype(as_string=True), 'GDT_Byte')
  461. self.assertEqual(self.band.nodata_value, 15)
  462. if numpy:
  463. data = self.band.data()
  464. assert_array = numpy.loadtxt(
  465. os.path.join(os.path.dirname(__file__), '../data/rasters/raster.numpy.txt')
  466. )
  467. numpy.testing.assert_equal(data, assert_array)
  468. self.assertEqual(data.shape, (self.band.height, self.band.width))
  469. try:
  470. smin, smax, smean, sstd = self.band.statistics(approximate=True)
  471. self.assertEqual(smin, 0)
  472. self.assertEqual(smax, 9)
  473. self.assertAlmostEqual(smean, 2.842331288343558)
  474. self.assertAlmostEqual(sstd, 2.3965567248965356)
  475. smin, smax, smean, sstd = self.band.statistics(approximate=False, refresh=True)
  476. self.assertEqual(smin, 0)
  477. self.assertEqual(smax, 9)
  478. self.assertAlmostEqual(smean, 2.828326634228898)
  479. self.assertAlmostEqual(sstd, 2.4260526986669095)
  480. self.assertEqual(self.band.min, 0)
  481. self.assertEqual(self.band.max, 9)
  482. self.assertAlmostEqual(self.band.mean, 2.828326634228898)
  483. self.assertAlmostEqual(self.band.std, 2.4260526986669095)
  484. # Statistics are persisted into PAM file on band close
  485. self.band = None
  486. self.assertTrue(os.path.isfile(pam_file))
  487. finally:
  488. # Close band and remove file if created
  489. self.band = None
  490. if os.path.isfile(pam_file):
  491. os.remove(pam_file)
  492. def test_read_mode_error(self):
  493. # Open raster in read mode
  494. rs = GDALRaster(self.rs_path, write=False)
  495. band = rs.bands[0]
  496. # Setting attributes in write mode raises exception in the _flush method
  497. with self.assertRaises(GDALException):
  498. setattr(band, 'nodata_value', 10)
  499. def test_band_data_setters(self):
  500. # Create in-memory raster and get band
  501. rsmem = GDALRaster({
  502. 'datatype': 1,
  503. 'driver': 'MEM',
  504. 'name': 'mem_rst',
  505. 'width': 10,
  506. 'height': 10,
  507. 'nr_of_bands': 1,
  508. 'srid': 4326,
  509. })
  510. bandmem = rsmem.bands[0]
  511. # Set nodata value
  512. bandmem.nodata_value = 99
  513. self.assertEqual(bandmem.nodata_value, 99)
  514. # Set data for entire dataset
  515. bandmem.data(range(100))
  516. if numpy:
  517. numpy.testing.assert_equal(bandmem.data(), numpy.arange(100).reshape(10, 10))
  518. else:
  519. self.assertEqual(bandmem.data(), list(range(100)))
  520. # Prepare data for setting values in subsequent tests
  521. block = list(range(100, 104))
  522. packed_block = struct.pack('<' + 'B B B B', *block)
  523. # Set data from list
  524. bandmem.data(block, (1, 1), (2, 2))
  525. result = bandmem.data(offset=(1, 1), size=(2, 2))
  526. if numpy:
  527. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  528. else:
  529. self.assertEqual(result, block)
  530. # Set data from packed block
  531. bandmem.data(packed_block, (1, 1), (2, 2))
  532. result = bandmem.data(offset=(1, 1), size=(2, 2))
  533. if numpy:
  534. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  535. else:
  536. self.assertEqual(result, block)
  537. # Set data from bytes
  538. bandmem.data(bytes(packed_block), (1, 1), (2, 2))
  539. result = bandmem.data(offset=(1, 1), size=(2, 2))
  540. if numpy:
  541. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  542. else:
  543. self.assertEqual(result, block)
  544. # Set data from bytearray
  545. bandmem.data(bytearray(packed_block), (1, 1), (2, 2))
  546. result = bandmem.data(offset=(1, 1), size=(2, 2))
  547. if numpy:
  548. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  549. else:
  550. self.assertEqual(result, block)
  551. # Set data from memoryview
  552. bandmem.data(memoryview(packed_block), (1, 1), (2, 2))
  553. result = bandmem.data(offset=(1, 1), size=(2, 2))
  554. if numpy:
  555. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  556. else:
  557. self.assertEqual(result, block)
  558. # Set data from numpy array
  559. if numpy:
  560. bandmem.data(numpy.array(block, dtype='int8').reshape(2, 2), (1, 1), (2, 2))
  561. numpy.testing.assert_equal(
  562. bandmem.data(offset=(1, 1), size=(2, 2)),
  563. numpy.array(block).reshape(2, 2)
  564. )
  565. # Test json input data
  566. rsmemjson = GDALRaster(JSON_RASTER)
  567. bandmemjson = rsmemjson.bands[0]
  568. if numpy:
  569. numpy.testing.assert_equal(
  570. bandmemjson.data(),
  571. numpy.array(range(25)).reshape(5, 5)
  572. )
  573. else:
  574. self.assertEqual(bandmemjson.data(), list(range(25)))
  575. def test_band_statistics_automatic_refresh(self):
  576. rsmem = GDALRaster({
  577. 'srid': 4326,
  578. 'width': 2,
  579. 'height': 2,
  580. 'bands': [{'data': [0] * 4, 'nodata_value': 99}],
  581. })
  582. band = rsmem.bands[0]
  583. # Populate statistics cache
  584. self.assertEqual(band.statistics(), (0, 0, 0, 0))
  585. # Change data
  586. band.data([1, 1, 0, 0])
  587. # Statistics are properly updated
  588. self.assertEqual(band.statistics(), (0.0, 1.0, 0.5, 0.5))
  589. # Change nodata_value
  590. band.nodata_value = 0
  591. # Statistics are properly updated
  592. self.assertEqual(band.statistics(), (1.0, 1.0, 1.0, 0.0))
  593. def test_band_statistics_empty_band(self):
  594. rsmem = GDALRaster({
  595. 'srid': 4326,
  596. 'width': 1,
  597. 'height': 1,
  598. 'bands': [{'data': [0], 'nodata_value': 0}],
  599. })
  600. self.assertEqual(rsmem.bands[0].statistics(), (None, None, None, None))
  601. def test_band_delete_nodata(self):
  602. rsmem = GDALRaster({
  603. 'srid': 4326,
  604. 'width': 1,
  605. 'height': 1,
  606. 'bands': [{'data': [0], 'nodata_value': 1}],
  607. })
  608. if GDAL_VERSION < (2, 1):
  609. msg = 'GDAL >= 2.1 required to delete nodata values.'
  610. with self.assertRaisesMessage(ValueError, msg):
  611. rsmem.bands[0].nodata_value = None
  612. else:
  613. rsmem.bands[0].nodata_value = None
  614. self.assertIsNone(rsmem.bands[0].nodata_value)
  615. def test_band_data_replication(self):
  616. band = GDALRaster({
  617. 'srid': 4326,
  618. 'width': 3,
  619. 'height': 3,
  620. 'bands': [{'data': range(10, 19), 'nodata_value': 0}],
  621. }).bands[0]
  622. # Variations for input (data, shape, expected result).
  623. combos = (
  624. ([1], (1, 1), [1] * 9),
  625. (range(3), (1, 3), [0, 0, 0, 1, 1, 1, 2, 2, 2]),
  626. (range(3), (3, 1), [0, 1, 2, 0, 1, 2, 0, 1, 2]),
  627. )
  628. for combo in combos:
  629. band.data(combo[0], shape=combo[1])
  630. if numpy:
  631. numpy.testing.assert_equal(band.data(), numpy.array(combo[2]).reshape(3, 3))
  632. else:
  633. self.assertEqual(band.data(), list(combo[2]))