Skip to content
This repository has been archived by the owner on Dec 20, 2018. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
aklink committed Jan 18, 2018
2 parents 6e5b5cd + ebd0bc8 commit a2c9418
Show file tree
Hide file tree
Showing 4 changed files with 304 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package biggis.landuse.spark.examples

import biggis.landuse.api.SpatialMultibandRDD
import com.typesafe.scalalogging.LazyLogging
import geotrellis.spark.pyramid.Pyramid
import geotrellis.spark.tiling.ZoomedLayoutScheme
import geotrellis.spark.{LayerId, MultibandTileLayerRDD, TileLayerRDD, resample, _}
import org.apache.spark.SparkContext
import org.apache.spark.SparkException

object ZoomResampleLayer extends LazyLogging {
/**
* Run as: layerNameIn zoomIn layerNameOut zoomOut /path/to/catalog
*/
def main(args: Array[String]): Unit = {
try {
val Array(layerNameIn, zoomIn, layerNameOut, zoomOut, catalogPath) = args
implicit val sc : SparkContext = Utils.initSparkAutoContext
ZoomResampleLayer(layerNameIn, zoomIn, layerNameOut, zoomOut)(catalogPath, sc)
sc.stop()
} catch {
case _: MatchError => println("Run as: layerNameIn zoomIn layerNameOut zoomOut /path/to/catalog")
case e: SparkException => logger error e.getMessage + ". Try to set JVM parameter: -Dspark.master=local[*]"
}
}

def apply(layerNameIn: String, zoomIn: String, layerNameOut: String, zoomOut: String)(implicit catalogPath: String, sc: SparkContext) {

logger info s"Resampling layer '$layerNameIn' with '$zoomOut' into '$layerNameOut' with '$zoomOut' in catalog '$catalogPath' ... "

val inputRdd : SpatialMultibandRDD = biggis.landuse.api.readRddFromLayer((layerNameIn, zoomIn.toInt))

val outputRdd : SpatialMultibandRDD = //inputRdd//.asInstanceOf[MultibandTileLayerRDD[SpatialKey]].resampleToZoom(zoomIn.toInt, zoomOut.toInt)
if(zoomOut > zoomIn) resampleLayerToZoom(inputRdd, zoomIn.toInt, zoomOut.toInt) // UpSample (DownLevel) using ZoomResample
else if(zoomOut < zoomIn) Pyramid.up( inputRdd, ZoomedLayoutScheme(inputRdd.metadata.crs), zoomOut.toInt)._2 // DownSample (UpLevel) using Pyramid.up
else inputRdd

biggis.landuse.api.writeRddToLayer(outputRdd, LayerId(layerNameOut, zoomOut.toInt))

logger info "done."
}

implicit def resampleLayerToZoom(rdd: MultibandTileLayerRDD[SpatialKey], zoomLevelIn: Int, zoomLevelOut: Int)(implicit sc: SparkContext): MultibandTileLayerRDD[SpatialKey] = {
geotrellis.spark.resample.Implicits.withZoomResampleMultibandMethods(rdd).resampleToZoom(zoomLevelIn, zoomLevelOut)
}

}
25 changes: 25 additions & 0 deletions src/main/scala/geotrellis/spark/resample/Implicits.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2016 Azavea
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package geotrellis.spark.resample

import geotrellis.spark._

object Implicits extends Implicits

trait Implicits {
implicit class withZoomResampleMultibandMethods[K: SpatialComponent](self: MultibandTileLayerRDD[K]) extends ZoomResampleMultibandMethods[K](self)
}
152 changes: 152 additions & 0 deletions src/main/scala/geotrellis/spark/resample/ZoomResampleMultiband.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright 2016 Azavea
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package geotrellis.spark.resample

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

import org.apache.spark.rdd.RDD

