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

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

The newest version!
package geotrellis.raster

import geotrellis._

import spire.syntax.cfor._

/**
 * RasterData based on an Array[Byte] as a bitmask; values are 0 and 1.
 * Thus, there are 8 boolean (0/1) values per byte in the array. For example,
 * Array(11, 9) corresponds to (0 0 0 0 1 0 1 1), (0 0 0 0 1 0 0 1) which
 * means that we have 5 cells set to 1 and 11 cells set to 0.
 *
 * Note that unlike the other array-based raster data objects we need to be
 * explicitly told our size, since length=7 and length=8 will both need to
 * allocate an Array[Byte] with length=1.
 */
final case class BitArrayRasterData(array: Array[Byte], cols: Int, rows: Int)
  extends MutableRasterData with IntBasedArray {
  val size = cols * rows

  // i >> 3 is the same as i / 8 but faster
  // i & 7 is the same as i % 8 but faster
  // i & 1 is the same as i % 2 but faster
  // ~3 -> -4, that is 00000011 -> 11111100
  // 3 | 9 -> 11, that is 00000011 | 00001001 -> 00001011
  // 3 & 9 -> 1,  that is 00000011 & 00001001 -> 00000001
  // 3 ^ 9 -> 10, that is 00000011 ^ 00001001 -> 00001010

  if (array.length != (size + 7) / 8) {
    sys.error(s"BitArrayRasterData array length must be ${(size + 7) / 8}, was ${array.length}")
  }
  def getType = TypeBit
  def alloc(cols: Int, rows: Int) = BitArrayRasterData.ofDim(cols, rows)
  def length = size
  def apply(i: Int) = ((array(i >> 3) >> (i & 7)) & 1).asInstanceOf[Int]
  def update(i: Int, z: Int): Unit = {
    val div = i >> 3
    if ((z & 1) == 0) {
      // unset the nth bit
      array(div) = (array(div) & ~(1 << (i & 7))).toByte
    } else {
      // set the nth bit
      array(div) = (array(div) | (1 << (i & 7))).toByte
    }
  }
  def copy = BitArrayRasterData(array.clone, cols, rows)

  override def map(f: Int => Int) = {
    val f0 = f(0) & 1
    val f1 = f(1) & 1

    if (f0 == 0 && f1 == 0) {
      BitConstant(false, cols, rows)
    } else if (f0 == 1 && f1 == 1) {
      BitConstant(true, cols, rows)
    } else if (f0 == 0 && f1 == 1) {
      // same data as we have now
      this
    } else {
      // inverse (complement) of what we have now
      val clone = array.clone
      var i = 0
      val len = array.length
      while(i < len) { clone(i) = (array(i) ^ -1).toByte ; i += 1 }
      BitArrayRasterData(clone, cols, rows)
    }
  }

  override def mapDouble(f: Double => Double) = map(z => d2i(f(i2d(z))))

  def toArrayByte: Array[Byte] = array

  def warp(current:RasterExtent,target:RasterExtent):RasterData = {
    val warped = Array.ofDim[Byte]((target.cols*target.rows+7)/8).fill(byteNODATA)
    Warp(current,target,new BitWarpAssign(array,warped))
    BitArrayRasterData(warped, target.cols, target.rows)
  }
}

object BitArrayRasterData {
  def ofDim(cols: Int, rows: Int) = new BitArrayRasterData(Array.ofDim[Byte](((cols * rows) + 7) / 8), cols, rows)
  def empty(cols: Int, rows: Int) = ofDim(cols, rows)

  def fromArrayByte(bytes: Array[Byte], cols: Int, rows: Int) = BitArrayRasterData(bytes, cols, rows)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy