com.astrolabsoftware.sparkfits.FitsHdu.scala Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2018 AstroLab Software
*
* 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 com.astrolabsoftware.sparkfits
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import org.apache.spark.sql.types.StructField
import com.astrolabsoftware.sparkfits.FitsLib.shortStringValue
object FitsHdu {
/**
* Trait containing generic informations concerning HDU informations.
* This includes for example number of rows, size of a row,
* number of columns, types of elements, and methods to access elements.
*
*/
trait HDU {
// 8 bits in one byte
val BYTE_SIZE = 8
/**
* Check whether the HDU is implemented in the library.
* Must be set for all extensions of HDU trait.
*
* @return (Boolean)
*/
def implemented: Boolean
/**
* Generic method to return the number of rows from the header information.
* To be implemented in specific HDU.
*
* @param keyValues : (Map[String, String])
* (Key, Values) from the header (see parseHeader)
* @return (Long) Number of rows in the data HDU.
*
*/
def getNRows(keyValues: Map[String, String]) : Long
/**
* Generic method to get the size of one row (bytes) from the header
* information.
* Must be implemented for all HDU extensions.
*
* @param keyValues : (Map[String, String])
* (Key, Values) from the header (see parseHeader)
* @return (Long) Size in bytes of one row.
*
*/
def getSizeRowBytes(keyValues: Map[String, String]) : Int
/**
* Generic method to get the number of columns from the header information.
* Must be implemented for all extensions of HDU.
*
* @param keyValues : (Map[String, String])
* (Key, Values) from the header (see parseHeader)
* @return (Long) Number of columns in the data HDU.
*
*/
def getNCols(keyValues : Map[String, String]) : Long
/**
* Generic method to get the types of column elements from the header
* information.
* Must be implemented for all extensions of HDU.
*
* @param keyValues : (Map[String, String])
* (Key, Values) from the header (see parseHeader)
* @return (List[String]) Types of elements of columns.
*
*/
def getColTypes(keyValues : Map[String, String]): List[String]
/**
* Generic method to convert header information into StructField used to
* build the DataFrame schema.
* Must be implemented for all extensions of HDU.
*
* @return (List[StructField]) List of StructField containing name and
* types of columns.
*/
def listOfStruct : List[StructField]
/**
* Generic method to decode the rows of the data block.
* Must be implemented for all extensions of HDU.
*
* @param buf : (Array[Bytes])
* Array of Bytes describing one row.
* @return (List[Any]) The decoded row containing primitives.
*
*/
def getRow(buf: Array[Byte]): List[Any]
/**
* Convert one array of bytes corresponding to one element of
* the table into its primitive type.
*
* @param subbuf : (Array[Byte])
* Array of byte describing one element of the table.
* @param fitstype : (String)
* The type of this table element according to the header.
* @return the table element converted from binary.
*
*/
def getElementFromBuffer(subbuf : Array[Byte], fitstype : String) : Any = {
// Grab the type of the element
val shortType = shortStringValue{fitstype}
shortType match {
// Single 16-bit Integer
case x if shortType == "I" => {
ByteBuffer.wrap(subbuf, 0, 2).getShort()
}
// 1 element vector is considered as a scalar
case x if shortType == "1I" => {
ByteBuffer.wrap(subbuf, 0, 2).getShort()
}
// Array of 16-bit Integers
case x if shortType.contains("I") => {
val num = x.slice(0, x.length - 1).toInt
val array = Array.ofDim[Short](num)
ByteBuffer.wrap(subbuf, 0, num*2).asShortBuffer().get(array)
array
}
// Single 32-bit Integer
case x if shortType == "J" => {
ByteBuffer.wrap(subbuf, 0, 4).getInt()
}
// 1 element vector is considered as a scalar
case x if shortType == "1J" => {
ByteBuffer.wrap(subbuf, 0, 4).getInt()
}
// Array of 32-bit Integers
case x if shortType.contains("J") => {
val num = x.slice(0, x.length - 1).toInt
val array = Array.ofDim[Int](num)
ByteBuffer.wrap(subbuf, 0, num*4).asIntBuffer().get(array)
array
}
// Single 64-bit Integer
case x if shortType == "K" => {
ByteBuffer.wrap(subbuf, 0, 8).getLong()
}
// 1 element vector is considered as a scalar
case x if shortType == "1K" => {
ByteBuffer.wrap(subbuf, 0, 8).getLong()
}
// Array of 64-bit Integers
case x if shortType.contains("K") => {
val num = x.slice(0, x.length - 1).toInt
val array = Array.ofDim[Long](num)
ByteBuffer.wrap(subbuf, 0, num*8).asLongBuffer().get(array)
array
}
// Single precision floating-point
case x if shortType == "E" => {
ByteBuffer.wrap(subbuf, 0, 4).getFloat()
}
// 1 element vector is considered as a scalar
case x if shortType == "1E" => {
ByteBuffer.wrap(subbuf, 0, 4).getFloat()
}
// Array of Single precision floating-points
case x if shortType.contains("E") => {
val num = x.slice(0, x.length - 1).toInt
val array = Array.ofDim[Float](num)
ByteBuffer.wrap(subbuf, 0, num*4).asFloatBuffer().get(array)
array
}
// Double precision floating-point
case x if shortType == "D" => {
ByteBuffer.wrap(subbuf, 0, 8).getDouble()
}
// 1 element vector is considered as a scalar
case x if shortType == "1D" => {
ByteBuffer.wrap(subbuf, 0, 8).getDouble()
}
// Array of Double precision floating-points
case x if shortType.contains("D") => {
val num = x.slice(0, x.length - 1).toInt
val array = Array.ofDim[Double](num)
ByteBuffer.wrap(subbuf, 0, num*8).asDoubleBuffer().get(array)
array
}
// Boolean
case x if shortType.contains("L") => {
// 1 Byte containing the ASCII char T(rue) or F(alse).
subbuf(0).toChar == 'T'
}
// ISSUE-59: 8-bit (1 byte) unsigned byte
case x if shortType.contains("B") => {
ByteBuffer.wrap(subbuf, 0, 1).get()
}
// Number of bits
case x if shortType.endsWith("X") => {
List(subbuf)
}
// Chain of characters
case x if shortType.endsWith("A") => {
// Example 20A means string on 20 bytes
new String(subbuf, StandardCharsets.UTF_8).trim()
}
case _ => {
println(s"""
FitsHdu.getElementFromBuffer> Cannot infer size of type
$shortType from the header! See getElementFromBuffer
""")
0
}
}
}
}
/**
* Generic class extending Infos concerning dummy HDU (e.g. not implemented).
* Set all variables and methods to null/0/false.
*/
case class AnyHDU() extends HDU {
/** Empty HDU not implemented. */
override def implemented: Boolean = {false}
/** Return no row */
override def getNRows(keyValues: Map[String, String]) : Long = {0L}
/** Rows have size zero */
override def getSizeRowBytes(keyValues: Map[String, String]) : Int = {0}
/** Return no columns */
override def getNCols(keyValues : Map[String, String]) : Long = {0L}
/** Elements have no types */
override def getColTypes(keyValues : Map[String, String]): List[String] = {null}
/** Return no schema structure */
override def listOfStruct : List[StructField] = {null}
/** Return null row */
override def getRow(buf: Array[Byte]): List[Any] = {null}
/** Return null element */
override def getElementFromBuffer(subbuf : Array[Byte], fitstype : String) : Any = {null}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy