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

edu.ucr.cs.bdlab.beast.geolite.ITileSerializer.scala Maven / Gradle / Ivy

/*
 * Copyright 2022 University of California, Riverside
 *
 * 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 edu.ucr.cs.bdlab.beast.geolite

import com.esotericsoftware.kryo.io.{Input, Output}
import com.esotericsoftware.kryo.{Kryo, Serializer}
import edu.ucr.cs.bdlab.beast.geolite.ITileSerializer.{numberToSparkType, sparkTypeToNumber}
import edu.ucr.cs.bdlab.beast.util.{BitArray, BitOutputStream, LZWCodec, LZWOutputStream}
import org.apache.commons.io.output.ByteArrayOutputStream
import org.apache.spark.sql.types.{ByteType, DataType, DoubleType, FloatType, IntegerType, LongType, NullType, ShortType}

import java.io.DataOutputStream

/**
 * A Kryo serializer for all tiles.
 */
class ITileSerializer[T] extends Serializer[ITile[T]]{
  override def write(kryo: Kryo, output: Output, tile: ITile[T]): Unit = {
    // A general method to write any tile
    // Write tile identifier and metadata
    output.writeInt(tile.tileID)
    kryo.writeObject(output, tile.rasterMetadata)
    // Write pixel type and number of components
    output.writeInt(tile.numComponents)
    output.writeInt(sparkTypeToNumber(tile.componentType))
    val bytesPerPixel = tile.componentType.defaultSize * tile.numComponents
    // Write pixel data
    val pixelExists = new Array[Byte]((tile.tileWidth * tile.tileHeight + 7) / 8)

    val dummyData: Array[Byte] = Array.fill[Byte](bytesPerPixel)(0)
    val baos2 = new ByteArrayOutputStream()
    val zos2 = new LZWOutputStream(baos2)
    val pixelData = new DataOutputStream(zos2)
    val dataWriter: (DataOutputStream, T) => Unit = if (tile.numComponents == 1)
      ITileSerializer.dataWriters(sparkTypeToNumber(tile.componentType)).asInstanceOf[(DataOutputStream, T) => Unit]
    else
      ITileSerializer.dataWriters(0x10 | sparkTypeToNumber(tile.componentType)).asInstanceOf[(DataOutputStream, T) => Unit]

    var i = 0
    for (y <- tile.y1 to tile.y2; x <- tile.x1 to tile.x2) {
      if (tile.isDefined(x, y)) {
        BitArray.setBit(pixelExists, i)
        dataWriter(pixelData, tile.getPixelValue(x, y))
      } else {
        pixelData.write(dummyData)
      }
      i += 1
    }

    pixelData.close()

    val data1 = LZWCodec.encode(pixelExists)
    output.writeInt(data1.length)
    output.write(data1)
    val data2 = baos2.toByteArray
    output.writeInt(data2.length)
    output.write(data2)
  }

  override def read(kryo: Kryo, input: Input, tileClass: Class[ITile[T]]): ITile[T] = {
    val tileID = input.readInt()
    val metadata = kryo.readObject(input, classOf[RasterMetadata])
    val numComponents = input.readInt()
    val componentType = numberToSparkType(input.readInt())
    val pixelExistsSize = input.readInt()
    val pixelExists = input.readBytes(pixelExistsSize)
    val pixelDataSize = input.readInt()
    val pixelData = input.readBytes(pixelDataSize)
    new DefaultReadOnlyTile[T](tileID, metadata, numComponents, componentType,
      pixelExists, pixelData, DefaultReadOnlyTile.LZW)
  }
}

object ITileSerializer {
  val sparkTypeToNumber: Map[DataType, Int] = Map(
    (ByteType, 1), (ShortType, 2), (IntegerType, 3), (LongType, 4), (FloatType, 5), (DoubleType, 6)
  )

  val numberToSparkType: Array[DataType] = Array(
    NullType, ByteType, ShortType, IntegerType, LongType, FloatType, DoubleType
  )

  val dataWriters: Array[(DataOutputStream, _<:Any) => Unit] = new Array(32)
  dataWriters(0x01) = (out: DataOutputStream, x: Byte) => out.writeByte(x)
  dataWriters(0x02) = (out: DataOutputStream, x: Short) => out.writeShort(x)
  dataWriters(0x03) = (out: DataOutputStream, x: Int) => out.writeInt(x)
  dataWriters(0x04) = (out: DataOutputStream, x: Long) => out.writeLong(x)
  dataWriters(0x05) = (out: DataOutputStream, x: Float) => out.writeFloat(x)
  dataWriters(0x06) = (out: DataOutputStream, x: Double) => out.writeDouble(x)
  dataWriters(0x11) = (out: DataOutputStream, xs: Array[Byte]) => out.write(xs)
  dataWriters(0x12) = (out: DataOutputStream, xs: Array[Short]) => for (x <- xs) out.writeShort(x)
  dataWriters(0x13) = (out: DataOutputStream, xs: Array[Int]) => for (x <- xs) out.writeInt(x)
  dataWriters(0x14) = (out: DataOutputStream, xs: Array[Long]) => for (x <- xs) out.writeLong(x)
  dataWriters(0x15) = (out: DataOutputStream, xs: Array[Float]) => for (x <- xs) out.writeFloat(x)
  dataWriters(0x16) = (out: DataOutputStream, xs: Array[Double]) => for (x <- xs) out.writeDouble(x)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy