Closed rooby closed 2 years ago
Hi @rooby
Could you share the tiff tags as read from geotiff.js or give us the output of tiffdump
? (Or share the original file if possible)
This sounds like a floating point rounding issue to me. Generally, it is not a good idea to compare equality of float values, but always compare with a slight relative +- epsilon.
@constantinius thanks for the quick reply. Here is the output of the tiffdump, and a zipped version of the example.tif is attached.
example.tif:
Magic: 0x4d4d <big-endian> Version: 0x2a <ClassicTIFF>
Directory 0: offset 8 (0x8) next 0 (0)
ImageWidth (256) SHORT (3) 1<279>
ImageLength (257) SHORT (3) 1<416>
BitsPerSample (258) SHORT (3) 1<32>
Compression (259) SHORT (3) 1<1>
Photometric (262) SHORT (3) 1<1>
SamplesPerPixel (277) SHORT (3) 1<1>
XResolution (282) RATIONAL (5) 1<1>
YResolution (283) RATIONAL (5) 1<1>
ResolutionUnit (296) SHORT (3) 1<1>
TileWidth (322) SHORT (3) 1<272>
TileLength (323) SHORT (3) 1<416>
TileOffsets (324) LONG (4) 2<433 453041>
TileByteCounts (325) LONG (4) 2<452608 452608>
SampleFormat (339) SHORT (3) 1<3>
34264 (0x85d8) DOUBLE (12) 16<0.00388926 0 0 135.033 0 -0.00378992 0 -23.5963 0 0 0 0 0 0 0 1>
34735 (0x87af) SHORT (3) 16<1 1 2 3 1024 0 1 2 1025 0 1 1 2048 0 1 4326>
42113 (0xa481) ASCII (2) 21<9.999999980506448E18\0>
In this case I think it is maybe an issue with the reading of the value as opposed to a calculation rounding issue. An epsilon value to make these two numbers match would is very large (19493552128).
@rooby I can't reproduce this issue. I wrote this small script to get the nodata value from the image:
const tiff = await fromUrl('data/example.tif');
const image = await tiff.getImage();
console.log(image.getGDALNoData());
Which gives me this output:
9999999980506448000
This is in the Browser (Chrome).
Which is the same as the exponential notation 9.999999980506448E18
. So I now believe that you retrieve the right value from the TIFF, but when it is displayed, the number is somehow rounded.
Can you share the context in which the value is used?
@constantinius Oh sorry, I should have given more context initially.
I'm in an Angular app, fetching the data from a WCS GetCoverage request into a Blob and then using fromBlob()
.
After some more investigation, it seems changing my request to return an array buffer and using fromArrayBuffer doesn't make any difference. When I request from the WCS directly in my browser and downloading the file gives me a file with the correct number, but requesting from the WCS in Angular and saving the blob as a file gives me a file with the incorrect number.
Have you checked the nodata value from the WCS response with GDAL?
I'm very certain, that geotiff.js is not the culprit on this case, but a wrong nodata value is included in the response.
I've run into a similar issue as well with other Float32 GeoTIFFs. I'm also not sure if it's an issue with geotiff.js or the program that created the GeoTIFF file (not sure it's GDAL in my case).
Here's a Float32 GeoTIFF of Sea Ice Concentration (originally via https://github.com/GeoTIFF/georaster-layer-for-leaflet/issues/47#issuecomment-716449294) https://github.com/GeoTIFF/test-data/blob/main/files/nt_20201024_f18_nrt_s.tif
image.fileDirectory.GDAL_NODATA
evaluates to '-3.39999999999999996e+38\x00'
42113 (0xa481) ASCII (2) 25<-3.39999999999999996e+38 ...>
NoData Value=-3.39999995214436425e+38
-3.4028234663852886e+38
1 11111110 11111111111111111111111
(I think this is the lowest possible float32 value)0xff7fffff
eval("-3.39999999999999996e+38")
returns: -3.4e+38 (seems like the mantissa exceeds that which modern browsers can handle although being less than the maximum 23 numbers, I'm not sure why that's the case. I don't understand floating point numbers that well)Math.fround(-3.4e+38): -3.3999999521443642e+38
evaluates to -3.3999999521443642e+38
A couple issues come to mind:
-3.4e+38
which people commonly say is the lowest float32 number (but to be all technical, it's actually -3.4028234663852886e+38
) Hope this helps a little.
@rooby Is there an update on this? Can you confirm that the WCS result has the same nodata value set as the original image?
@constantinius
I have a workaround for my issue for now.
Still doing a bit more investigation to confirm exactly what is the source of the inaccurate number, however I don't think the issue is with geotiff.js.
Happy to close this now if you like, otherwise I'll close it off when I've finished the full investigation.
Thanks for the info @DanielJDufour
I'll close it now. Please re-open if the issue remains.
I have a similar issue with CMEMS sea_ice_fraction scaled NetCDF. It contains non-sea points with descaled value 1.27999997138977, that I have to override as nodata.
gdal_translate -a_nodata 1.27999997138977
stores it as 1.27999997138977051 string. QGIS shows these points correctly as nodata. JS parses this string as 1.2799999713897705, so strict equality in JS doesn't match the points.
My current workaround:
function maskData(data, nodata = undefined) {
if (!nodata) {
return data;
}
const maskedData = new data.constructor(Array.from(data).map(value => {
return Math.abs(value - nodata) > Number.EPSILON * 2 ? value : NaN;
}));
return maskedData;
}
It seems that the issue is with specifically Float32 GeoTIFF bands, because geotiff.js parses GDAL_NODATA into a Float64 number.
How about applying Math.fround for Float32 bands here? https://github.com/geotiffjs/geotiff.js/blob/ae4eb19864879c894ef89e6499d9a08c18b0248f/src/geotiffimage.js#L801
@zakjan your issue seems different to the one I was reporting.
My issue is not one relating to inaccuracies in floating point numbers, and the problem occurs before getGDALNoData() ever gets called, since the number parsed from the file header into the fileDirectory.GDAL_NODATA
is already incorrect and was either already incorrect in the source file or was incorrectly parsed from the header.
Probably best to create a separate issue for your findings.
I think my issue is similar to @DanielJDufour's issue. As fileDirectory.GDAL_NODATA is stored as ASCII text, is it possible that it is a GDAL bug (or a "feature")?
Generally the GDAL_NODATA metadata is working for me but I have some files that get an incorrect value.
For example with this file, when I run gdalinfo I get a nodata value of 9.99999998050644787e+18 but geotiff.js says 1.0E19 (gdalinfo is correct).
I'm not sure what the difference is with how gdalinfo determines the value, so I'm not sure if it's possible that the nodata metadata is incorrect but gdalinfo gets its info from somewhere else, or if geotiff.js is not reading the value correctly.