You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We have a simple unit test, based on single pixel images, to test an algorithm. With Geotrellis 1.0.0, this test fails because a test file is decoded incorrectly.
The code snippet below exposes the issue, it saves a single pixel geotiff, with a pixel value of 145. When it is decoded again, the value in the tile is 389. The issue seems to occur during decoding, because according to GDAL, the only value in the saved geotiff is 145, which is correct.
test("Geotiffreading"){
val extent = Extent(0,0,100,100)
val tile = IntArrayTile(Array(145 ), 1, 1)
val raster = Raster(tile, extent)
val resultTiff = GeoTiff(raster, CRS.fromName("EPSG:4326"))
resultTiff.write("test.tif")
val singlebandGeoTiff2: SinglebandGeoTiff = GeoTiffReader.readSingleband("test.tif")
tile.asciiDraw() shouldEqual singlebandGeoTiff2.tile.asciiDraw()
}
I found the issue. It's specific for geotiffs that consist of less than 4 bytes. This method is wrong in this case:
geotrellis.raster.io.geotiff.util.ByteReaderExtensions.ByteReaderUtilities#getSignedByteArray
(There are a number of similar methods there that also contain the same issue.)
This line:
val bb = ByteBuffer.allocate(4).order(byteReader.order).putInt(0, offset.toInt)
stores the offset in a new bytebuffer, not sure why, and the code never reads from the byteReader containing the actual pixels.
I'm not a Scala expert, but a fix could look like this:
if (length <= 4) {
val arr = Array.ofDim[Byte](len)
val bb = ByteBuffer.allocate(4).order(byteReader.order).putInt(0, offset.toInt)
val oldPos = byteReader.position
byteReader.position(offset)
cfor(0)(_ < len, _ + 1) { i =>
arr(i) = byteReader.get
}
byteReader.position(oldPos)
arr
}
@jdries this has to do with a weird property of Tiffs - when the length is less than or equal to 4 bytes, the value is just stored in the offset (and the offset doesn't actually point to anything). So the line
val bb = ByteBuffer.allocate(4).order(byteReader.order).putInt(0, offset.toInt)
Puts the offset into a new byte buffer, and then that byte buffer is read in.
Activity
jdries commentedon Jan 31, 2017
I found the issue. It's specific for geotiffs that consist of less than 4 bytes. This method is wrong in this case:
geotrellis.raster.io.geotiff.util.ByteReaderExtensions.ByteReaderUtilities#getSignedByteArray
(There are a number of similar methods there that also contain the same issue.)
This line:
val bb = ByteBuffer.allocate(4).order(byteReader.order).putInt(0, offset.toInt)
stores the offset in a new bytebuffer, not sure why, and the code never reads from the byteReader containing the actual pixels.
I'm not a Scala expert, but a fix could look like this:
lossyrob commentedon Mar 12, 2017
Apologies, I didn't see this when it was incoming. Thanks for the bug report, I'll look into it.
lossyrob commentedon Mar 13, 2017
This is the method in question https://github.com/locationtech/geotrellis/blob/master/raster/src/main/scala/geotrellis/raster/io/geotiff/util/ByteReaderExtensions.scala#L176
lossyrob commentedon Mar 13, 2017
@jdries this has to do with a weird property of Tiffs - when the length is less than or equal to 4 bytes, the value is just stored in the offset (and the offset doesn't actually point to anything). So the line
Puts the offset into a new byte buffer, and then that byte buffer is read in.
jdries commentedon Mar 14, 2017