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

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

Go to download

GeoTrellis is an open source geographic data processing engine for high performance applications.

The newest version!
/*
 * 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.vector.Extent

import spire.syntax.cfor._


/**
  * [[ArrayTile]] provides access and update to the grid data of a
  * raster.  Designed to be a near drop-in replacement for Array in
  * many cases.
  */
trait ArrayTile extends Tile with Serializable {

  /**
    * Return the [[ArrayTile]] equivalent of this ArrayTile.
    *
    * @return  The object on which the method was invoked
    */
  def toArrayTile = this

  /**
    * Returns a [[Tile]] equivalent to this [[ArrayTile]], except with
    * cells of the given type.
    *
    * @param   cellType  The type of cells that the result should have
    * @return            The new Tile
    */
  def convert(targetCellType: CellType): Tile = {
    val tile = ArrayTile.alloc(targetCellType, cols, rows)

    if(!cellType.isFloatingPoint) {
      cfor(0)(_ < rows, _ + 1) { row =>
        cfor(0)(_ < cols, _ + 1) { col =>
          tile.set(col, row, get(col, row))
        }
      }
    } else {
      cfor(0)(_ < rows, _ + 1) { row =>
        cfor(0)(_ < cols, _ + 1) { col =>
          tile.setDouble(col, row, getDouble(col, row))
        }
      }
    }

    tile
  }

  /**
    * Execute a function on each cell of the [[ArrayTile]].
    *
    * @param  f  A function from Int to Unit.  Presumably, the function is executed for side-effects.
    */
  def foreach(f: Int => Unit): Unit = {
    val len = size
    var i = 0
    while (i < len) {
      f(apply(i))
      i += 1
    }
  }

  /**
    * Execute a function on each cell of the [[ArrayTile]].
    *
    * @param  f  A function from Double to Unit.  Presumably, the function is executed for side-effects.
    */
  def foreachDouble(f: Double => Unit): Unit = {
    val len = size
    var i = 0
    while (i < len) {
      f(applyDouble(i))
      i += 1
    }
  }

  /**
    * Execute an [[IntTileVisitor]] at each cell of the [[ArrayTile]].
    *
    * @param  visitor  An IntTileVisitor
    */
  def foreachIntVisitor(visitor: IntTileVisitor): Unit = {
    cfor(0)(_ < rows, _ + 1) { row =>
      cfor(0)(_ < cols, _ + 1) { col =>
        visitor(col, row, get(col, row))
      }
    }
  }

  /**
    * Execute an [[DoubleTileVisitor]] at each cell of the [[ArrayTile]].
    *
    * @param  visitor  A DoubleTileVisitor
    */
  def foreachDoubleVisitor(visitor: DoubleTileVisitor): Unit = {
    cfor(0)(_ < rows, _ + 1) { row =>
      cfor(0)(_ < cols, _ + 1) { col =>
        visitor(col, row, getDouble(col, row))
      }
    }
  }

  /**
    * Map each cell in the given raster to a new one, using the given
    * function.
    *
    * @param   f  A function from Int to Int, executed at each point of the tile
    * @return     The result, a [[Tile]]
    */
  def map(f: Int=>Int): Tile = {
    val output = ArrayTile.alloc(cellType, cols, rows)
    var i = 0
    val len = size
    while (i < len) {
      output(i) = f(apply(i))
      i += 1
    }
    output
  }

  /**
    * Map each cell in the given raster to a new one, using the given
    * function.
    *
    * @param   f  A function from Double to Double, executed at each point of the tile
    * @return     The result, a [[Tile]]
    */
  def mapDouble(f: Double => Double): Tile = {
    val len = size
    val tile = ArrayTile.alloc(cellType, cols, rows)
    var i = 0
    while (i < len) {
      tile.updateDouble(i, f(applyDouble(i)))
      i += 1
    }
    tile
  }

  /**
    * Map an [[IntTileMapper]] over the present tile.
    *
    * @param   mapper  The mapper
    * @return          The result, a [[Tile]]
    */
  def mapIntMapper(mapper: IntTileMapper): Tile = {
    val tile = ArrayTile.alloc(cellType, cols, rows)
    cfor(0)(_ < rows, _ + 1) { row =>
      cfor(0)(_ < cols, _ + 1) { col =>
        tile.set(col, row, mapper(col, row, get(col, row)))
      }
    }
    tile
  }

  /**
    * Map an [[DoubleTileMapper]] over the present tile.
    *
    * @param   mapper  The mapper
    * @return          The result, a [[Tile]]
    */
  def mapDoubleMapper(mapper: DoubleTileMapper): Tile = {
    val tile = ArrayTile.alloc(cellType, cols, rows)
    cfor(0)(_ < rows, _ + 1) { row =>
      cfor(0)(_ < cols, _ + 1) { col =>
        tile.setDouble(col, row, mapper(col, row, getDouble(col, row)))
      }
    }
    tile
  }

  /**
    * Combine two [[ArrayTile]]s' cells into new cells using the given
    * integer function. For every (x, y) cell coordinate, get each of
    * the ArrayTiles' integer values, map them to a new value, and
    * assign it to the output's (x, y) cell.
    *
    * @param   other  The other ArrayTile
    * @param   f      A function from (Int, Int) to Int
    * @return         The result, an ArrayTile
    */
  def combine(other: ArrayTile)(f: (Int, Int) => Int): ArrayTile = {
    (this, other).assertEqualDimensions

    val output = ArrayTile.alloc(cellType.union(other.cellType), cols, rows)
    var i = 0
    val len = size
    while (i < len) {
      output(i) = f(apply(i), other(i))
      i += 1
    }
    output
  }

  /**
    * Combine the cells of an [[ArrayTile]] and a [[Tile]] into a new
    * Tile using the given function. For every (x, y) cell coordinate,
    * get each of the Tiles' integer value, map them to a new value,
    * and assign it to the output's (x, y) cell.
    *
    * @param   other  The other Tile
    * @param   f      A function from (Int, Int) to Int
    * @return         The result, an Tile
    */
  def combine(other: Tile)(f: (Int, Int) => Int): Tile = {
    other match {
      case ar: ArrayTile =>
        combine(ar)(f)
      case ct: ConstantTile =>
        ct.combine(this)(f)
      case ct: CompositeTile =>
        ct.combine(this)((z1, z2) => f(z2, z1))
      case ct: CroppedTile =>
        ct.combine(this)((z1, z2) => f(z2, z1))
    }
  }

  /**
    * Combine two [[ArrayTile]]s' cells into new cells using the given
    * double function. For every (x, y) cell coordinate, get each of
    * the ArrayTiles' double values, map them to a new value, and
    * assign it to the output's (x, y) cell.
    *
    * @param   other  The other ArrayTile
    * @param   f      A function from (Double, Double) to Double
    * @return         The result, an ArrayTile
    */
  def combineDouble(other: ArrayTile)(f: (Double, Double) => Double): ArrayTile = {
    (this, other).assertEqualDimensions

    val output = ArrayTile.alloc(cellType.union(other.cellType), cols, rows)
    var i = 0
    val len = size
    while (i < len) {
      output.updateDouble(i, f(applyDouble(i), other.applyDouble(i)))
      i += 1
    }
    output
  }

  /**
    * Combine the cells of an [[ArrayTile]] and a [[Tile]] into a new
    * Tile using the given function. For every (x, y) cell coordinate,
    * get tiles' double values, map them to a new value, and assign it
    * to the output's (x, y) cell.
    *
    * @param   other  The other Tile
    * @param   f      A function from (Double, Double) to Double
    * @return         The result, an Tile
    */
  def combineDouble(other: Tile)(f: (Double, Double) => Double): Tile = {
    other match {
      case ar: ArrayTile =>
        combineDouble(ar)(f)
      case ct: ConstantTile =>
        ct.combineDouble(this)(f)
      case ct: CompositeTile =>
        ct.combineDouble(this)((z1, z2) => f(z2, z1))
    }
  }

  /**
    * Check for equality between the present [[ArrayTile]] and any
    * other object.
    *
    * @param   other  The other object
    * @return         A boolean
    */
  override def equals(other: Any): Boolean = other match {
    case r: ArrayTile => {
      if (r == null) return false
      val len = size
      if (len != r.size) return false
      var i = 0
      while (i < len) {
        if (apply(i) != r(i)) return false
        i += 1
      }
      true
    }
    case _ => false
  }

  /**
    * Fetch the datum at the given index in the array.
    *
    * @param   i  The index
    * @return     The Int datum found at the index
    */
  def apply(i: Int): Int

