Extents
The GeoTrellis raster module depends on the vector module to handle the job of describing the scope of real territory (usually in projected units) over which a Tile
is representative.
Extent
is the class which, paired with a Tile
, allows us to build a Raster
.
Its constructor takes a minimum X, minimum Y, maximum X, and a maximum Y.
With these boundaries we can actually fix the corners of an associated Tile
and use interpolation to determine mappings from real world coordinates within the provided Extent
to values derived from that Tile
's cells.
import geotrellis.vector._
val extent = Extent(0, 0, 90, 90)
// extent: Extent = Extent(0.0, 0.0, 90.0, 90.0)
As discussed in vectors, rather than reinvent the wheel, geotrellis.vector
provides conveniences which leverage the great work done in JTS.
That fact is apparent here as well: the GeoTrellis Extent
is the JTS Envelope
.
extent.jtsEnvelope
// res0: org.locationtech.jts.geom.Envelope = Env[0.0 : 90.0, 0.0 : 90.0]
Extent Reprojection
As implied by the limited input to its constructor, an Extent
can only represent a rectangular region which is aligned with the coordinate system.
GeoTrellis provides three different ways to reproject Extents
Naive Reprojection (not recommended)
Naive reprojection of an Extent
is very simple but can mislead.
As seen in the case of reprojection as a Polygon
, Extent
shapes can be expected to warp during projection.
Imagine turning a rectangle on its diagonal: drawing a rectangle which surrounds this diagonal rectangle but which remains parallel with the original ensures a larger rectangle than the one we turned on its diagonal.
import geotrellis.proj4.{LatLng, WebMercator, Transform}
val transform = Transform(LatLng, WebMercator)
// transform: (Double, Double) => (Double, Double) = geotrellis.proj4.Proj4Transform$$$Lambda$9397/1941514068@6b9e1606
extent.reproject(transform)
// res1: Extent = Extent(
// 0.0,
// -7.081154551613622E-10,
// 1.0018754171394622E7,
// 2.3810769326496765E8
// )
Caution is warranted when naively reprojecting an
Extent
. It can be heuristically useful, but should be relied upon for high precision calculations. Instead, usereprojectAsPolygon
if reprojecting only theExtent
and cellgrid-aware reprojection if attempting to reproject a gridded space.
Polygon Reprojection (recommended)
Rectangular regions become curvilinear regions after geodetic projection.
To see exactly which bits of territory in projection B are covered by an Extent
defined for projection A, it should be reprojected as a polygon
extent.reprojectAsPolygon(transform, 0.01)
// res2: Polygon = POLYGON ((0 -0.0000000007081155, 5009377.085697311 -0.0000000007081155, 10018754.171394622 -0.0000000007081155, 10018754.171394622 5621521.486192066, 10018754.171394622 238107693.26496765, 5009377.085697311 238107693.26496765, 0 238107693.26496765, 0 5621521.486192066, 0 -0.0000000007081155))
This method adaptively refines its output. To exit adaptive refinement, a tolerance value is required to define success. In this case, 0.01 specifies the amount of deflection allowed in terms of distance from the original
Extent
's line to the point which represents it under another projection
CellGrid-Aware Reprojection (recommended)
Reprojection of a gridded space's extent imposes constraints not seen in the above cases.
In addition to the needing to accurately capture the transformation of an Extent
, this method requires information about the grid of cells which corresponds to said input Extent
.
This information is used to approximately preserve the pre-reprojection resolution.
They only information in addition to Extent
required to compute this kind of reprojection is the resolution.
Extent
plus resolution yields a GridExtent
or a RasterExtent
, either of which are suitable for carrying out reprojection of a gridded space.
Take note that the CellSize
has been transformed in addition to the Extent
:
import geotrellis.raster._
val cs = CellSize(1, 1)
// cs: CellSize = CellSize(1.0, 1.0)
val before = GridExtent[Long](extent, cs)
// before: GridExtent[Long] = GridExtent(Extent(0.0, 0.0, 90.0, 90.0),CellSize(1.0,1.0),90x90)
val after = before.reproject(LatLng, WebMercator)
// after: GridExtent[Long] = GridExtent(Extent(0.0, 312130.176451385, 9362030.042854972, 2.3810769326496765E8),CellSize(1872406.0085709943,1872406.0085709943),5x127)