Skip to content

Commit

Permalink
Fix codestyle, make TypeClass more generic, cover with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pomadchin committed Oct 7, 2019
1 parent cb8a443 commit aba9c53
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 33 deletions.
43 changes: 30 additions & 13 deletions raster/src/main/scala/geotrellis/raster/FeatureExtraction.scala
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
/*
* Copyright 2019 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.raster

import geotrellis.raster.rasterize.Rasterizer
import geotrellis.vector.{Feature, Geometry, Point, PointFeature}

import spire.syntax.cfor._

import scala.collection.mutable.ListBuffer

trait FeatureExtraction[G <: Geometry, T <: CellGrid[Int], D] {
def features(geom: G, raster: Raster[T]): Array[Array[PointFeature[D]]]
trait FeatureExtraction[I <: Geometry, T <: CellGrid[Int], O <: Geometry, D] {
def features(geom: I, raster: Raster[T]): Array[Array[Feature[O, D]]]
}

object FeatureExtraction {
def apply[G <: Geometry: FeatureExtraction[*, T, D], T <: CellGrid[Int], D] = implicitly[FeatureExtraction[G, T, D]]
def apply[I <: Geometry: FeatureExtraction[*, T, O, D], T <: CellGrid[Int], O <: Geometry, D] = implicitly[FeatureExtraction[I, T, O, D]]

implicit def multibandTile[G <: Geometry] = new FeatureExtraction[G, MultibandTile, Int] {
def features(geom: G, raster: Raster[MultibandTile]): Array[Array[PointFeature[Int]]] = {
implicit def multibandTile[I <: Geometry] = new PointFeatureExtraction[I, MultibandTile, Int] {
def features(geom: I, raster: Raster[MultibandTile]): Array[Array[PointFeature[Int]]] = {
val arr = Array.ofDim[Array[PointFeature[Int]]](raster.tile.bandCount)

cfor(0)(_ < raster.tile.bandCount, _ + 1) { i =>
Expand All @@ -29,8 +46,8 @@ object FeatureExtraction {
}
}

implicit def multibandTileDouble[G <: Geometry] = new FeatureExtraction[G, MultibandTile, Double] {
def features(geom: G, raster: Raster[MultibandTile]): Array[Array[PointFeature[Double]]] = {
implicit def multibandTileDouble[I <: Geometry] = new PointFeatureExtraction[I, MultibandTile, Double] {
def features(geom: I, raster: Raster[MultibandTile]): Array[Array[PointFeature[Double]]] = {
val arr = Array.ofDim[Array[PointFeature[Double]]](raster.tile.bandCount)

cfor(0)(_ < raster.tile.bandCount, _ + 1) { i =>
Expand All @@ -45,14 +62,14 @@ object FeatureExtraction {
}
}

implicit def tile[G <: Geometry] = new FeatureExtraction[G, Tile, Int] {
def features(geom: G, raster: Raster[Tile]): Array[Array[PointFeature[Int]]] =
multibandTile[G].features(geom, raster.mapTile(MultibandTile(_)))
implicit def tile[I <: Geometry] = new PointFeatureExtraction[I, Tile, Int] {
def features(geom: I, raster: Raster[Tile]): Array[Array[PointFeature[Int]]] =
multibandTile[I].features(geom, raster.mapTile(MultibandTile(_)))
}

implicit def tileDouble[G <: Geometry] = new FeatureExtraction[G, Tile, Double] {
def features(geom: G, raster: Raster[Tile]): Array[Array[PointFeature[Double]]] = {
multibandTileDouble[G].features(geom, raster.mapTile(MultibandTile(_)))
implicit def tileDouble[I <: Geometry] = new PointFeatureExtraction[I, Tile, Double] {
def features(geom: I, raster: Raster[Tile]): Array[Array[PointFeature[Double]]] = {
multibandTileDouble[I].features(geom, raster.mapTile(MultibandTile(_)))
}
}
}
30 changes: 10 additions & 20 deletions raster/src/main/scala/geotrellis/raster/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@
package geotrellis.raster

import geotrellis.vector.Point
import geotrellis.macros.{NoDataMacros, TypeConversionMacros}
import geotrellis.vector._
import geotrellis.util.{MethodExtensions, np}


object Implicits extends Implicits

trait Implicits
Expand Down Expand Up @@ -127,25 +125,17 @@ trait Implicits
}
}

implicit class MultibandRasterFeatureExtractionMethods(val self: Raster[MultibandTile]) extends MethodExtensions[Raster[MultibandTile]] {
def features[G <: Geometry: FeatureExtraction[*, MultibandTile, D], D](geom: G, raster: Raster[MultibandTile]): Array[Array[PointFeature[D]]] =
FeatureExtraction[G, MultibandTile, D].features(geom, raster)

def featuresInt[G <: Geometry: FeatureExtraction[*, MultibandTile, Int]](geom: G, raster: Raster[MultibandTile]): Array[Array[PointFeature[Int]]] =
features[G, Int](geom, raster)

def featuresDouble[G <: Geometry: FeatureExtraction[*, MultibandTile, Double]](geom: G, raster: Raster[MultibandTile]): Array[Array[PointFeature[Double]]] =
features[G, Double](geom, raster)
implicit class MultibandRasterPointFeatureExtractionMethods(val self: Raster[MultibandTile]) extends MethodExtensions[Raster[MultibandTile]] {
def pointFeatures[D] = new {
def apply[I <: Geometry: PointFeatureExtraction[*, MultibandTile, D]](geom: I): Array[Array[PointFeature[D]]] =
FeatureExtraction[I, MultibandTile, Point, D].features(geom, self)
}
}

implicit class rasterFeatureExtractionMethods(val self: Raster[Tile]) extends MethodExtensions[Raster[Tile]] {
def features[G <: Geometry: FeatureExtraction[*, Tile, D], D](geom: G, raster: Raster[Tile]): Array[PointFeature[D]] =
FeatureExtraction[G, Tile, D].features(geom, raster).head

def featuresInt[G <: Geometry: FeatureExtraction[*, Tile, Int]](geom: G, raster: Raster[Tile]): Array[PointFeature[Int]] =
features[G, Int](geom, raster)

def featuresDouble[G <: Geometry: FeatureExtraction[*, Tile, Double]](geom: G, raster: Raster[Tile]): Array[PointFeature[Double]] =
features[G, Double](geom, raster)
implicit class rasterPointFeatureExtractionMethods(val self: Raster[Tile]) extends MethodExtensions[Raster[Tile]] {
def pointFeatures[D] = new {
def apply[I <: Geometry: PointFeatureExtraction[*, Tile, D]](geom: I): Array[PointFeature[D]] =
FeatureExtraction[I, Tile, Point, D].features(geom, self).head
}
}
}
3 changes: 3 additions & 0 deletions raster/src/main/scala/geotrellis/raster/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package geotrellis

import geotrellis.macros.{NoDataMacros, TypeConversionMacros}
import geotrellis.vector.{Geometry, Point}
import spire.math.Integral


Expand Down Expand Up @@ -184,6 +185,8 @@ package object raster extends Implicits {
def fill(v: Double) = { java.util.Arrays.fill(arr, v) ; arr }
}

type PointFeatureExtraction[I <: Geometry, T <: CellGrid[Int], D] = FeatureExtraction[I, T, Point, D]

/* http://stackoverflow.com/questions/3508077/how-to-define-type-disjunction-union-types */
sealed class TileOrMultibandTile[T]
object TileOrMultibandTile {
Expand Down
103 changes: 103 additions & 0 deletions raster/src/test/scala/geotrellis/raster/FeatureExtractionSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2019 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.raster

import geotrellis.vector._
import geotrellis.raster.testkit._

import org.scalatest._

class FeatureExtractionSpec extends FunSpec
with Matchers
with RasterMatchers
with TileBuilders {
describe("Tile") {
it("should extract all int point features") {
val ext = Extent(0.0, 0.0, 3.0, 3.0)
val data = Array(
1, 2, 3,
4, 5, 6,
7, 8, 9
)
val raster = Raster(ArrayTile(data, 3, 3), ext)

val features = raster.pointFeatures[Int](ext.toPolygon)

features.map(_.data) shouldBe data
features.foreach { case feature @ Feature(point, _) => raster.pointFeatures[Int](point).head shouldBe feature }
}

it("should extract all double point features") {
val ext = Extent(0.0, 0.0, 3.0, 3.0)
val data = Array(
1.1, 2.2, 3.3,
4.4, 5.5, 6.6,
7.7, 8.8, 9.8
)
val raster = Raster(ArrayTile(data, 3, 3), ext)

val features = raster.pointFeatures[Double](ext.toPolygon)

features.map(_.data) shouldBe data
features.foreach { case feature @ Feature(point, _) => raster.pointFeatures[Double](point).head shouldBe feature }
}
}

describe("MultibandTile") {
it("should extract all int point features") {
val ext = Extent(0.0, 0.0, 3.0, 3.0)
val b1 = Array(
1, 2, 3,
4, 5, 6,
7, 8, 9
)
val b2 = b1.map(_ + 1)
val b3 = b2.map(_ + 1)
val data = Array(b1, b2, b3)
val raster = Raster(MultibandTile(data.map(ArrayTile(_, 3, 3))), ext)

val features = raster.pointFeatures[Int](ext.toPolygon)

features.map(_.map(_.data)) shouldBe data
features.zipWithIndex.foreach { case (features, i) =>
features.foreach { case feature @ Feature(point, _) =>
raster.pointFeatures[Int](point).apply(i).head shouldBe feature }
}
}

it("should extract all double point features") {
val ext = Extent(0.0, 0.0, 3.0, 3.0)
val b1 = Array(
1.1, 2.2, 3.3,
4.4, 5.5, 6.6,
7.7, 8.8, 9.8
)
val b2 = b1.map(_ + 1)
val b3 = b2.map(_ + 1)
val data = Array(b1, b2, b3)
val raster = Raster(MultibandTile(data.map(ArrayTile(_, 3, 3))), ext)

val features = raster.pointFeatures[Double](ext.toPolygon)

features.map(_.map(_.data)) shouldBe data
features.zipWithIndex.foreach { case (features, i) =>
features.foreach { case feature @ Feature(point, _) =>
raster.pointFeatures[Double](point).apply(i).head shouldBe feature }
}
}
}
}

0 comments on commit aba9c53

Please sign in to comment.