Skip to content

Commit

Permalink
ZoomResample will now work on Both TileLayerRDDs and MultibandTileLay…
Browse files Browse the repository at this point in the history
…erRDDs

Signed-off-by: jbouffard <jbouffard@azavea.com>

Created TileLayerRDDZoomResampleMethods and MultibandTileLayerRDDZoomResampleMethods

Signed-off-by: jbouffard <jbouffard@azavea.com>

Renamed ZoomResampleMethods to TileLayerRDDZoomResampleMethods

Added unit tests that used MultibandTileLayerRDDs for ZoomResample

Updated the changelog

Signed-off-by: jbouffard <jbouffard@azavea.com>

Consolidatd the methods for ZoomResample into LayerRDDZoomResampleMethods

Signed-off-by: jbouffard <jbouffard@azavea.com>
  • Loading branch information
jbouffard authored and echeipesh committed Feb 23, 2018
1 parent 94278df commit 3dbcc4f
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 19 deletions.
1 change: 1 addition & 0 deletions docs/CHANGELOG.rst
Expand Up @@ -18,6 +18,7 @@ API Changes
- **Change:** Reprojection has improved performance due to one less shuffle stage and lower memory usage.
``TileRDDReproject`` loses dependency on ``TileReprojectMethods`` in favor of ``RasterRegionReproject``
- **New:** CollectionLayerReader now has an SPI interface.
- **New:** ``ZoomResample`` can now be used on ``MultibandTileLayerRDD``\s.

1.2.1
_____
Expand Down
12 changes: 11 additions & 1 deletion spark/src/main/scala/geotrellis/spark/resample/Implicits.scala
Expand Up @@ -16,10 +16,20 @@

package geotrellis.spark.resample

import geotrellis.raster._
import geotrellis.raster.resample._
import geotrellis.spark._
import geotrellis.spark.tiling._
import geotrellis.util._
import geotrellis.vector._

import org.apache.spark.rdd._

object Implicits extends Implicits

trait Implicits {
implicit class withZoomResampleMethods[K: SpatialComponent](self: TileLayerRDD[K]) extends ZoomResampleMethods[K](self)
implicit class withLayerRDDZoomResampleMethods[
K: SpatialComponent,
V <: CellGrid: (? => TileResampleMethods[V])
](self: RDD[(K, V)] with Metadata[TileLayerMetadata[K]]) extends LayerRDDZoomResampleMethods[K, V](self)
}
Expand Up @@ -19,52 +19,57 @@ package geotrellis.spark.resample
import geotrellis.raster._
import geotrellis.raster.resample._
import geotrellis.spark._
import geotrellis.spark.tiling.ZoomedLayoutScheme
import geotrellis.util.MethodExtensions
import geotrellis.spark.tiling.{ZoomedLayoutScheme, LayoutDefinition}
import geotrellis.util._
import geotrellis.vector.Extent

