All Downloads are FREE. Search and download functionalities are using the official Maven repository.

geotrellis.raster.KernelStamper.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2014 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.mapalgebra.focal.Kernel


/**
  * Supplies functionality to operations that do convolution.
  */
trait KernelStamper {

  /**
    * Given a column, row, and value, apply the kernel at the given
    * point.
    */
  def stampKernel(col: Int, row: Int, z: Int): Unit

  /**
    * Given a column, row, and value, apply the kernel at the given
    * point.
    */
  def stampKernelDouble(col: Int, row: Int, z: Double): Unit

  def result: Tile
}

/**
  * The companion object for the [[KernelStamper]] trait.
  */
object KernelStamper {
  def apply(cellType: CellType, cols: Int, rows: Int, k: Kernel): KernelStamper =
    if(k.cellType.isFloatingPoint) DoubleKernelStamper(cellType, cols, rows, k)
    else IntKernelStamper(cellType, cols, rows, k)
}

/**
  * A [[KernelStamper]] for double-valued tiles.
  */
case class DoubleKernelStamper(cellType: CellType, cols: Int, rows: Int, k: Kernel) extends KernelStamper {

  val ktile = k.tile
  val kernelcols = ktile.cols
  val kernelrows = ktile.rows

  val tile: MutableArrayTile = ArrayTile.empty(cellType, cols, rows)

  /**
    * Given a column, row, and value, apply the kernel at the given
    * point.
    */
  def stampKernel(col: Int, row: Int, z: Int) = {
    if(z == 0) {
      val o = tile.get(col, row)
      tile.set(col, row,
        if(isNoData(o)) 0
        else o
      )
    } else {

      val rowmin = row - kernelrows / 2
      val rowmax = math.min(row + kernelrows / 2 + 1, rows)

      val colmin = col - kernelcols / 2
      val colmax = math.min(col + kernelcols / 2 + 1, cols)

      var kcol = 0
      var krow = 0

      var r = rowmin
      var c = colmin
      while(r < rowmax) {
        while(c < colmax) {
          if (r >= 0 && c >= 0 && r < rows && c < cols &&
            kcol >= 0 && krow >= 0 && kcol < kernelcols && krow < kernelrows) {

            val k = ktile.getDouble(kcol, krow)
            if (isData(k)) {
              val o = tile.get(c, r)
              val w =
                if (isNoData(o)) {
                  k * z
                } else {
                  o + k * z
                }
              tile.set(c, r, w.toInt)
            }
          }

          c += 1
          kcol += 1
        }

        kcol = 0
        c = colmin
        r += 1
        krow += 1
      }
    }
  }

  /**
    * Given a column, row, and value, apply the kernel at the given
    * point.
    */
  def stampKernelDouble(col: Int, row: Int, z: Double) = {
    if(z == 0.0) {
      val o = tile.getDouble(col, row)
      tile.setDouble(col, row,
        if(isNoData(o)) 0.0
        else o
      )
    } else {

      val rowmin = row - kernelrows / 2
      val rowmax = math.min(row + kernelrows / 2 + 1, rows)

      val colmin = col - kernelcols / 2
      val colmax = math.min(col + kernelcols / 2 + 1, cols)

      var kcol = 0
      var krow = 0

      var r = rowmin
      var c = colmin
      while(r < rowmax) {
        while(c < colmax) {
          if (r >= 0 && c >= 0 && r < rows && c < cols &&
            kcol >= 0 && krow >= 0 && kcol < kernelcols && krow < kernelrows) {

            val k = ktile.getDouble(kcol, krow)
            if (isData(k)) {
              val o = tile.getDouble(c, r)
              val w =
                if (isNoData(o)) {
                  k * z
                } else {
                  o + k * z
                }
              tile.setDouble(c, r, w)
            }
          }

          c += 1
          kcol += 1
        }

        kcol = 0
        c = colmin
        r += 1
        krow += 1
      }
    }
  }

  def result = tile
}

/**
  * A [[KernelStamper]] for integer-valued tiles.
  */
case class IntKernelStamper(cellType: CellType, cols: Int, rows: Int, k: Kernel) extends KernelStamper {

  val ktile = k.tile
  val kernelcols = ktile.cols
  val kernelrows = ktile.rows

  val tile: MutableArrayTile = ArrayTile.empty(cellType, cols, rows)

  /**
    * Given a column, row, and value, apply the kernel at the given
    * point.
    */
  def stampKernel(col: Int, row: Int, z: Int) = {
    if(z == 0) {
      val o = tile.get(col, row)
      tile.set(col, row,
        if(isNoData(o)) 0
        else o
      )
    } else {

      val rowmin = row - kernelrows / 2
      val rowmax = math.min(row + kernelrows / 2 + 1, rows)

      val colmin = col - kernelcols / 2
      val colmax = math.min(col + kernelcols / 2 + 1, cols)

      var kcol = 0
      var krow = 0

      var r = rowmin
      var c = colmin
      while(r < rowmax) {
        while(c < colmax) {
          if (r >= 0 && c >= 0 && r < rows && c < cols &&
            kcol >= 0 && krow >= 0 && kcol < kernelcols && krow < kernelrows) {

            val k = ktile.get(kcol, krow)
            if (isData(k)) {
              val o = tile.get(c, r)
              val w =
                if (isNoData(o)) {
                  k * z
                } else {
                  o + k * z
                }
              tile.set(c, r, w.toInt)
            }
          }

          c += 1
          kcol += 1
        }

        kcol = 0
        c = colmin
        r += 1
        krow += 1
      }
    }
  }

  /**
    * Given a column, row, and value, apply the kernel at the given
    * point.
    */
  def stampKernelDouble(col: Int, row: Int, z: Double) = {
    if(z == 0.0) {
      val o = tile.getDouble(col, row)
      tile.setDouble(col, row,
        if(isNoData(o)) 0.0
        else o
      )
    } else {

      val rowmin = row - kernelrows / 2
      val rowmax = math.min(row + kernelrows / 2 + 1, rows)

      val colmin = col - kernelcols / 2
      val colmax = math.min(col + kernelcols / 2 + 1, cols)

      var kcol = 0
      var krow = 0

      var r = rowmin
      var c = colmin
      while(r < rowmax) {
        while(c < colmax) {
          if (r >= 0 && c >= 0 && r < rows && c < cols &&
            kcol >= 0 && krow >= 0 && kcol < kernelcols && krow < kernelrows) {

            val k = ktile.get(kcol, krow)
            if (isData(k)) {
              val o = tile.getDouble(c, r)
              val w =
                if (isNoData(o)) {
                  k * z
                } else {
                  o + k * z
                }
              tile.setDouble(c, r, w)
            }
          }

          c += 1
          kcol += 1
        }

        kcol = 0
        c = colmin
        r += 1
        krow += 1
      }
    }
  }

  def result = tile
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy