RasterSource
RasterSource
interface is fundamental raster input interface in GeoTrellis.
It abstracts over file format, access schema, and reader implementation.
As of GeoTrellis 3.0 implementations exist for:
GeoTiffRasterSource
: GeoTrellis GeoTiff ReaderGeoTrellisRasterSource
: GeoTrellis Indexed LayersGDALRasterSource
: GDAL throughgdal-warp-bindings
Metadata
RasterSource
s are lazy, performing minimum I/O on instantiation. Raster metadata is read on demand and implementations are expected to cache it for repeated access through available fields.
import geotrellis.raster.geotiff.GeoTiffRasterSource
val source = GeoTiffRasterSource("https://geotrellis-demo.s3.amazonaws.com/data/aspect.tif")
// source: GeoTiffRasterSource = geotrellis.raster.geotiff.GeoTiffRasterSource@1ff59a11
source.extent
// res0: geotrellis.vector.Extent = Extent(
// 630000.0,
// 215000.0,
// 645000.0,
// 228500.0
// )
source.crs
// res1: geotrellis.proj4.CRS = lcc-CS
source.cellType
// res2: geotrellis.raster.package.CellType = FloatUserDefinedNoDataCellType(
// -9999.0F
// )
source.dimensions
// res3: geotrellis.raster.Dimensions[Long] = Dimensions(1500L, 1350L)
source.metadata.attributes
// res4: Map[String, String] = Map("AREA_OR_POINT" -> "AREA")
Windows
GeoTrellis is designed around the assumption that rasters may be large and need to be split for parallel processed. Therefore the primary way to read with RasterSource
is through windows into the source raster. When the file format supports it, like Cloud Optimized GeoTiff, these reads can be very efficient.
- Result of a window read is an
Option[Raster[MultibandTile]]
- When the query extent does not intersect the source raster
None
will be returned - Reads will be snapped to the pixel grid of the source raster, that may differ from the query extent
- Reads are not buffered when query extent partially covers the source raster
import geotrellis.raster._
val bbox = source.extent.buffer(-source.extent.width/4, -source.extent.height/4)
// bbox: geotrellis.vector.Extent = Extent(
// 633750.0,
// 218375.0,
// 641250.0,
// 225125.0
// )
val centerWindow = source.read(bbox)
// centerWindow: Option[Raster[MultibandTile]] = Some(
// Raster(
// geotrellis.raster.ArrayMultibandTile@62c48c77,
// Extent(633750.0, 218370.0, 641250.0, 225130.0)
// )
// )
centerWindow.get.dimensions
// res5: Dimensions[Int] = Dimensions(750, 676)
val leftCorner = source.read(GridBounds(0L, 0L, 255L, 255L))
// leftCorner: Option[Raster[MultibandTile]] = Some(
// Raster(
// geotrellis.raster.ArrayMultibandTile@1e78bde4,
// Extent(630000.0, 225940.0, 632560.0, 228500.0)
// )
// )
leftCorner.get.dimensions
// res6: Dimensions[Int] = Dimensions(256, 256)
Additional read methods readExtents
and readBounds
are provided for reading multiple windows at once. This allows the RasterSource
implementation to perform optimizations for the read sequence. For instance GeoTiffRasterSource
ensures that each GeoTiff segment is read only once, even if it contributes to multiple windows. This optimization is not guaranteed for all implementations of RasterSource
.
Views
RasterSource
provides a way to perform a lazy resampling and reprojection.
These operations produce a new RasterSource
instance which is a lazy view of the source raster.
Both metadata and results of read function are consistent with this the view.
This feature is similar to GDAL VRT, indeed GDALRasterSource
is backed by GDAL VRT.
import geotrellis.proj4._
import geotrellis.vector._
import geotrellis.raster.resample.{ NearestNeighbor }
val wgs84Source = source.reproject(LatLng, method = NearestNeighbor)
// wgs84Source: RasterSource = geotrellis.raster.geotiff.GeoTiffReprojectRasterSource@23e609ac
wgs84Source.extent
// res7: Extent = Extent(
// -78.7746204948163,
// 35.687534170395224,
// -78.60827512493357,
// 35.80960938344992
// )
val w1 = wgs84Source.read(Extent(-78.68, 35.76, -78.65, 35.78))
// w1: Option[Raster[MultibandTile]] = Some(
// Raster(
// geotrellis.raster.ArrayMultibandTile@2da5fc4d,
// Extent(
// -78.68004798889035,
// 35.759920477633685,
// -78.64998926808794,
// 35.78006186538224
// )
// )
// )
w1.get.dimensions
// res8: Dimensions[Int] = Dimensions(294, 197)
Often the view may need to be matched to a specific resolution and pixel offset. This is a requirement to be able to combine two rasters in a map algebra operation.
val minuteGrid = GridExtent(Extent(-180, -90, 180, 90), cols = 360*60, rows = 180*60)
// minuteGrid: GridExtent[Int] = GridExtent(Extent(-180.0, -90.0, 180.0, 90.0),CellSize(0.016666666666666666,0.016666666666666666),21600x10800)
val w2 = source.reprojectToGrid(LatLng, minuteGrid.toGridType[Long], NearestNeighbor)
// w2: RasterSource = geotrellis.raster.geotiff.GeoTiffReprojectRasterSource@6b446aba
w2.dimensions
// res9: Dimensions[Long] = Dimensions(11L, 8L)
Read Tiles
When a raster needs to be read by tile keys from a known layout LayoutTileSource
can be used.
LayoutTileSource
imposes tile layout of LayoutDefinition
(see layer model) on an instance of RasterSource
.
Lets read our raster as a web tiler would, using tile grid for TMS zoom level 14:
import geotrellis.layer._
val scheme = ZoomedLayoutScheme(WebMercator, 256)
// scheme: ZoomedLayoutScheme = geotrellis.layer.ZoomedLayoutScheme@30886dab
val level = scheme.levelForZoom(14)
// level: LayoutLevel = LayoutLevel(
// 14,
// LayoutDefinition(
// Extent(
// -2.0037508342789244E7,
// -2.0037508342789244E7,
// 2.0037508342789244E7,
// 2.0037508342789244E7
// ),
// TileLayout(16384, 16384, 256, 256)
// )
// )
val tileSource = LayoutTileSource.spatial(
source = source.reprojectToGrid(WebMercator, level.layout),
layout = level.layout)
// tileSource: LayoutTileSource[SpatialKey] = geotrellis.layer.LayoutTileSource@23b0a97f
tileSource.keys.take(3)
// res10: Set[SpatialKey] = Set(
// SpatialKey(4609, 6448),
// SpatialKey(4609, 6444),
// SpatialKey(4607, 6449)
// )
tileSource.read(SpatialKey(4609, 6448))
// res11: Option[MultibandTile] = Some(
// geotrellis.raster.ArrayMultibandTile@21f56ed0
// )