  /**
    * Fetch the datum at the given index in the array.
    *
    * @param   i  The index
    * @return     The Double datum found at the index
    */
  def applyDouble(i: Int): Double

  /**
    * Fetch the datum at the given column and row of the
    * [[ArrayTile]].
    *
    * @param   col  The column
    * @param   row  The row
    * @return       The Int datum found at the given location
    */
  def get(col: Int, row: Int) = apply(row * cols + col)

  /**
    * Fetch the datum at the given column and row of the
    * [[ArrayTile]].
    *
    * @param   col  The column
    * @param   row  The row
    * @return       The Double datum found at the given location
    */
  def getDouble(col: Int, row: Int) = applyDouble(row * cols + col)

  /**
    * Return a copy of the present [[ArrayTile]].
    *
    * @return  The copy
    */
  def copy: ArrayTile

  /**
    * Return the underlying array of this [[ArrayTile]] as a list.
    *
    * @return  The list
    */
  def toList = toArray.toList

  /**
    * Return the under-laying array of this [[ArrayTile]] as a list.
    *
    * @return  The list
    */
  def toListDouble = toArrayDouble.toList

  /**
    * Return a copy of the underlying array of the present
    * [[ArrayTile]].
    *
    * @return  The copy as an Array[Int]
    */
  def toArray: Array[Int] = {
    val len = size
    val arr = Array.ofDim[Int](len)
    var i = 0
    while (i < len) {
      arr(i) = apply(i)
      i += 1
    }
    arr
  }

  /**
    * Return a copy of the underlying array of the present
    * [[ArrayTile]].
    *
    * @return  The copy as an Array[Double]
    */
  def toArrayDouble: Array[Double] = {
    val len = size
    val arr = Array.ofDim[Double](len)
    var i = 0
    while (i < len) {
      arr(i) = applyDouble(i)
      i += 1
    }
    arr
  }

  /**
    * Convert the present [[ArrayTile]] to an array of bytes and
    * return that array.
    *
    * @return  An array of bytes
    */
  def toBytes: Array[Byte]
}

/**
  * An object housing apply methods which produce [[ArrayTile]]s.
  */
object ArrayTile {

  /**
    * Allocate a new [[MutableArrayTile]].
    *
    * @param   t     The [[CellType]] of the new [[MutableArrayTile]]
    * @param   cols  The number of columns that the new [[MutableArrayTile]] should have
    * @param   rows  The number of rows that the new [[MutableArrayTile]] should have
    * @return        The new [[MutableArrayTile]]
    */
  def alloc(t: CellType, cols: Int, rows: Int): MutableArrayTile =
    t match {
      case _: BitCells => BitArrayTile.ofDim(cols, rows)
      case ct: ByteCells => ByteArrayTile.ofDim(cols, rows, ct)
      case ct: UByteCells => UByteArrayTile.ofDim(cols, rows, ct)
      case ct: ShortCells => ShortArrayTile.ofDim(cols, rows, ct)
      case ct: UShortCells => UShortArrayTile.ofDim(cols, rows, ct)
      case ct: IntCells => IntArrayTile.ofDim(cols, rows, ct)
      case ct: FloatCells => FloatArrayTile.ofDim(cols, rows, ct)
      case ct: DoubleCells => DoubleArrayTile.ofDim(cols, rows, ct)
    }

  /**
    * Create a new, empty [[MutableArrayTile]].
    *
    * @param   t     The [[CellType]] of the new [[MutableArrayTile]]
    * @param   cols  The number of columns that the new [[MutableArrayTile]] should have
    * @param   rows  The number of rows that the new [[MutableArrayTile]] should have
    * @return        The new [[MutableArrayTile]]
    */
  def empty(t: CellType, cols: Int, rows: Int): MutableArrayTile =
    t match {
      case _: BitCells => BitArrayTile.empty(cols, rows)
      case ct: ByteCells => ByteArrayTile.empty(cols, rows, ct)
      case ct: UByteCells => UByteArrayTile.empty(cols, rows, ct)
      case ct: ShortCells => ShortArrayTile.empty(cols, rows, ct)
      case ct: UShortCells => UShortArrayTile.empty(cols, rows, ct)
      case ct: IntCells => IntArrayTile.empty(cols, rows, ct)
      case ct: FloatCells => FloatArrayTile.empty(cols, rows, ct)
      case ct: DoubleCells => DoubleArrayTile.empty(cols, rows, ct)
    }

  /**
    * Create a [[MutableArrayTile]] from a byte array.
    *
    * @param   bytes  The array of bytes
    * @param   t      The [[CellType]] of the new [[MutableArrayTile]]
    * @param   cols   The number of columns that the new [[MutableArrayTile]] should have
    * @param   rows   The number of rows that the new [[MutableArrayTile]] should have
    * @return         The new [[MutableArrayTile]]
    */
  def fromBytes(bytes: Array[Byte], t: CellType, cols: Int, rows: Int): MutableArrayTile =
    t match {
      case _: BitCells => BitArrayTile.fromBytes(bytes, cols, rows)
      case ct: ByteCells => ByteArrayTile.fromBytes(bytes, cols, rows, ct)
      case ct: UByteCells => UByteArrayTile.fromBytes(bytes, cols, rows, ct)
      case ct: ShortCells => ShortArrayTile.fromBytes(bytes, cols, rows, ct)
      case ct: UShortCells => UShortArrayTile.fromBytes(bytes, cols, rows, ct)
      case ct: IntCells => IntArrayTile.fromBytes(bytes, cols, rows, ct)
      case ct: FloatCells => FloatArrayTile.fromBytes(bytes, cols, rows, ct)
      case ct: DoubleCells => DoubleArrayTile.fromBytes(bytes, cols, rows, ct)
    }

  /**
    * Create a new [[ByteConstantNoDataArrayTile]] from an array of
    * Bytes.
    *
    * @param   arr   The array of Bytes
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Byte], cols: Int, rows: Int): ByteConstantNoDataArrayTile = new ByteConstantNoDataArrayTile(arr, cols, rows)

  /**
    * Create a new [[ShortConstantNoDataArrayTile]] from an array of
    * Shorts.
    *
    * @param   arr   The array of Shorts
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Short], cols: Int, rows: Int): ShortConstantNoDataArrayTile = new ShortConstantNoDataArrayTile(arr, cols, rows)

  /**
    * Create a new [[IntConstantNoDataArrayTile]] from an array of
    * integers.
    *
    * @param   arr   The array of integers
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Int], cols: Int, rows: Int): IntConstantNoDataArrayTile = new IntConstantNoDataArrayTile(arr, cols, rows)

  /**
    * Create a new [[FloatConstantNoDataArrayTile]] from an array of
    * Floats.
    *
    * @param   arr   The array of Floats
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Float], cols: Int, rows: Int): FloatConstantNoDataArrayTile = new FloatConstantNoDataArrayTile(arr, cols, rows)

  /**
    * Create a new [[DoubleConstantNoDataArrayTile]] from an array of
    * Doubles.
    *
    * @param   arr   The array of Doubles
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Double], cols: Int, rows: Int): DoubleConstantNoDataArrayTile = new DoubleConstantNoDataArrayTile(arr, cols, rows)
}

/**
  * An object housing apply methods which produce [[RawArrayTile]]s.
  */
object RawArrayTile {

  /**
    * Create a new [[ByteRawArrayTile]] from an array of Bytes.
    *
    * @param   arr   The array of Bytes
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Byte], cols: Int, rows: Int): ByteRawArrayTile = new ByteRawArrayTile(arr, cols, rows)

  /**
    * Create a new [[ShortRawArrayTile]] from an array of Shorts.
    *
    * @param   arr   The array of Shorts
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Short], cols: Int, rows: Int): ShortRawArrayTile = new ShortRawArrayTile(arr, cols, rows)

  /**
    * Create a new [[IntRawArrayTile]] from an array of integers.
    *
    * @param   arr   The array of integers
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Int], cols: Int, rows: Int): IntRawArrayTile = new IntRawArrayTile(arr, cols, rows)

  /**
    * Create a new [[FloatRawArrayTile]] from an array of Floats.
    *
    * @param   arr   The array of Floats
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Float], cols: Int, rows: Int): FloatRawArrayTile = new FloatRawArrayTile(arr, cols, rows)

  /**
    * Create a new [[DoubleRawArrayTile]] from an array of Doubles.
    *
    * @param   arr   The array of Doubles
    * @param   cols  The number of columns in the new tile
    * @param   rows  The number of rows in the new tile
    * @return        The newly-created tile
    */
  def apply(arr: Array[Double], cols: Int, rows: Int): DoubleRawArrayTile = new DoubleRawArrayTile(arr, cols, rows)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy