diff --git a/.gitignore b/.gitignore index 92980a7f5b..98d4cb9c96 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ metastore_db/ spark/src/test/resources/vlm/*catalog* + +# vscode +.vscode diff --git a/CHANGELOG.md b/CHANGELOG.md index a2aa06f348..5e4de3b72b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Fix `Encoder[GeometryCollection]` including subclasses of GeometryCollection twice in the json (MultiPolygon, Multipoint,MultiLinestring) [#3167](https://github.com/locationtech/geotrellis/issues/3167) +-Fix `LayoutTileSource` buffer should only be 1/2 a cellsize to avoid going out of bounds and creating `NODATA` values [#3302](https://github.com/locationtech/geotrellis/pull/3302) - Remove unused allocation from CroppedTile [#3297](https://github.com/locationtech/geotrellis/pull/3297) ## [3.5.0] - 2020-08-18 diff --git a/layer/src/test/resources/vlm/nodata-row.tif b/layer/src/test/resources/vlm/nodata-row.tif new file mode 100644 index 0000000000..7e5f444753 Binary files /dev/null and b/layer/src/test/resources/vlm/nodata-row.tif differ diff --git a/layer/src/test/scala/geotrellis/layer/LayoutTileSourceSpec.scala b/layer/src/test/scala/geotrellis/layer/LayoutTileSourceSpec.scala index 03ed5568d6..053d672445 100644 --- a/layer/src/test/scala/geotrellis/layer/LayoutTileSourceSpec.scala +++ b/layer/src/test/scala/geotrellis/layer/LayoutTileSourceSpec.scala @@ -244,5 +244,37 @@ class LayoutTileSourceSpec extends AnyFunSpec with RasterMatchers { t.dimensions shouldBe Dimensions(256, 256) } } + + it("should read reprojected and tiled to layout source without nodata stripe // see issue-3299") { + val path = Resource.path("vlm/nodata-row.tif") + val testZoomLevel = 19 + val rs = GeoTiffRasterSource(path) + + val scheme = ZoomedLayoutScheme(WebMercator) + val layout = scheme.levelForZoom(testZoomLevel).layout + + val extent: Extent = rs.extent.reproject(rs.crs, WebMercator) + val SpatialKey(x, y) = layout.mapTransform(extent.center) + + val neighborhood = for { + col <- ((x - 2) until (x + 2)).toList + row <- ((y - 2) until (y + 2)).toList + } yield (col, row) + + def checkExtent(col: Int, row: Int, rs: RasterSource): Unit = { + val tile = rs + .tileToLayout(layout) + .read(SpatialKey(col, row), List(0)) + .get + .band(0) + val arr = tile.toArray + val ones = tile.mapIfSet(i => 1).toArray() + ones.sum shouldBe(arr.size) + } + + neighborhood map { + case (x, y) => checkExtent(x, y, rs) + } + } } } diff --git a/raster/src/main/scala/geotrellis/raster/geotiff/GeoTiffResampleRasterSource.scala b/raster/src/main/scala/geotrellis/raster/geotiff/GeoTiffResampleRasterSource.scala index ccf613b8e8..0639d93899 100644 --- a/raster/src/main/scala/geotrellis/raster/geotiff/GeoTiffResampleRasterSource.scala +++ b/raster/src/main/scala/geotrellis/raster/geotiff/GeoTiffResampleRasterSource.scala @@ -111,9 +111,10 @@ class GeoTiffResampleRasterSource( targetPixelBounds <- queryPixelBounds.intersection(this.dimensions) } yield { val targetExtent = gridExtent.extentFor(targetPixelBounds) - // buffer the targetExtent to read a buffered area from the source tiff + // Buffer the targetExtent to read a buffered area from the source tiff // so the resample would behave properly on borders - val bufferedTargetExtent = targetExtent.buffer(cellSize.width, cellSize.height) + // Buffer by half of CS to avoid resampling out of bounds + val bufferedTargetExtent = targetExtent.buffer(cellSize.width / 2, cellSize.height / 2) val sourcePixelBounds = closestTiffOverview.rasterExtent.gridBoundsFor(bufferedTargetExtent) val targetRasterExtent = RasterExtent(targetExtent, targetPixelBounds.width.toInt, targetPixelBounds.height.toInt) (sourcePixelBounds, targetRasterExtent)