abstract class ZoomResampleMethods[K: SpatialComponent](val self: TileLayerRDD[K]) extends MethodExtensions[TileLayerRDD[K]] {
import org.apache.spark.rdd._

abstract class LayerRDDZoomResampleMethods[
K: SpatialComponent,
V <: CellGrid: (? => TileResampleMethods[V])
](val self: RDD[(K, V)] with Metadata[TileLayerMetadata[K]]) extends MethodExtensions[RDD[(K, V)] with Metadata[TileLayerMetadata[K]]] {
def resampleToZoom(
sourceZoom: Int,
targetZoom: Int
): TileLayerRDD[K] =
): RDD[(K, V)] with Metadata[TileLayerMetadata[K]] =
resampleToZoom(sourceZoom, targetZoom, None, NearestNeighbor)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
method: ResampleMethod
): TileLayerRDD[K] =
): RDD[(K, V)] with Metadata[TileLayerMetadata[K]] =
resampleToZoom(sourceZoom, targetZoom, None, method)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
targetGridBounds: GridBounds
): TileLayerRDD[K] =
): RDD[(K, V)] with Metadata[TileLayerMetadata[K]] =
resampleToZoom(sourceZoom, targetZoom, Some(targetGridBounds), NearestNeighbor)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
targetGridBounds: GridBounds,
method: ResampleMethod
): TileLayerRDD[K] =
): RDD[(K, V)] with Metadata[TileLayerMetadata[K]] =
resampleToZoom(sourceZoom, targetZoom, Some(targetGridBounds), method)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
targetExtent: Extent
): TileLayerRDD[K] =
): RDD[(K, V)] with Metadata[TileLayerMetadata[K]] =
resampleToZoom(sourceZoom, targetZoom, targetExtent, NearestNeighbor)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
targetExtent: Extent,
method: ResampleMethod
): TileLayerRDD[K] = {
): RDD[(K, V)] with Metadata[TileLayerMetadata[K]] = {
val layout = ZoomedLayoutScheme.layoutForZoom(targetZoom, self.metadata.layout.extent, self.metadata.layout.tileLayout.tileCols)
val targetGridBounds = layout.mapTransform(targetExtent)
resampleToZoom(sourceZoom, targetZoom, Some(targetGridBounds), method)
Expand All @@ -75,6 +80,6 @@ abstract class ZoomResampleMethods[K: SpatialComponent](val self: TileLayerRDD[K
targetZoom: Int ,
targetGridBounds: Option[GridBounds] = None,
method: ResampleMethod = NearestNeighbor
): TileLayerRDD[K] =
): RDD[(K, V)] with Metadata[TileLayerMetadata[K]] =
ZoomResample(self, sourceZoom, targetZoom, targetGridBounds, method)
}
17 changes: 10 additions & 7 deletions spark/src/main/scala/geotrellis/spark/resample/ZoomResample.scala
Expand Up @@ -61,28 +61,31 @@ object ZoomResample {
* @param targetGridBounds Optionally, a grid bounds in the target zoom level we want to filter by.
* @param method The resample method to use for resampling.
*/
def apply[K: SpatialComponent](
rdd: TileLayerRDD[K],
def apply[
K: SpatialComponent,
V <: CellGrid: (? => TileResampleMethods[V])
](
rdd: RDD[(K, V)] with Metadata[TileLayerMetadata[K]],
sourceZoom: Int,
targetZoom: Int,
targetGridBounds: Option[GridBounds] = None,
method: ResampleMethod = NearestNeighbor
): TileLayerRDD[K] = {
): RDD[(K, V)] with Metadata[TileLayerMetadata[K]] = {
require(sourceZoom < targetZoom, "This resample call requires that the target zoom level be greater than the source zoom level")
val tileSize = rdd.metadata.layout.tileLayout.tileCols
val targetLayoutDefinition =
ZoomedLayoutScheme.layoutForZoom(targetZoom, rdd.metadata.layout.extent, tileSize)
val targetMapTransform = targetLayoutDefinition.mapTransform
val sourceMapTransform = rdd.metadata.mapTransform
val (resampledRdd: RDD[(K, Tile)], md) =
val (resampledRdd: RDD[(K, V)], md) =
targetGridBounds match {
case Some(tgb) =>
val resampleKeyBounds: KeyBounds[K] =
boundsAtZoom(sourceZoom, rdd.metadata.bounds, targetZoom).get

resampleKeyBounds.toGridBounds.intersection(tgb) match {
case Some(resampleGridBounds) => {
val resampled: RDD[(K, Tile)] = rdd.flatMap { case (key, tile) =>
val resampled: RDD[(K, V)] = rdd.flatMap { case (key, tile) =>
val gbaz: Option[GridBounds] =
gridBoundsAtZoom(sourceZoom, key.getComponent[SpatialKey], targetZoom)
.intersection(resampleGridBounds)
Expand Down Expand Up @@ -120,11 +123,11 @@ object ZoomResample {
bounds = (EmptyBounds: Bounds[K])
)

(rdd.sparkContext.emptyRDD[(K, Tile)], md)
(rdd.sparkContext.emptyRDD[(K, V)], md)
}
}
case None => {
val resampled: RDD[(K, Tile)] =
val resampled: RDD[(K, V)] =
rdd
.flatMap { case (key, tile) =>
gridBoundsAtZoom(sourceZoom, key.getComponent[SpatialKey], targetZoom)
Expand Down
Expand Up @@ -18,7 +18,7 @@ package geotrellis.spark.resample

import geotrellis.raster._
import geotrellis.raster.testkit._
import geotrellis.raster.io.geotiff.SinglebandGeoTiff
import geotrellis.raster.io.geotiff.{SinglebandGeoTiff, MultibandGeoTiff}
import geotrellis.spark._
import geotrellis.spark.testkit._
import geotrellis.vector.Extent
Expand Down Expand Up @@ -60,6 +60,34 @@ class ZoomResampleMethodsSpec extends FunSpec
}
}

describe("Zoom Resample on MultibandTileLayerRDD - aspect.tif") {
val path = "raster/data/aspect.tif"
val gt = MultibandGeoTiff(path)
val originalRaster = gt.raster.resample(500, 500)
val rdd = createMultibandTileLayerRDD(sc, originalRaster, TileLayout(5, 5, 100, 100), gt.crs)
val md = rdd.metadata
val overall = md.extent
val Extent(xmin, ymin, xmax, ymax) = overall
val half = Extent(xmin, ymin, xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2)
val small = Extent(xmin, ymin, xmin + (xmax - xmin) / 5, ymin + (ymax - ymin) / 5)

it("should correctly crop by the rdd extent") {
val count = rdd.crop(overall).count
count should be (25)
}

it("should correctly increase the number of tiles by 2 when going up one level") {
val resampled = rdd.resampleToZoom(5, 6)
val count = resampled.count
count should be (rdd.count * 4)

val gridBounds = rdd.metadata.bounds.get.toGridBounds
val resampledGridBounds = resampled.metadata.bounds.get.toGridBounds

resampledGridBounds.sizeLong should be (gridBounds.sizeLong * 4)
}
}

describe("Zoom Resample on TileLayerRDD - manual example") {
it("should correctly resample and filter in a larger example") {
val tile =
Expand Down Expand Up @@ -93,4 +121,39 @@ class ZoomResampleMethodsSpec extends FunSpec
}
}
}

describe("Zoom Resample on MultibandTileLayerRDD - manual example") {
it("should correctly resample and filter in a larger example") {
val tile =
createTile(
Array(
1, 1, 2, 2, 3, 3, 4, 4,
1, 1, 2, 2, 3, 3, 4, 4,

5, 5, 6, 6, 7, 7, 8, 8,
5, 5, 6, 6, 7, 7, 8, 8,

9, 9, 10, 10, 11, 11, 12, 12,
9, 9, 10, 10, 11, 11, 12, 12,

13, 13, 13, 13, 14, 14, 15, 15,
13, 13, 13, 13, 14, 14, 15, 15
), 8, 8)
val layer =
createMultibandTileLayerRDD(
sc,
MultibandTile(tile, tile, tile),
TileLayout(2, 2, 4, 4)
)

val resampled = layer.resampleToZoom(1, 2, GridBounds(1, 1, 1, 1))
val count = resampled.count
count should be (1)
val result = resampled.collect.head._2

result.foreach { z =>
z should be (Array(6, 6, 6))
}
}
}
}

0 comments on commit 3dbcc4f

Please sign in to comment.