locationtech / geotrellis

GeoTrellis is a geographic data processing engine for high performance applications.
http://geotrellis.io
Other
1.34k stars 361 forks source link

GeoTiffLayerReader fails with GeoAttrsError #3011

Open atararaksin opened 5 years ago

atararaksin commented 5 years ago

I'm using GeoTiffLayerReader to read tiles from unstructured COGs like this:

val attributeStore = FileIMGeoTiffAttributeStore("test", new URI(uri))
val reader = new FileGeoTiffLayerReader(attributeStore, ZoomedLayoutScheme(WebMercator))
val raster = reader.read[MultibandTile](LayerId("test", zoom))(x, y)

I believe there's an issue when one the source tiff's boundaries lies close to one of the target tile's boundaries, but shifted by a small delta. If this delta is smaller than the size of the cell (pixel), we end up with the following error:

geotrellis.raster.GeoAttrsError: Grid bounds do not intersect: GridBounds(0,0,512,512) crop GridBounds(192,0,256,-1)
    at geotrellis.raster.crop.MultibandTileCropMethods.cropBands(MultibandTileCropMethods.scala:35)
    at geotrellis.raster.crop.MultibandTileCropMethods.cropBands$(MultibandTileCropMethods.scala:34)
    at geotrellis.raster.package$withMultibandTileMethods.cropBands(package.scala:83)
    at geotrellis.raster.crop.MultibandTileCropMethods.crop(MultibandTileCropMethods.scala:72)
    at geotrellis.raster.crop.MultibandTileCropMethods.crop$(MultibandTileCropMethods.scala:71)
    at geotrellis.raster.package$withMultibandTileMethods.crop(package.scala:83)
    at geotrellis.raster.package$withMultibandTileMethods.crop(package.scala:83)
    at geotrellis.raster.crop.RasterCropMethods.crop(RasterCropMethods.scala:37)
    at geotrellis.raster.io.geotiff.MultibandGeoTiff.crop(MultibandGeoTiff.scala:54)
    at geotrellis.raster.io.geotiff.MultibandGeoTiff.crop(MultibandGeoTiff.scala:27)
    at geotrellis.spark.io.hadoop.geotiff.GeoTiffLayerReader.$anonfun$read$3(GeoTiffLayerReader.scala:87)
    at scala.Option.map(Option.scala:163)
    at geotrellis.spark.io.hadoop.geotiff.GeoTiffLayerReader.$anonfun$read$2(GeoTiffLayerReader.scala:84)
    at cats.effect.internals.IORunLoop$.cats$effect$internals$IORunLoop$$loop(IORunLoop.scala:87)
    at cats.effect.internals.IORunLoop$RestartCallback.signal(IORunLoop.scala:351)
    at cats.effect.internals.IORunLoop$RestartCallback.apply(IORunLoop.scala:372)
    at cats.effect.internals.IORunLoop$RestartCallback.apply(IORunLoop.scala:312)
    at cats.effect.internals.IOShift$Tick.run(IOShift.scala:36)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

This is a sample tif which is prone to this problem: area-22000.zip. An attempt to read (x = 279187, y = 495127, z = 20) in WebMercator fails.

atararaksin commented 5 years ago

I'll explain what happens using a concrete example. The source tiff's raster extent is GridExtent(Extent(-9367510.69040492, 1114145.5271281332, -9367204.345135182, 1114451.8723978705),0.597164268494669,0.5971642684937613). We are trying to read a tile (279187, 495127) on zoom 20. The intersection of the tiff's extent and the target tile extent is Extent(-9367396.034862492, 1114451.8723978698, -9367357.81634835, 1114451.8723978705). GeoTiffLayerReader crops the source tiff to this intersection: https://github.com/locationtech/geotrellis/blob/04ecc722803d55fc101ca6a0ca2387308e7e5417/spark/src/main/scala/geotrellis/spark/store/hadoop/geotiff/GeoTiffLayerReader.scala#L87 which further leads us here: https://github.com/locationtech/geotrellis/blob/04ecc722803d55fc101ca6a0ca2387308e7e5417/raster/src/main/scala/geotrellis/raster/crop/RasterCropMethods.scala#L35 The grid bounds of the intersection get calculated as GridBounds(192,0,256,-1). Finally we get geotrellis.raster.GeoAttrsError: Grid bounds do not intersect: GridBounds(0,0,512,512) crop GridBounds(192,0,256,-1).

When reading from a single tiff, it is not too bad, because we would still fail later on with smth like java.lang.UnsupportedOperationException: empty.reduceLeft. But when reading from multiple tiffs we are loosing data from any other tiffs that intersect with the target tile, in case there are any.