object ZoomResampleMultiband {
private def gridBoundsAtZoom(sourceZoom: Int, spatialKey: SpatialKey, targetZoom: Int): GridBounds = {
val SpatialKey(col, row) = spatialKey
val zoomDiff = targetZoom - sourceZoom
val factor = math.pow(2, zoomDiff).toInt
val (minCol, minRow) = (col * factor, row * factor)
val (maxCol, maxRow) = (((col + 1) * factor) - 1, ((row + 1) * factor) - 1)
GridBounds(minCol, minRow, maxCol, maxRow)
}

private def boundsAtZoom[K: SpatialComponent](sourceZoom: Int, bounds: Bounds[K], targetZoom: Int): Bounds[K] =
bounds match {
case KeyBounds(minKey, maxKey) =>
val min = {
val gb = gridBoundsAtZoom(sourceZoom, minKey.getComponent[SpatialKey], targetZoom)
minKey.setComponent(SpatialKey(gb.colMin, gb.rowMin))
}

val max = {
val gb = gridBoundsAtZoom(sourceZoom, maxKey.getComponent[SpatialKey], targetZoom)
maxKey.setComponent(SpatialKey(gb.colMax, gb.rowMax))
}
KeyBounds(min, max)
case EmptyBounds =>
EmptyBounds
}

/** Resamples a tile layer from a lower zoom level to a higher zoom level.
* The levels are based on the ZoomedLayoutScheme.
*
* @param rdd The RDD to be resampled.
* @param sourceZoom The zoom level of the rdd.
* @param targetZoom The zoom level we want to resample to.
* @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: MultibandTileLayerRDD[K],
sourceZoom: Int,
targetZoom: Int,
targetGridBounds: Option[GridBounds] = None,
method: ResampleMethod = NearestNeighbor
): MultibandTileLayerRDD[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, MultibandTile)], 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, MultibandTile)] = rdd.flatMap { case (key, tile) =>
val gbaz: Option[GridBounds] =
gridBoundsAtZoom(sourceZoom, key.getComponent[SpatialKey], targetZoom)
.intersection(resampleGridBounds)

gbaz.map { gb =>
gb.coordsIter
.map { case (col, row) =>
val sourceExtent = sourceMapTransform.keyToExtent(key.getComponent[SpatialKey])
val targetExtent = targetMapTransform.keyToExtent(col, row)
val resampled = tile.resample(
sourceExtent,
RasterExtent(targetExtent, tileSize, tileSize),
method
)

(key.setComponent(SpatialKey(col, row)), resampled)
}
}.getOrElse(Iterator.empty)
}

val extent: Extent =
targetMapTransform(resampleGridBounds).intersection(rdd.metadata.extent).get

val md = rdd.metadata.copy(
layout = targetLayoutDefinition,
bounds = resampleKeyBounds.setSpatialBounds(resampleGridBounds),
extent = extent
)

(resampled, md)

case None =>
val md = rdd.metadata.copy(
layout = targetLayoutDefinition,
bounds = EmptyBounds: Bounds[K]
)

(rdd.sparkContext.emptyRDD[(K, MultibandTile)], md)

}
case None =>
val resampled: RDD[(K, MultibandTile)] =
rdd
.flatMap { case (key, tile) =>
gridBoundsAtZoom(sourceZoom, key.getComponent[SpatialKey], targetZoom)
.coordsIter
.map { case (col, row) =>
val sourceExtent = sourceMapTransform.keyToExtent(key.getComponent[SpatialKey])
val targetExtent = targetMapTransform.keyToExtent(col, row)
val resampled =
tile.resample(sourceExtent, RasterExtent(targetExtent, tileSize, tileSize), method)
(key.setComponent(SpatialKey(col, row)), resampled)
}
}

val md = rdd.metadata.copy(
layout = targetLayoutDefinition,
bounds = boundsAtZoom(sourceZoom, rdd.metadata.bounds, targetZoom)
)

(resampled, md)
}


ContextRDD(resampledRdd, md)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2016 Azavea
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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.vector.Extent

abstract class ZoomResampleMultibandMethods[K: SpatialComponent](val self: MultibandTileLayerRDD[K]) extends MethodExtensions[MultibandTileLayerRDD[K]] {
def resampleToZoom(
sourceZoom: Int,
targetZoom: Int
): MultibandTileLayerRDD[K] =
resampleToZoom(sourceZoom, targetZoom, None, NearestNeighbor)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
method: ResampleMethod
): MultibandTileLayerRDD[K] =
resampleToZoom(sourceZoom, targetZoom, None, method)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
targetGridBounds: GridBounds
): MultibandTileLayerRDD[K] =
resampleToZoom(sourceZoom, targetZoom, Some(targetGridBounds), NearestNeighbor)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
targetGridBounds: GridBounds,
method: ResampleMethod
): MultibandTileLayerRDD[K] =
resampleToZoom(sourceZoom, targetZoom, Some(targetGridBounds), method)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
targetExtent: Extent
): MultibandTileLayerRDD[K] =
resampleToZoom(sourceZoom, targetZoom, targetExtent, NearestNeighbor)

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int,
targetExtent: Extent,
method: ResampleMethod
): MultibandTileLayerRDD[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)
}

def resampleToZoom(
sourceZoom: Int,
targetZoom: Int ,
targetGridBounds: Option[GridBounds] = None,
method: ResampleMethod = NearestNeighbor
): MultibandTileLayerRDD[K] =
ZoomResampleMultiband(self, sourceZoom, targetZoom, targetGridBounds, method)
}

0 comments on commit a2c9418

Please sign in to comment.