Repository navigation
Add Support for the GridCoverage2D Type - #1444
jamesmcclain wants to merge 17 commits into
Conversation
91827cf to
f6be292
Compare
|
I would like to talk through the NoData handling. The way this reads is that each band in So then we have a problem if we try to map such a tile to a raster, which NoData value to pick? The way We've had no end of trouble with incorrect interpretation of NoData values, so I don't think we should fudge it and pick the first one as What are your thoughts on this? |
| } | ||
| else { | ||
| val noData = noDataValue.get | ||
| typeEnum match { |
There was a problem hiding this comment.
What if the noDataValue matches our convention? Can we ever recover tile with DoubleConstantNoDataCellType for instance?
Sorry for not responding earlier. I am now working on a task that will need this code, so I will have a chance to do some more work with this. |
I don't know how common different permutations are, but I agree that the closest Geotrellis analog is an array of tiles. |
f6be292 to
c56ac33
Compare
It is no longer attempted to create a single MultibandTile to represent all layers of a GridCoverage2D, now a sequence of Tiles is created. Also, the converter now emits *ConstantNoDataCellType when it can.
It is no longer attempted to create a single MultibandTile to represent all layers of a GridCoverage2D, now a sequence of Tiles is created. Also, the converter now emits *ConstantNoDataCellType when it can.
c56ac33 to
79fddf2
Compare
Since all bands of Geotrellis MultibandTiles must all have the same cellType and NODATA value, that particular type is not a good candidate for representing the data in a GeoTools GridCoverage2D. The best analog of a GeoTools GridCoverage2D is an Array of Geotrellis Rasters. Also, the converter now emits *ConstantNoDataCellType when it can.
79fddf2 to
37c7806
Compare
Since all bands of Geotrellis MultibandTiles must all have the same cellType and NODATA value, that particular type is not a good candidate for representing the data in a GeoTools GridCoverage2D. The best analog of a GeoTools GridCoverage2D is an Array of Geotrellis Rasters. Also, the converter now emits *ConstantNoDataCellType when it can.
The Raster[Tile] to GridCoverage2D transformation provides best-guess ColorModels to the created GridCoverage2D objects. The Raster[MultibandTile] to GridCoverage2D transformation does not attempt to provide ColorModel information. NODATA metadata is now correct in both the Tile case and MultibandTile case.
Since all bands of Geotrellis MultibandTiles must all have the same cellType and NODATA value, that particular type is not a good candidate for representing the data in a GeoTools GridCoverage2D. The best analog of a GeoTools GridCoverage2D is an Array of Geotrellis Rasters. Also, the converter now emits *ConstantNoDataCellType when it can.
The Raster[Tile] to GridCoverage2D transformation provides best-guess ColorModels to the created GridCoverage2D objects. The Raster[MultibandTile] to GridCoverage2D transformation does not attempt to provide ColorModel information. NODATA metadata is now correct in both the Tile case and MultibandTile case.
37c7806 to
a05483e
Compare
Since all bands of Geotrellis MultibandTiles must all have the same cellType and NODATA value, that particular type is not a good candidate for representing the data in a GeoTools GridCoverage2D. The best analog of a GeoTools GridCoverage2D is an Array of Geotrellis Rasters. Also, the converter now emits *ConstantNoDataCellType when it can.
The Raster[Tile] to GridCoverage2D transformation provides best-guess ColorModels to the created GridCoverage2D objects. The Raster[MultibandTile] to GridCoverage2D transformation does not attempt to provide ColorModel information. NODATA metadata is now correct in both the Tile case and MultibandTile case.
| * @param gridCoverage The GeoTools GridCoverage2D object | ||
| * @return A sequence of rasters of singleband Geotrellis [[Tile]]s | ||
| */ | ||
| def apply(gridCoverage: GridCoverage2D): Seq[Raster[Tile]] = { |
There was a problem hiding this comment.
Is there a reason why this should be Seq[Raster[Tile]] and not a Raster[MultibandTile]? Is it because the cell types can be different?
There was a problem hiding this comment.
Yeah, that is the reason.
a05483e to
3df1f0e
Compare
Is 274 a special number? |
According to the documentation[1], DataBuffer.TYPE_BYTE corresponds to "unsigned byte" not "byte". 1. https://docs.oracle.com/javase/7/docs/api/java/awt/image/DataBuffer.html
Since all bands of Geotrellis MultibandTiles must all have the same cellType and NODATA value, that particular type is not a good candidate for representing the data in a GeoTools GridCoverage2D. The best analog of a GeoTools GridCoverage2D is an Array of Geotrellis Rasters. Also, the converter now emits *ConstantNoDataCellType when it can.
The Raster[Tile] to GridCoverage2D transformation provides best-guess ColorModels to the created GridCoverage2D objects. The Raster[MultibandTile] to GridCoverage2D transformation does not attempt to provide ColorModel information. NODATA metadata is now correct in both the Tile case and MultibandTile case.
3df1f0e to
dfa9d59
Compare
It's just a good (large) number |
|
The GridCoverage2DTile does not handle NoData correctly. For instance, getDouble does not check to see if the pixel value corresponds to the "NoData" value saved off with the GridCoverage2D, and translate that to NODATA as the rest of the tiles do. Also, there was a technique I figured would be more performant, which also solves the NoData issue, by using the arrays of DataBuffers directly; spiking it shows it to be significantly faster: class GridCoverage2DConvertersBenchmark extends Benchmarks with ConsoleReport {
import GridCoverage2DConvertersBenchmark._
val paths =
List(
BenchmarkUtil.dataPath("geotiff/SBN_inc_percap.tif"),
BenchmarkUtil.dataPath("geotiff/elevation-comp.tif"),
BenchmarkUtil.dataPath("geotiff/r-nir-wm-clipped.tif")
)
for(path <- paths) {
benchmark(s"Converting a GeoTools GridCoverage2D to a GeoTrellis type: ${new java.io.File(path).getName}") {
run("Foreach over GeoToolsTile") {
new Benchmark {
val p = path
var gridCoverage: GridCoverage2D = _
override def setUp(): Unit = {
gridCoverage = getImage(p)
}
def run(): Unit = {
// The previous way
val Raster(tile, extent) = GridCoverage2DToRaster(gridCoverage).head
var s = 0
tile.foreach { z => if(isData(z)) s += z }
}
}
}
run("Foreach over GeoToolsTile Refactor") {
new Benchmark {
val p = path
var gridCoverage: GridCoverage2D = _
override def setUp(): Unit = {
gridCoverage = getImage(p)
}
def run(): Unit = {
// The refactored way
val Raster(tile, extent) = gridCoverage.toRaster(0)
var s = 0
tile.foreach { z => if(isData(z)) s += z }
}
}
}
}
}
}Running benchmarks for Converting a GeoTools GridCoverage2D to a GeoTrellis type: SBN_inc_percap.tif...
1 of 2: Foreach over GeoToolsTile 54130140.55 ns; σ=57058178.07221 ns @ 10 trials
2 of 2: Foreach over GeoToolsTile 4550679.99 ns; σ=4796837.89136 ns @ 10 trials
Name ms linear runtime
Foreach over GeoToolsTile 54.13 ===========================
Foreach over GeoToolsTile Refa 4.55 ==
Running benchmarks for Converting a GeoTools GridCoverage2D to a GeoTrellis type: elevation-comp.tif...
1 of 2: Foreach over GeoToolsTile 36791265.45 ns; σ=38781398.93927 ns @ 10 trials
2 of 2: Foreach over GeoToolsTile 3368608.23 ns; σ=3550824.85014 ns @ 10 trials
Name ms linear runtime
Foreach over GeoToolsTile 36.79 ============================
Foreach over GeoToolsTile Refa 3.37 ==
Running benchmarks for Converting a GeoTools GridCoverage2D to a GeoTrellis type: r-nir-wm-clipped.tif...
1 of 2: Foreach over GeoToolsTile 2954836000.00 ns; σ=3114670624.08710 ns @ 10 trials
2 of 2: Foreach over GeoToolsTile 974585600.00 ns; σ=1027303423.60060 ns @ 10 trials
Name s linear runtime
Foreach over GeoToolsTile 2.95 =========================
Foreach over GeoToolsTile Refa 0.97 ========These are big changes to this PR, so it would be best to freeze work on this until my changes take place. Let me know if there's any significant difference to the branch I pulled yesterday vs this branch (I see a bunch of commits after my comment, but can't tell if those are actually newly pushed commits or old commits that I have). |
|
It seems like NoData is handled in GeoTools differently then the GridSampleModel method: from what I have read, it is recommended to use the http://docs.geotools.org/stable/userguide/tutorial/raster/jaiext.html I'm including using this method to get/set NoData as part of the changes, so no action is necessary as of now. Also, this is a NoData per GridCoverage2D, which would allow us to view GridCoverage2D as a |
|
Superseded by #1502 |

In this pull request, support is added for converting the GeoTools
GridCoverage2Dto a GeotrellisRasterand vice-versa.GridCoverage2DtoRasterConfirm Kryo serializability ofGridCoverage2DMultibandTileandGridCoverage2DTileGridCoverage2Dis not Kryo Serializable (See experimental diff below)RastertoGridCoverage2DGridCoverage2DtoRasterRastertoGridCoverage2DPoints of Interest
Rasters to GeoToolsGridCoverage2Ds, the NODATA value must be non-negative. This is as a result of what seems to be a bug in Java'sSamleDimensionimplementation.MultibandTileis not supported. I suggest converting the individual bands to separateGridCoverage2Dobjects.To Do (2)
To Do (1)
More
The following diff can be applied to allow experimentation with
GridCoverage2Dvis-a-vis Spark