1.2.0
API Changes
geotrellis.raster
- Deprecation:
GridBounds.size
in favor ofGridBounds.sizeLong
. - Deprecation:
GridBounds.coords
in favor ofGridBounds.coordsIter
. - New:
GridBounds.offset
andGridBounds.buffer
for creating a modifiedGridBounds
from an existing one. - New:
ColorRamps.greyscale: Int => ColorRamp
, which will generate a ramp when given some number of stops. - New:
ConstantTile.fromBytes
to create any type ofConstantTile
from anArray[Byte]
. - New:
Tile.rotate90: Int => Tile
,Tile.flipVertical: Tile
andTile.flipHorizontal: Tile
.
- Deprecation:
geotrellis.vector
- New:
Geometry.isEmpty: Boolean
. This incurs much less overhead than previous ways of determining emptiness. - New:
Line.head
andLine.last
for efficiently grabbing the first or lastPoint
in theLine
.
- New:
geotrellis.spark
- Deprecation: The
LayerUpdater
trait hierarchy. UseLayerWriter.update
orLayerWriter.overwrite
instead. - Deprecation: Every cache provided by
geotrellis.spark.util.cache
. These will be removed in favor of a pluggable cache in 2.0. - New:
SpatialKey.extent: LayoutDefinition => Extent
- New:
ValueReader.attributeStore: AttributeStore
- New:
TileLayerRDD.toSpatialReduce: ((V, V) => V) => TileLayerRDD[SpatialKey]
for smarter folding of 3D tile layers into 2D tile layers. - The often-used
apply
method overloads inMapKeyTransform
have been given more descriptive aliases.
- Deprecation: The
geotrellis.vectortile
(experimental)- New:
VectorTile.toGeoJson
andVectorTile.toIterable
. - Library simplified by assuming the codec backend will always be Protobuf.
- New:
New Features
Rasterizing Geometry
Layers
Finally, the full marriage of the vector
, raster
, and spark
packages! You can now transform an RDD[Geometry]
into a writable GeoTrellis layer of (SpatialKey, Tile)
!
val geoms: RDD[Geometry] = ...
val celltype: CellType = ...
val layout: LayoutDefinition = ...
val value: Double = ... /* Value to fill the intersecting pixels with */
val layer: RDD[(SpatialKey, Tile)] with Metadata[LayoutDefinition] =
geoms.rasterize(value, celltype, layout)
Clipping Geometry
Layers to a Grid
In a similar vein to the above, you can now transform an arbitrarily
large collection of Geometries into a proper GeoTrellis layer, where the
sections of each Geometry are clipped to fit inside their enclosing
Extents.
Here we can see a large Line
being clipped into nine sublines. It's
one method call:
import geotrellis.spark._
val layout: LayoutDefinition = ... /* The definition of your grid */
val geoms: RDD[Geometry] = ... /* Result of some previous work */
/* There are likely many clipped Geometries per SpatialKey... */
val layer: RDD[(SpatialKey, Geometry)] = geoms.clipToGrid(layout)
/* ... so we can group them! */
val grouped: RDD[(SpatialKey, Iterable[Geometry])] = layer.groupByKey
If clipping on the Extent boundaries is not what you want, there are ways to customize this. See the ClipToGrid entry in our Scaladocs.
Sparkified Viewshed
A Viewshed shows "visibility" from some set vantage point, given an Elevation raster. Prior to GeoTrellis 1.2 this was possible at the individual Tile
level but not the Layer (RDD
) level. Now it is.
First, we need to think about the Viewpoint
type:
import geotrellis.spark.viewshed._
val point: Viewpoint(
x = ..., // some coordinate.
y = ..., // some coordinate.
viewHeight = 4000, // 4 kilometres above the surface.
angle = Math.PI / 2, // direction that the "camera" faces (in radians). 0 == east.
fieldOfView = Math.PI / 2, // angular width of the "view port".
altitude = 0 // the height of points you're interested in seeing.
)
In other words:
- x, y, viewHeight: where are we?
- angle: what direction are we looking?
- fieldOfView: how wide are we looking?
- altitude: how high/low is the "target" of our viewing?
Given a Seq[Viewpoint]
(the algorithm supports multiple simultaneous
view points), we can do:
// Recall this common alias:
// type TileLayerRDD[K] = RDD[(K, Tile)] with Metadata[TileLayerMetadata[K]]
val layer: TileLayerRDD[SpatialKey] = ... /* Result of previous work */
val viewshed: TileLayerRDD[SpatialKey] = layer.viewshed(Seq(point))
Sparkified Euclidean Distance
We use Euclidean Distance to render a collection of points into a
heatmap of proximities of some area. Say, of two roads crossing:
Prior to GeoTrellis 1.2, this was possible at the individual Tile
level but not the Layer (RDD
) level. Now it is.
/* Result of previous work. Potentially millions of points per SpatialKey. */
val points: RDD[(SpatialKey, Array[Coordinate])] = ...
val layout: LayoutDefinition = ... /* The definition of your grid */
val layer: RDD[(SpatialKey, Tile)] = points.euclideanDistance(layout)
Polygonal Summaries over Time
The following was possible prior to GeoTrellis 1.2:
val layer: TileLayerRDD[SpatialKey] = ...
val polygon: Polgyon = ...
/* The maximum value within some Polygon overlaid on a Tile layer */
val summary: Double = layer.polygonalMaxDouble(polygon)
The above is also now possible for layers keyed by SpaceTimeKey
to
form a "time series":
val layer: TileLayerRDD[SpaceTimeKey] = ...
val polygon: MultiPolygon = ...
/* The maximum value within some Polygonal area at each time slice */
val summary: Map[ZonedDateTime, Double] = layer.maxSeries(polygon)
Overzooming ValueReader
A GeoTrellis ValueReader
connects to some layer catalog and lets you read individual values (usually Tiles):
import geotrellis.spark.io.s3._
val store: AttributeStore = ...
val reader: Reader[SpatialKey, Tile] = S3ValueReader(store).reader(LayerId("my-catalog", 10))
val tile: Tile = reader.read(SpatialKey(10, 10))
However .reader
is limited to zoom levels that actually exist for the given layer. Now you can use .overzoomingReader
to go as deep as you like:
import geotrellis.raster.resample._
val reader: Reader[SpatialKey, Tile] =
S3ValueReader(store).overzoomingReader(LayerId("my-catalog", 20), Average)
val tile: Tile = reader.read(SpatialKey(1000, 1000))
Regridding a Tile Layer
Have you ever wanted to "redraw" a grid over an established GeoTrellis layer? Say, this 16-tile Layer into a 4-tile one, both of 1024x1024 total pixels:
Prior to GeoTrellis 1.2, there was no official way to do this. Now you can use .regrid
:
/* The result of some previous work. Say each Tile is 256x256. */
val layer: TileLayerRDD[SpatialKey] = ...
/* "Recut" the tiles so that each one is now 512x512.
* No pixels are gained or lost, save some NODATA on the bottom
* and right edges that may appear for padding purposes.
*/
val regridded: TileLayerRDD[SpatialKey] = layer.regrid(512)
You can also regrid to non-rectangular sizes:
val regridded: TileLayerRDD[SpatialKey] = layer.regrid(tileCols = 100, tileRows = 300)
Robust Layer Querying
It's common to find a subset of Tiles in a layer that are touched by some given Polygon
:
val poly: Polygon = ???
val rdd: TileLayerRDD[SpatialKey] =
layerReader
.query[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](Layer("name", zoom))
.where(Intersects(poly))
.result
Now you can perform this same operation with Line
, MultiLine
, and even (Polygon, CRS)
to ensure that your Layer and Geometry always exist in the same projection.
Improved Tile
ASCII Art
Sometimes you just want to visualize a Tile
without going through the song-and-dance of rendering it to a .png
. The existing Tile.asciiDraw
method kind of does that, except its output is all in numbers.
The new Tile.renderAscii: Palette => String
method fulfills your heart's desire:
import geotrellis.raster._
import geotrellis.raster.io.geotiff._
import geotrellis.raster.render.ascii._
val tile: Tile = SinglebandGeoTiff("path/to/tiff.tiff").tile
// println(tile.renderAscii()) // the default
println(tile.renderAscii(AsciiArtEncoder.Palette.STIPLED))
▚▖
▚▚▜▚▚
▚▖▚▜▚▖▚▚
▜▚▚▚▜▚█▚▜▚█▚
█▚▜▖▜▖▚▚█▚▚▜▚█▖
▚▚█▚▜▚▚▚▚▚▚▚▜▚▚▚▚▚
▚▚▖▚▚▚▚▚█▜▚▚▜▚▚▖▚▖▚▖▚
▚▚▚▚█▚▚▚▚▚██▚▚▚▜▖▖██▚▚▜▚
▚▚█▚▚▚▚▚▚▚▜▚▚▚▚▚▚▜▚█▚▚▚▚▚▚▚
█▚▚▖▚█▚▜▚▚▚▚▖▚▚▚▚▚▚▚▚▚▚▜▚▚▚▚▚▚▖
█▚▚▚▜▚▖▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚██▖▜▚█▚▚▚
█▚▚██▚▚▚▚▚▚▚▚▖▚▚▚▚▚▚▚▚█▚▚▚▚▚▚▖▖▖▚▚▚▚
█▜▚▚██▜▚▚▚▜▖▚▚▜▚█▜▚▚▚▜▚▖▚▜▚█▚▚▖▚▚▖▚▚▖▖▚▚
▚▚█▚▚▚█▚██▚▚▚▚▚▚▚▚▜▚▚█▜▚▖█▚▚▚▜▚▚▚▚▚▚▜▚█▚█
█▚▜▚▜▚█▚▜▚▚▜▚█▚▚▚▚▚▚▚▚▚▚▚▖▚▖▚▚▖▚█▚█▚▚▚▖█▚
████▚███▚▚▚▚██▚▚▚█▜▚▚▖▚▚▚▖▖▚▚▚▚▚▚▚▚█▚▜▖█
▖█▜▚█▚██▜▖▜▜█▜▜█▜▚▚▚▚▚█▖▚▚▚▚█▚▚▚▚▚▚▜▚▚█▖▜
▚▖██▚▜▚█▚▚▜▜█▜▜▜██▚▚▚▚█▚▚▚▜▖▚▚█▚▖▚▜▚▚▚▖▚█
█▚▚▚▚▜▚██▖██▜▚▚█▚▚▖▚▚▜▚▖▚▖▚▚▚▚▚▖▚▚▖▖▖▚▖▚
▚▚▚█▚▚▚▚▚█▜▚▚████▚█▚▚▚█▚▖▚▚▚▖▚▚█▚▚▖▚▚▚▖▖▖
▚▚▚█▚▚▚▖▖▚▜█▜██▜██▚▚▖██▜▚▜▚█▚▚▚▚▚▚▚▚▖▖▜██
▚▚▚▚▜█▚▚▚▚▚█████▚▜██▚██▚▚▚▚▜▚▖▚█▚▚▖▚▖▚▚█
▚▚▜▚▚▚▚▜▚▜▚▚▚▚▜▚█▚▜█▚██▚██▚▚▚▚▖▚▚▚▚▖▖▚▚▖█
▚▜▚▜▚▚▚▚▚▚█▚▚▚▚▚██▜▜▜███▖▚▚▜█▚▚▖▚█▚▚█▚▖▚
▚▜▚▚▚▚▚▚▚▚▚▚▜▜▜▚▚▖▚▖▚▚▜▜██▜▚██▚▚▚▚▚▚▖▜█▚
▚▚▖▚▚█▚█▚▚▚█▚▖▚▚▚█▚▚▚▚▚▜██▚█▜▚█▚▜▚▚███▜█▜
▚▚▚▜▚▚▚▚▚▚▚▚▚▚▚▖█▚█▚▚▜█▜█▜█▜▚▖▚▚▚██▜▜█▚▜
▚▚▚▚▜▚▚▚▚▚▚▜▚▚▚▚▚▚▖▚█▜▖▖█▚▖▜▖▚▖█▚▖█▚▚▜▚█
▚▚█▚▚█▚▚▜▚▚▚▚▜▚▚▚▚▚▜▚▖▚█▜█▚▜▜▚█▖▜███▜▚▚
▚▚▚▚▚▚▖▜▚█▚▚▚▖▚▚▚▚▚▚▚▚▚▚▚▜█▖▜▜▜█▚▚▚▖▚█▚█
▜▚▚▚█▚▖▚█▚█▚▚█▚▚▚▚▚▚▚▖▚▚▚▜▚▚▚▜▚▖▚▖▚▚▚▚▜▚
▚▚▚▚▖▚█▖█▜▚▚▚▚▚▚▚▚▖▚▚▖▖█▚▜▚▖▚▚▚▚▖▖▚█▚▚▚
▚▚▚▚▚▚▚▚▚█▚▚▚▖▚▚▚█▚▜▚█▚▚▖▜██▚▖▚▚▚▚▚▚▚▚▚▖
▚▚▚▚▚▚▚▖▚▚██▚▚▚▚▚▚▚▚▜▚▚█▚██▚▚▚▚▖▚▚▖▚▚█▜▖
▚▚▚▚▚▚▚▚▚▚▚▚▚█▚▜▚▚▚▜▚▚▖▚▚▚▚▚▜▚▚▚▚▖▚▚▚▚▚
▚██▖▚▚▚▚▚▚▚▚▜▚▚█▚▚▚▚▜▚▚▚▚█▜▖▚▚█▜▜█▜█▚▖▚▖
▚▚▚▖▚▚█▚▚▜███▚▚▚▜▚▚▚▚▚█▚▖▖█▖▚████▜███▚██
▚█▚▚▚▚██▜▚▜▚▜▜▜█▜▚█▚▜▖▜▚▚▚█▚▜█▚▜▚▚▚▚▚▖▖
█▜█▚▚▜▚▜▚▜▜▜▚▚▚▚██▖▖▖▚██▖█▚▜▜▚▚▚▚▚▚▖
▚█▜▜▜▜▜██▚▜▚▚▚▚▚▚▖▜▚▜▚▚▚▜▚█▚▚▖▖▖
██▚▚▚▚▚▚▚▜▚▜▖▚██▜▜▚▖▚▚█▚▚▚▖▜▜
▜▚▚▖▚▚▚▖▚▜▜██▜▜▚█▚▚▜▚▚▜██▚
▚▚█▚▜▚▚█▖▜▚▚▚▖█▚▚█▚▚█▚
█▜▜▚▚▜▜▚▚▚▜█▚▚▚▜█▜█
▚▚▖▚█▖▚▖▜▚▖▚▖▜▚
███▖██▚▖▚▚▚▚
▜▚▚█▚▚▖▖█
▚▖▜█▜▚
▖█▚
Gorgious.
Storage on Azure via HDFS
By adding some additional configuration, you can now use our HDFS Layer Backend to read and write GeoTrellis layers to Microsoft Azure's blob storage.
S3 Configurability
It's now possible to customize how our S3 backend communicates with S3.
Configuring JTS Precision
GeoTrellis uses the Java Topology Suite for its vector processing.
By default, JTS uses a "floating" PrecisionModel.
When writing code that needs to be numerically robust, this default can lead to Topology Exceptions.
You can now use Typesafe Config to configure this to your application's needs. See here for the specifics.
Other New Features
- Polygonal Summaries for MultibandTiles
- Filter GeoTiffRDDs by Geometry
- Can create ValueReaders via URIs through LayerProvides classes
- Can read/write GeoTiffs with Sinusoidal projections
- Can Resample via Sum operation
Fixes
- Negative grid bounds bug
- getSignedByteArray BugFix - fixes certain read problems
- Allow Merge Queue To Handle Larger Inputs
- Generate Windows That Conform To GeoTiff Segments
- Removed inefficient LayerFilter check
- Fixed issue with S3 URI not having a key
prefix - Improve S3 makePath function
- Fix S3GeoTiffRDD behavior with some
options. - Allow Contains(Point) query for temporal RDDs
- Haversine formula fix
- Use Scaffeine instead of LRU cache in
HadoopValueReader - Fix GeoTiffInfo serialization issues
- Estimate partitions number based on GeoTiff segments
- Estimate partitions number basing on a desired partition size
- Pyramid operation preserves partitioning
- Don't constrain GridBounds size to IntMax x IntMax
- 4-Connected Line Drawing
- Added requirement for CRS implementations to provide a readable toString
representation. - Allow rasterizer to store Z value at double precision
- Changed scheme path file from /User -> current working
dir - Fix CRS parser and proj4 cea projection support
- Spark pyramid resamples tiles on the map side, reducing shuffle sizes
- ConstantTile map and mapDouble methods preserve CellType of tile
- ConstantTile combination with ArrayTile