test_raster.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  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_vsi_raster_creation(self):
  139. # Open a raster as a file object.
  140. with open(self.rs_path, 'rb') as dat:
  141. # Instantiate a raster from the file binary buffer.
  142. vsimem = GDALRaster(dat.read())
  143. # The data of the in-memory file is equal to the source file.
  144. result = vsimem.bands[0].data()
  145. target = self.rs.bands[0].data()
  146. if numpy:
  147. result = result.flatten().tolist()
  148. target = target.flatten().tolist()
  149. self.assertEqual(result, target)
  150. def test_vsi_raster_deletion(self):
  151. path = '/vsimem/raster.tif'
  152. # Create a vsi-based raster from scratch.
  153. vsimem = GDALRaster({
  154. 'name': path,
  155. 'driver': 'tif',
  156. 'width': 4,
  157. 'height': 4,
  158. 'srid': 4326,
  159. 'bands': [{
  160. 'data': range(16),
  161. }],
  162. })
  163. # The virtual file exists.
  164. rst = GDALRaster(path)
  165. self.assertEqual(rst.width, 4)
  166. # Delete GDALRaster.
  167. del vsimem
  168. del rst
  169. # The virtual file has been removed.
  170. msg = 'Could not open the datasource at "/vsimem/raster.tif"'
  171. with self.assertRaisesMessage(GDALException, msg):
  172. GDALRaster(path)
  173. def test_vsi_invalid_buffer_error(self):
  174. msg = 'Failed creating VSI raster from the input buffer.'
  175. with self.assertRaisesMessage(GDALException, msg):
  176. GDALRaster(b'not-a-raster-buffer')
  177. def test_vsi_buffer_property(self):
  178. # Create a vsi-based raster from scratch.
  179. rast = GDALRaster({
  180. 'name': '/vsimem/raster.tif',
  181. 'driver': 'tif',
  182. 'width': 4,
  183. 'height': 4,
  184. 'srid': 4326,
  185. 'bands': [{
  186. 'data': range(16),
  187. }],
  188. })
  189. # Do a round trip from raster to buffer to raster.
  190. result = GDALRaster(rast.vsi_buffer).bands[0].data()
  191. if numpy:
  192. result = result.flatten().tolist()
  193. # Band data is equal to nodata value except on input block of ones.
  194. self.assertEqual(result, list(range(16)))
  195. # The vsi buffer is None for rasters that are not vsi based.
  196. self.assertIsNone(self.rs.vsi_buffer)
  197. def test_offset_size_and_shape_on_raster_creation(self):
  198. rast = GDALRaster({
  199. 'datatype': 1,
  200. 'width': 4,
  201. 'height': 4,
  202. 'srid': 4326,
  203. 'bands': [{
  204. 'data': (1,),
  205. 'offset': (1, 1),
  206. 'size': (2, 2),
  207. 'shape': (1, 1),
  208. 'nodata_value': 2,
  209. }],
  210. })
  211. # Get array from raster.
  212. result = rast.bands[0].data()
  213. if numpy:
  214. result = result.flatten().tolist()
  215. # Band data is equal to nodata value except on input block of ones.
  216. self.assertEqual(
  217. result,
  218. [2, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 2]
  219. )
  220. def test_set_nodata_value_on_raster_creation(self):
  221. # Create raster filled with nodata values.
  222. rast = GDALRaster({
  223. 'datatype': 1,
  224. 'width': 2,
  225. 'height': 2,
  226. 'srid': 4326,
  227. 'bands': [{'nodata_value': 23}],
  228. })
  229. # Get array from raster.
  230. result = rast.bands[0].data()
  231. if numpy:
  232. result = result.flatten().tolist()
  233. # All band data is equal to nodata value.
  234. self.assertEqual(result, [23, ] * 4)
  235. def test_set_nodata_none_on_raster_creation(self):
  236. if GDAL_VERSION < (2, 1):
  237. self.skipTest("GDAL >= 2.1 is required for this test.")
  238. # Create raster without data and without nodata value.
  239. rast = GDALRaster({
  240. 'datatype': 1,
  241. 'width': 2,
  242. 'height': 2,
  243. 'srid': 4326,
  244. 'bands': [{'nodata_value': None}],
  245. })
  246. # Get array from raster.
  247. result = rast.bands[0].data()
  248. if numpy:
  249. result = result.flatten().tolist()
  250. # Band data is equal to zero becaues no nodata value has been specified.
  251. self.assertEqual(result, [0] * 4)
  252. def test_raster_metadata_property(self):
  253. # Check for required gdal version.
  254. if GDAL_VERSION < (1, 11):
  255. msg = 'GDAL ≥ 1.11 is required for using the metadata property.'
  256. with self.assertRaisesMessage(ValueError, msg):
  257. self.rs.metadata
  258. return
  259. self.assertEqual(
  260. self.rs.metadata,
  261. {'DEFAULT': {'AREA_OR_POINT': 'Area'}, 'IMAGE_STRUCTURE': {'INTERLEAVE': 'BAND'}},
  262. )
  263. # Create file-based raster from scratch
  264. source = GDALRaster({
  265. 'datatype': 1,
  266. 'width': 2,
  267. 'height': 2,
  268. 'srid': 4326,
  269. 'bands': [{'data': range(4), 'nodata_value': 99}],
  270. })
  271. # Set metadata on raster and on a band.
  272. metadata = {
  273. 'DEFAULT': {'OWNER': 'Django', 'VERSION': '1.0', 'AREA_OR_POINT': 'Point', },
  274. }
  275. source.metadata = metadata
  276. source.bands[0].metadata = metadata
  277. self.assertEqual(source.metadata['DEFAULT'], metadata['DEFAULT'])
  278. self.assertEqual(source.bands[0].metadata['DEFAULT'], metadata['DEFAULT'])
  279. # Update metadata on raster.
  280. metadata = {
  281. 'DEFAULT': {'VERSION': '2.0', },
  282. }
  283. source.metadata = metadata
  284. self.assertEqual(source.metadata['DEFAULT']['VERSION'], '2.0')
  285. # Remove metadata on raster.
  286. metadata = {
  287. 'DEFAULT': {'OWNER': None, },
  288. }
  289. source.metadata = metadata
  290. self.assertNotIn('OWNER', source.metadata['DEFAULT'])
  291. def test_raster_info_accessor(self):
  292. if GDAL_VERSION < (2, 1):
  293. msg = 'GDAL ≥ 2.1 is required for using the info property.'
  294. with self.assertRaisesMessage(ValueError, msg):
  295. self.rs.info
  296. return
  297. gdalinfo = """
  298. Driver: GTiff/GeoTIFF
  299. Files: {0}
  300. Size is 163, 174
  301. Coordinate System is:
  302. PROJCS["NAD83 / Florida GDL Albers",
  303. GEOGCS["NAD83",
  304. DATUM["North_American_Datum_1983",
  305. SPHEROID["GRS 1980",6378137,298.257222101,
  306. AUTHORITY["EPSG","7019"]],
  307. TOWGS84[0,0,0,0,0,0,0],
  308. AUTHORITY["EPSG","6269"]],
  309. PRIMEM["Greenwich",0,
  310. AUTHORITY["EPSG","8901"]],
  311. UNIT["degree",0.0174532925199433,
  312. AUTHORITY["EPSG","9122"]],
  313. AUTHORITY["EPSG","4269"]],
  314. PROJECTION["Albers_Conic_Equal_Area"],
  315. PARAMETER["standard_parallel_1",24],
  316. PARAMETER["standard_parallel_2",31.5],
  317. PARAMETER["latitude_of_center",24],
  318. PARAMETER["longitude_of_center",-84],
  319. PARAMETER["false_easting",400000],
  320. PARAMETER["false_northing",0],
  321. UNIT["metre",1,
  322. AUTHORITY["EPSG","9001"]],
  323. AXIS["X",EAST],
  324. AXIS["Y",NORTH],
  325. AUTHORITY["EPSG","3086"]]
  326. Origin = (511700.468070655711927,435103.377123198588379)
  327. Pixel Size = (100.000000000000000,-100.000000000000000)
  328. Metadata:
  329. AREA_OR_POINT=Area
  330. Image Structure Metadata:
  331. INTERLEAVE=BAND
  332. Corner Coordinates:
  333. Upper Left ( 511700.468, 435103.377) ( 82d51'46.16"W, 27d55' 1.53"N)
  334. Lower Left ( 511700.468, 417703.377) ( 82d51'52.04"W, 27d45'37.50"N)
  335. Upper Right ( 528000.468, 435103.377) ( 82d41'48.81"W, 27d54'56.30"N)
  336. Lower Right ( 528000.468, 417703.377) ( 82d41'55.54"W, 27d45'32.28"N)
  337. Center ( 519850.468, 426403.377) ( 82d46'50.64"W, 27d50'16.99"N)
  338. Band 1 Block=163x50 Type=Byte, ColorInterp=Gray
  339. NoData Value=15
  340. """.format(self.rs_path)
  341. # Data
  342. info_dyn = [line.strip() for line in self.rs.info.split('\n') if line.strip() != '']
  343. info_ref = [line.strip() for line in gdalinfo.split('\n') if line.strip() != '']
  344. self.assertEqual(info_dyn, info_ref)
  345. def test_compressed_file_based_raster_creation(self):
  346. rstfile = tempfile.NamedTemporaryFile(suffix='.tif')
  347. # Make a compressed copy of an existing raster.
  348. compressed = self.rs.warp({'papsz_options': {'compress': 'packbits'}, 'name': rstfile.name})
  349. # Check physically if compression worked.
  350. self.assertLess(os.path.getsize(compressed.name), os.path.getsize(self.rs.name))
  351. if GDAL_VERSION > (1, 11):
  352. # Create file-based raster with options from scratch.
  353. compressed = GDALRaster({
  354. 'datatype': 1,
  355. 'driver': 'tif',
  356. 'name': rstfile.name,
  357. 'width': 40,
  358. 'height': 40,
  359. 'srid': 3086,
  360. 'origin': (500000, 400000),
  361. 'scale': (100, -100),
  362. 'skew': (0, 0),
  363. 'bands': [{
  364. 'data': range(40 ^ 2),
  365. 'nodata_value': 255,
  366. }],
  367. 'papsz_options': {
  368. 'compress': 'packbits',
  369. 'pixeltype': 'signedbyte',
  370. 'blockxsize': 23,
  371. 'blockysize': 23,
  372. }
  373. })
  374. # Check if options used on creation are stored in metadata.
  375. # Reopening the raster ensures that all metadata has been written
  376. # to the file.
  377. compressed = GDALRaster(compressed.name)
  378. self.assertEqual(compressed.metadata['IMAGE_STRUCTURE']['COMPRESSION'], 'PACKBITS',)
  379. self.assertEqual(compressed.bands[0].metadata['IMAGE_STRUCTURE']['PIXELTYPE'], 'SIGNEDBYTE')
  380. if GDAL_VERSION >= (2, 1):
  381. self.assertIn('Block=40x23', compressed.info)
  382. def test_raster_warp(self):
  383. # Create in memory raster
  384. source = GDALRaster({
  385. 'datatype': 1,
  386. 'driver': 'MEM',
  387. 'name': 'sourceraster',
  388. 'width': 4,
  389. 'height': 4,
  390. 'nr_of_bands': 1,
  391. 'srid': 3086,
  392. 'origin': (500000, 400000),
  393. 'scale': (100, -100),
  394. 'skew': (0, 0),
  395. 'bands': [{
  396. 'data': range(16),
  397. 'nodata_value': 255,
  398. }],
  399. })
  400. # Test altering the scale, width, and height of a raster
  401. data = {
  402. 'scale': [200, -200],
  403. 'width': 2,
  404. 'height': 2,
  405. }
  406. target = source.warp(data)
  407. self.assertEqual(target.width, data['width'])
  408. self.assertEqual(target.height, data['height'])
  409. self.assertEqual(target.scale, data['scale'])
  410. self.assertEqual(target.bands[0].datatype(), source.bands[0].datatype())
  411. self.assertEqual(target.name, 'sourceraster_copy.MEM')
  412. result = target.bands[0].data()
  413. if numpy:
  414. result = result.flatten().tolist()
  415. self.assertEqual(result, [5, 7, 13, 15])
  416. # Test altering the name and datatype (to float)
  417. data = {
  418. 'name': '/path/to/targetraster.tif',
  419. 'datatype': 6,
  420. }
  421. target = source.warp(data)
  422. self.assertEqual(target.bands[0].datatype(), 6)
  423. self.assertEqual(target.name, '/path/to/targetraster.tif')
  424. self.assertEqual(target.driver.name, 'MEM')
  425. result = target.bands[0].data()
  426. if numpy:
  427. result = result.flatten().tolist()
  428. self.assertEqual(
  429. result,
  430. [0.0, 1.0, 2.0, 3.0,
  431. 4.0, 5.0, 6.0, 7.0,
  432. 8.0, 9.0, 10.0, 11.0,
  433. 12.0, 13.0, 14.0, 15.0]
  434. )
  435. def test_raster_warp_nodata_zone(self):
  436. # Create in memory raster.
  437. source = GDALRaster({
  438. 'datatype': 1,
  439. 'driver': 'MEM',
  440. 'width': 4,
  441. 'height': 4,
  442. 'srid': 3086,
  443. 'origin': (500000, 400000),
  444. 'scale': (100, -100),
  445. 'skew': (0, 0),
  446. 'bands': [{
  447. 'data': range(16),
  448. 'nodata_value': 23,
  449. }],
  450. })
  451. # Warp raster onto a location that does not cover any pixels of the original.
  452. result = source.warp({'origin': (200000, 200000)}).bands[0].data()
  453. if numpy:
  454. result = result.flatten().tolist()
  455. # The result is an empty raster filled with the correct nodata value.
  456. self.assertEqual(result, [23] * 16)
  457. def test_raster_transform(self):
  458. # Prepare tempfile and nodata value
  459. rstfile = tempfile.NamedTemporaryFile(suffix='.tif')
  460. ndv = 99
  461. # Create in file based raster
  462. source = GDALRaster({
  463. 'datatype': 1,
  464. 'driver': 'tif',
  465. 'name': rstfile.name,
  466. 'width': 5,
  467. 'height': 5,
  468. 'nr_of_bands': 1,
  469. 'srid': 4326,
  470. 'origin': (-5, 5),
  471. 'scale': (2, -2),
  472. 'skew': (0, 0),
  473. 'bands': [{
  474. 'data': range(25),
  475. 'nodata_value': ndv,
  476. }],
  477. })
  478. # Transform raster into srid 4326.
  479. target = source.transform(3086)
  480. # Reload data from disk
  481. target = GDALRaster(target.name)
  482. self.assertEqual(target.srs.srid, 3086)
  483. self.assertEqual(target.width, 7)
  484. self.assertEqual(target.height, 7)
  485. self.assertEqual(target.bands[0].datatype(), source.bands[0].datatype())
  486. self.assertAlmostEqual(target.origin[0], 9124842.791079799)
  487. self.assertAlmostEqual(target.origin[1], 1589911.6476407414)
  488. self.assertAlmostEqual(target.scale[0], 223824.82664250192)
  489. self.assertAlmostEqual(target.scale[1], -223824.82664250192)
  490. self.assertEqual(target.skew, [0, 0])
  491. result = target.bands[0].data()
  492. if numpy:
  493. result = result.flatten().tolist()
  494. # The reprojection of a raster that spans over a large area
  495. # skews the data matrix and might introduce nodata values.
  496. self.assertEqual(
  497. result,
  498. [
  499. ndv, ndv, ndv, ndv, 4, ndv, ndv,
  500. ndv, ndv, 2, 3, 9, ndv, ndv,
  501. ndv, 1, 2, 8, 13, 19, ndv,
  502. 0, 6, 6, 12, 18, 18, 24,
  503. ndv, 10, 11, 16, 22, 23, ndv,
  504. ndv, ndv, 15, 21, 22, ndv, ndv,
  505. ndv, ndv, 20, ndv, ndv, ndv, ndv,
  506. ]
  507. )
  508. class GDALBandTests(SimpleTestCase):
  509. def setUp(self):
  510. self.rs_path = os.path.join(os.path.dirname(__file__), '../data/rasters/raster.tif')
  511. rs = GDALRaster(self.rs_path)
  512. self.band = rs.bands[0]
  513. def test_band_data(self):
  514. pam_file = self.rs_path + '.aux.xml'
  515. self.assertEqual(self.band.width, 163)
  516. self.assertEqual(self.band.height, 174)
  517. self.assertEqual(self.band.description, '')
  518. self.assertEqual(self.band.datatype(), 1)
  519. self.assertEqual(self.band.datatype(as_string=True), 'GDT_Byte')
  520. self.assertEqual(self.band.color_interp(), 1)
  521. self.assertEqual(self.band.color_interp(as_string=True), 'GCI_GrayIndex')
  522. self.assertEqual(self.band.nodata_value, 15)
  523. if numpy:
  524. data = self.band.data()
  525. assert_array = numpy.loadtxt(
  526. os.path.join(os.path.dirname(__file__), '../data/rasters/raster.numpy.txt')
  527. )
  528. numpy.testing.assert_equal(data, assert_array)
  529. self.assertEqual(data.shape, (self.band.height, self.band.width))
  530. try:
  531. smin, smax, smean, sstd = self.band.statistics(approximate=True)
  532. self.assertEqual(smin, 0)
  533. self.assertEqual(smax, 9)
  534. self.assertAlmostEqual(smean, 2.842331288343558)
  535. self.assertAlmostEqual(sstd, 2.3965567248965356)
  536. smin, smax, smean, sstd = self.band.statistics(approximate=False, refresh=True)
  537. self.assertEqual(smin, 0)
  538. self.assertEqual(smax, 9)
  539. self.assertAlmostEqual(smean, 2.828326634228898)
  540. self.assertAlmostEqual(sstd, 2.4260526986669095)
  541. self.assertEqual(self.band.min, 0)
  542. self.assertEqual(self.band.max, 9)
  543. self.assertAlmostEqual(self.band.mean, 2.828326634228898)
  544. self.assertAlmostEqual(self.band.std, 2.4260526986669095)
  545. # Statistics are persisted into PAM file on band close
  546. self.band = None
  547. self.assertTrue(os.path.isfile(pam_file))
  548. finally:
  549. # Close band and remove file if created
  550. self.band = None
  551. if os.path.isfile(pam_file):
  552. os.remove(pam_file)
  553. def test_read_mode_error(self):
  554. # Open raster in read mode
  555. rs = GDALRaster(self.rs_path, write=False)
  556. band = rs.bands[0]
  557. # Setting attributes in write mode raises exception in the _flush method
  558. with self.assertRaises(GDALException):
  559. setattr(band, 'nodata_value', 10)
  560. def test_band_data_setters(self):
  561. # Create in-memory raster and get band
  562. rsmem = GDALRaster({
  563. 'datatype': 1,
  564. 'driver': 'MEM',
  565. 'name': 'mem_rst',
  566. 'width': 10,
  567. 'height': 10,
  568. 'nr_of_bands': 1,
  569. 'srid': 4326,
  570. })
  571. bandmem = rsmem.bands[0]
  572. # Set nodata value
  573. bandmem.nodata_value = 99
  574. self.assertEqual(bandmem.nodata_value, 99)
  575. # Set data for entire dataset
  576. bandmem.data(range(100))
  577. if numpy:
  578. numpy.testing.assert_equal(bandmem.data(), numpy.arange(100).reshape(10, 10))
  579. else:
  580. self.assertEqual(bandmem.data(), list(range(100)))
  581. # Prepare data for setting values in subsequent tests
  582. block = list(range(100, 104))
  583. packed_block = struct.pack('<' + 'B B B B', *block)
  584. # Set data from list
  585. bandmem.data(block, (1, 1), (2, 2))
  586. result = bandmem.data(offset=(1, 1), size=(2, 2))
  587. if numpy:
  588. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  589. else:
  590. self.assertEqual(result, block)
  591. # Set data from packed block
  592. bandmem.data(packed_block, (1, 1), (2, 2))
  593. result = bandmem.data(offset=(1, 1), size=(2, 2))
  594. if numpy:
  595. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  596. else:
  597. self.assertEqual(result, block)
  598. # Set data from bytes
  599. bandmem.data(bytes(packed_block), (1, 1), (2, 2))
  600. result = bandmem.data(offset=(1, 1), size=(2, 2))
  601. if numpy:
  602. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  603. else:
  604. self.assertEqual(result, block)
  605. # Set data from bytearray
  606. bandmem.data(bytearray(packed_block), (1, 1), (2, 2))
  607. result = bandmem.data(offset=(1, 1), size=(2, 2))
  608. if numpy:
  609. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  610. else:
  611. self.assertEqual(result, block)
  612. # Set data from memoryview
  613. bandmem.data(memoryview(packed_block), (1, 1), (2, 2))
  614. result = bandmem.data(offset=(1, 1), size=(2, 2))
  615. if numpy:
  616. numpy.testing.assert_equal(result, numpy.array(block).reshape(2, 2))
  617. else:
  618. self.assertEqual(result, block)
  619. # Set data from numpy array
  620. if numpy:
  621. bandmem.data(numpy.array(block, dtype='int8').reshape(2, 2), (1, 1), (2, 2))
  622. numpy.testing.assert_equal(
  623. bandmem.data(offset=(1, 1), size=(2, 2)),
  624. numpy.array(block).reshape(2, 2)
  625. )
  626. # Test json input data
  627. rsmemjson = GDALRaster(JSON_RASTER)
  628. bandmemjson = rsmemjson.bands[0]
  629. if numpy:
  630. numpy.testing.assert_equal(
  631. bandmemjson.data(),
  632. numpy.array(range(25)).reshape(5, 5)
  633. )
  634. else:
  635. self.assertEqual(bandmemjson.data(), list(range(25)))
  636. def test_band_statistics_automatic_refresh(self):
  637. rsmem = GDALRaster({
  638. 'srid': 4326,
  639. 'width': 2,
  640. 'height': 2,
  641. 'bands': [{'data': [0] * 4, 'nodata_value': 99}],
  642. })
  643. band = rsmem.bands[0]
  644. # Populate statistics cache
  645. self.assertEqual(band.statistics(), (0, 0, 0, 0))
  646. # Change data
  647. band.data([1, 1, 0, 0])
  648. # Statistics are properly updated
  649. self.assertEqual(band.statistics(), (0.0, 1.0, 0.5, 0.5))
  650. # Change nodata_value
  651. band.nodata_value = 0
  652. # Statistics are properly updated
  653. self.assertEqual(band.statistics(), (1.0, 1.0, 1.0, 0.0))
  654. def test_band_statistics_empty_band(self):
  655. rsmem = GDALRaster({
  656. 'srid': 4326,
  657. 'width': 1,
  658. 'height': 1,
  659. 'bands': [{'data': [0], 'nodata_value': 0}],
  660. })
  661. self.assertEqual(rsmem.bands[0].statistics(), (None, None, None, None))
  662. def test_band_delete_nodata(self):
  663. rsmem = GDALRaster({
  664. 'srid': 4326,
  665. 'width': 1,
  666. 'height': 1,
  667. 'bands': [{'data': [0], 'nodata_value': 1}],
  668. })
  669. if GDAL_VERSION < (2, 1):
  670. msg = 'GDAL >= 2.1 required to delete nodata values.'
  671. with self.assertRaisesMessage(ValueError, msg):
  672. rsmem.bands[0].nodata_value = None
  673. else:
  674. rsmem.bands[0].nodata_value = None
  675. self.assertIsNone(rsmem.bands[0].nodata_value)
  676. def test_band_data_replication(self):
  677. band = GDALRaster({
  678. 'srid': 4326,
  679. 'width': 3,
  680. 'height': 3,
  681. 'bands': [{'data': range(10, 19), 'nodata_value': 0}],
  682. }).bands[0]
  683. # Variations for input (data, shape, expected result).
  684. combos = (
  685. ([1], (1, 1), [1] * 9),
  686. (range(3), (1, 3), [0, 0, 0, 1, 1, 1, 2, 2, 2]),
  687. (range(3), (3, 1), [0, 1, 2, 0, 1, 2, 0, 1, 2]),
  688. )
  689. for combo in combos:
  690. band.data(combo[0], shape=combo[1])
  691. if numpy:
  692. numpy.testing.assert_equal(band.data(), numpy.array(combo[2]).reshape(3, 3))
  693. else:
  694. self.assertEqual(band.data(), list(combo[2]))