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

geotrellis.spark.io.index.zcurve.Z2.scala Maven / Gradle / Ivy

Go to download

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

The newest version!
package geotrellis.spark.io.index.zcurve

import geotrellis.spark.io.index.MergeQueue

class Z2(val z: Long) extends AnyVal {
  import Z2._

  def < (other: Z2) = z < other.z
  def > (other: Z2) = z > other.z

  def + (offset: Long) = new Z2(z + offset)
  def - (offset: Long) = new Z2(z - offset)

  def == (other: Z2) = other.z == z

  def decode: (Int, Int) = {    
    ( combine(z), combine(z>>1) )      
  }

  def dim(i: Int) = Z2.combine(z >> i)

  def mid(p: Z2): Z2 = {
    var ans: Z2  = new Z2(0)
    if (p.z < z)
      ans  = new Z2(p.z + (z - p.z)/2)
    else
      ans = new Z2(z + (p.z - z)/2)
    ans
  }

  def bitsToString = f"(${z.toBinaryString}%16s)(${dim(0).toBinaryString}%8s,${dim(1).toBinaryString}%8s)"
  override def toString = f"$z ${decode}"
}

object Z2 {  
  final val MAX_BITS = 31
  final val MAX_MASK = 0x7fffffff // ignore the sign bit, using it breaks < relationship
  final val MAX_DIM = 2

  /** insert 0 between every bit in value. Only first 31 bits can be considred. */
  def split(value: Long): Long = {
    var x: Long = value & MAX_MASK  
    x = (x ^ (x << 32)) & 0x00000000ffffffffL
    x = (x ^ (x << 16)) & 0x0000ffff0000ffffL
    x = (x ^ (x <<  8)) & 0x00ff00ff00ff00ffL // 11111111000000001111111100000000..
    x = (x ^ (x <<  4)) & 0x0f0f0f0f0f0f0f0fL // 1111000011110000
    x = (x ^ (x <<  2)) & 0x3333333333333333L // 11001100..
    x = (x ^ (x <<  1)) & 0x5555555555555555L // 1010...
    x
  }

  /** combine every other bit to form a value. Maximum value is 31 bits. */
  def combine(z: Long): Int = {
    var x = z & 0x5555555555555555L;
    x = (x ^ (x >>  1)) & 0x3333333333333333L;
    x = (x ^ (x >>  2)) & 0x0f0f0f0f0f0f0f0fL;
    x = (x ^ (x >>  4)) & 0x00ff00ff00ff00ffL;
    x = (x ^ (x >>  8)) & 0x0000ffff0000ffffL;
    x = (x ^ (x >> 16)) & 0x00000000ffffffffL;       
    x.toInt
  }

  /**
   * Bits of x and y will be encoded as ....y1x1y0x0
   */
  def apply(x: Int, y:  Int): Z2 = 
    new Z2(split(x) | split(y) << 1)  

  def unapply(z: Z2): Option[(Int, Int)] = 
    Some(z.decode)
  
  def zdivide(p: Z2, rmin: Z2, rmax: Z2): (Z2, Z2) = {
    val (litmax,bigmin) = zdiv(load, MAX_DIM)(p.z, rmin.z, rmax.z)
    (new Z2(litmax), new Z2(bigmin))
  }
  
  /** Loads either 1000... or 0111... into starting at given bit index of a given dimention */
  def load(target: Long, p: Long, bits: Int, dim: Int): Long = {    
    val mask = ~(Z2.split(MAX_MASK >> (MAX_BITS-bits)) << dim)
    val wiped = target & mask
    wiped | (split(p) << dim)
  }  

  /** Recurse down the quad-tree and report all z-ranges which are contained in the rectangle defined by the min and max points */
  def zranges(min: Z2, max: Z2): Seq[(Long, Long)] = {
    val mq = new MergeQueue
    val sr = Z2Range(min, max)

    var recCounter = 0
    var reportCounter = 0

    def _zranges(prefix: Long, offset: Int, quad: Long): Unit = {      
      recCounter += 1

      val min: Long = prefix | (quad << offset) // QR + 000..
      val max: Long = min | (1L << offset) - 1  // QR + 111..
      
      val qr = Z2Range(new Z2(min), new Z2(max))
      if (sr contains qr){                         // whole range matches, happy day
        mq += (qr.min.z, qr.max.z)
        reportCounter +=1
      } else if (offset > 0 && (sr overlaps qr)) { // some portion of this range are excluded
        _zranges(min, offset - MAX_DIM, 0)
        _zranges(min, offset - MAX_DIM, 1)
        _zranges(min, offset - MAX_DIM, 2)
        _zranges(min, offset - MAX_DIM, 3)        
        //let our children punt on each subrange
      }
    }

    var prefix: Long = 0
    var offset = MAX_BITS*MAX_DIM                
    _zranges(prefix, offset, 0) // the entire space
    mq.toSeq
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy