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

geotrellis.raster.reproject.RowTransform.scala Maven / Gradle / Ivy

package geotrellis.raster.reproject

import geotrellis.raster._
import geotrellis.vector.Extent
import geotrellis.proj4._

import spire.syntax.cfor._

object RowTransform {
  /** Transform each point exactly. Assumes user has taken care to make all array dimensions equal. */
  def exact(transform: Transform): RowTransform =
    { (srcX: Array[Double], srcY: Array[Double], destX: Array[Double], destY: Array[Double]) =>
      cfor(0)(_ < srcX.length, _ + 1) { i =>
        val (x, y) = transform(srcX(i), srcY(i))
        destX(i) = x
        destY(i) = y
      }
    }

  /** Computes an approximate transformation of a row.
    * 
    * Based on GDALApproxTransform. Assumes that the number of elements in the row is greater than 5.
    * Should only be used when the line being transformed is roughly linear.
    * 
    * @param transform                   The transformation that we are approximating
    * @param errorThreshold              maximum error measured in input pixels that is allowed
    *                                    in approximating the transformation (0.0 for exact calculations).
    * 
    * @note This algorithm does not garuntee all values will be within the error threshold of the exactly transformed values.
    *       In practice, as long as the line is roughly linear, it should be at least very close if over the error threshold away
    *       from the actual values.
    */
  def approximate(transform: Transform, errorThreshold: Double): RowTransform =
    if(errorThreshold == 0.0) {
      exact(transform)
    } else {
      { (srcX: Array[Double], srcY: Array[Double], destX: Array[Double], destY: Array[Double]) =>
        // Reproject first and last points
        val len = srcX.length
        val (xmin, ymin) = transform(srcX(0), srcY(0))
        val (xmax, ymax) = transform(srcX(len - 1), srcY(len - 1))
        destX(0) = xmin
        destY(0) = ymin
        destX(len - 1) = xmax
        destY(len - 1) = ymax

        computeApprox(transform, errorThreshold, srcX, srcY, destX, destY, 0, len)
      }
    }

  private def computeApprox(
    transform: Transform, errorThreshold: Double,
    srcX: Array[Double], srcY: Array[Double], destX: Array[Double], destY: Array[Double],
    startIndex: Int, length: Int): Unit = {

    if(length == 2) return

    val midPoint = startIndex + ((length - 1) / 2)
    val (xmid, ymid) = transform(srcX(midPoint), srcY(midPoint))

    destX(midPoint) = xmid
    destY(midPoint) = ymid

    if(length != 3) {
      val srcXMax = srcX(startIndex + length - 1)
      val srcXMin = srcX(startIndex)
      val xmax = destX(startIndex + length - 1)
      val xmin = destX(startIndex)
      val ymax = destY(startIndex + length - 1)
      val ymin = destY(startIndex)

      val dx = srcXMax - srcXMin
      val deltaX = (xmax - xmin) / dx
      val deltaY = (ymax - ymin) / dx

      val dxmid = srcX(midPoint) - srcXMin
      val error = math.abs( (xmin + (deltaX * dxmid)) - xmid) +
                  math.abs( (ymin + (deltaY * dxmid)) - ymid)

      if(error > errorThreshold) {
        // Compute against the two halves of the row
        computeApprox(transform, errorThreshold, srcX, srcY, destX, destY,
          startIndex, midPoint - startIndex + 1)
        computeApprox(transform, errorThreshold, srcX, srcY, destX, destY,
          midPoint, startIndex + length - midPoint)
      } else {
        // Fill out the values based on the linear interpolation
        cfor(startIndex + 1)(_ < startIndex + length - 1, _ + 1) { i =>
          if(i != midPoint) {
            val dxi = srcX(i) - srcXMin
            destX(i) = xmin + (deltaX * dxi)
            destY(i) = ymin + (deltaY * dxi)
          }
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy