Skip to content

Commit

Permalink
Merge pull request #2142 from metasim/celltype-name
Browse files Browse the repository at this point in the history
Reorganized handling of `CellType.name`
  • Loading branch information
lossyrob authored Apr 20, 2017
2 parents 54f7cde + 6c55044 commit bd30bec
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 63 deletions.
19 changes: 14 additions & 5 deletions raster-test/src/test/scala/geotrellis/raster/CellTypeSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,18 @@ class CellTypeSpec extends FunSpec with Matchers {
FloatCellType.intersect(IntCellType) should be (IntCellType)
}
def roundTrip(ct: CellType) {
val str = ct.toString
val ctp = CellType.fromString(str)
ctp should be (ct)
{
// Updated behavior.
val str = ct.name
val ctp = CellType.fromName(str)
ctp should be (ct)
}
{
// Tests backward compatibility.
val str = ct.toString
val ctp = CellType.fromString(str)
ctp should be (ct)
}
}

it("should serialize float64ud123") {
Expand Down Expand Up @@ -78,7 +87,7 @@ class CellTypeSpec extends FunSpec with Matchers {
}

it("should read float64udNaN as float64") {
CellType.fromString(DoubleUserDefinedNoDataCellType(Double.NaN).toString) should be (DoubleConstantNoDataCellType)
CellType.fromName(DoubleUserDefinedNoDataCellType(Double.NaN).toString) should be (DoubleConstantNoDataCellType)
}

//----
Expand Down Expand Up @@ -115,7 +124,7 @@ class CellTypeSpec extends FunSpec with Matchers {
}

it("should read float32udNaN as float32") {
CellType.fromString(FloatUserDefinedNoDataCellType(Float.NaN).toString) should be (FloatConstantNoDataCellType)
CellType.fromName(FloatUserDefinedNoDataCellType(Float.NaN).toString) should be (FloatConstantNoDataCellType)
}

}
Expand Down
174 changes: 116 additions & 58 deletions raster/src/main/scala/geotrellis/raster/CellType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package geotrellis.raster

import scala.util.matching.Regex
import java.awt.image.DataBuffer
import java.lang.IllegalArgumentException

// Note: CellType defined in package object as
// `type CellType = DataType with NoDataHandling`
Expand All @@ -30,7 +29,7 @@ import java.lang.IllegalArgumentException
sealed abstract class DataType extends Serializable { self: CellType =>
val bits: Int
val isFloatingPoint: Boolean
val name: String
def name: String = CellType.toName(self)

/** Determine if two [[CellType]] instances have equal [[DataType]] component */
def equalDataType(other: DataType): Boolean
Expand All @@ -56,7 +55,7 @@ sealed abstract class DataType extends Serializable { self: CellType =>
*
* @return Bytes per sample.
*/
def bytes = bits / 8
def bytes: Int = bits / 8

/**
* Union only checks to see that the correct bitsize and int vs
Expand All @@ -73,7 +72,7 @@ sealed abstract class DataType extends Serializable { self: CellType =>
* @param other The other cell type
* @return The union of this data type and the other cell type
*/
def union(other: CellType) =
def union(other: CellType): CellType =
if (bits < other.bits)
other
else if (bits > other.bits)
Expand All @@ -90,7 +89,7 @@ sealed abstract class DataType extends Serializable { self: CellType =>
* @param other The other cell type
* @return The intersection of this data type and the other cell type
*/
def intersect(other: CellType) =
def intersect(other: CellType): CellType =
if (bits < other.bits)
self
else if (bits > other.bits)
Expand All @@ -107,15 +106,15 @@ sealed abstract class DataType extends Serializable { self: CellType =>
* @param other The other cell type
* @return True for containment, false otherwise
*/
def contains(other: CellType) = bits >= other.bits
def contains(other: CellType): Boolean = bits >= other.bits

/**
* Return the number of bytes that would be consumed by the given number of items of the present type.
*
* @param size The number of items
* @return The number of bytes
*/
def numBytes(size: Int) = bytes * size
def numBytes(size: Int): Int = bytes * size

/**
* Return the string representation of this data type.
Expand All @@ -131,8 +130,7 @@ sealed abstract class DataType extends Serializable { self: CellType =>
sealed trait BitCells extends DataType { self: CellType =>
val bits: Int = 1
val isFloatingPoint: Boolean = false
val name = "bool"
def equalDataType(other: DataType) = other.isInstanceOf[BitCells]
def equalDataType(other: DataType): Boolean = other.isInstanceOf[BitCells]
def withNoData(noDataValue: Option[Double]): BitCells with NoDataHandling =
BitCellType // No other options is possible
def withDefaultNoData(): BitCells with NoDataHandling = BitCellType
Expand All @@ -144,8 +142,7 @@ sealed trait BitCells extends DataType { self: CellType =>
sealed trait ByteCells extends DataType { self: CellType =>
val bits: Int = 8
val isFloatingPoint: Boolean = false
val name = "int8"
def equalDataType(other: DataType) = other.isInstanceOf[ByteCells]
def equalDataType(other: DataType): Boolean = other.isInstanceOf[ByteCells]
def withNoData(noDataValue: Option[Double]): ByteCells with NoDataHandling =
ByteCells.withNoData(noDataValue.map(_.toByte))
def withDefaultNoData(): ByteCells with NoDataHandling = ByteConstantNoDataCellType
Expand All @@ -169,8 +166,7 @@ object ByteCells {
sealed trait UByteCells extends DataType { self: CellType =>
val bits: Int = 8
val isFloatingPoint: Boolean = false
val name = "uint8"
def equalDataType(other: DataType) = other.isInstanceOf[UByteCells]
def equalDataType(other: DataType): Boolean = other.isInstanceOf[UByteCells]
def withNoData(noDataValue: Option[Double]): UByteCells with NoDataHandling =
UByteCells.withNoData(noDataValue.map(_.toByte))
def withDefaultNoData(): UByteCells with NoDataHandling = UByteConstantNoDataCellType
Expand All @@ -194,8 +190,7 @@ object UByteCells {
sealed trait ShortCells extends DataType { self: CellType =>
val bits: Int = 16
val isFloatingPoint: Boolean = false
val name = "int16"
def equalDataType(other: DataType) = other.isInstanceOf[ShortCells]
def equalDataType(other: DataType): Boolean = other.isInstanceOf[ShortCells]
def withNoData(noDataValue: Option[Double]): ShortCells with NoDataHandling =
ShortCells.withNoData(noDataValue.map(_.toShort))
def withDefaultNoData(): ShortCells with NoDataHandling = ShortConstantNoDataCellType
Expand All @@ -219,8 +214,7 @@ object ShortCells {
sealed trait UShortCells extends DataType { self: CellType =>
val bits: Int = 16
val isFloatingPoint: Boolean = false
val name = "uint16"
def equalDataType(other: DataType) = other.isInstanceOf[UShortCells]
def equalDataType(other: DataType): Boolean = other.isInstanceOf[UShortCells]
def withNoData(noDataValue: Option[Double]): UShortCells with NoDataHandling =
UShortCells.withNoData(noDataValue.map(_.toShort))
def withDefaultNoData(): UShortCells with NoDataHandling = UShortConstantNoDataCellType
Expand All @@ -244,8 +238,7 @@ object UShortCells {
sealed trait IntCells extends DataType { self: CellType =>
val bits: Int = 32
val isFloatingPoint: Boolean = false
val name = "int32"
def equalDataType(other: DataType) = other.isInstanceOf[IntCells]
def equalDataType(other: DataType): Boolean = other.isInstanceOf[IntCells]
def withNoData(noDataValue: Option[Double]): IntCells with NoDataHandling =
IntCells.withNoData(noDataValue.map(_.toInt))
def withDefaultNoData(): IntCells with NoDataHandling = IntConstantNoDataCellType
Expand All @@ -266,8 +259,7 @@ object IntCells {
sealed trait FloatCells extends DataType { self: CellType =>
val bits: Int = 32
val isFloatingPoint: Boolean = true
val name = "float32"
def equalDataType(other: DataType) = other.isInstanceOf[FloatCells]
def equalDataType(other: DataType): Boolean = other.isInstanceOf[FloatCells]
def withNoData(noDataValue: Option[Double]): FloatCells with NoDataHandling =
FloatCells.withNoData(noDataValue.map(_.toFloat))
def withDefaultNoData(): FloatCells with NoDataHandling = FloatConstantNoDataCellType
Expand All @@ -291,8 +283,7 @@ object FloatCells {
sealed trait DoubleCells extends DataType { self: CellType =>
val bits: Int = 64
val isFloatingPoint: Boolean = true
val name = "float64"
def equalDataType(other: DataType) = other.isInstanceOf[DoubleCells]
def equalDataType(other: DataType): Boolean = other.isInstanceOf[DoubleCells]
def withNoData(noDataValue: Option[Double]): DoubleCells with NoDataHandling =
DoubleCells.withNoData(noDataValue)
def withDefaultNoData(): DoubleCells with NoDataHandling = DoubleConstantNoDataCellType
Expand Down Expand Up @@ -324,24 +315,21 @@ sealed trait ConstantNoData extends NoDataHandling { cellType: CellType => }
/**
* The [[NoNoData]] type, derived from [[NoDataHandling]].
*/
sealed trait NoNoData extends NoDataHandling { cellType: CellType =>
abstract override def toString: String = cellType.name + "raw"
}
sealed trait NoNoData extends NoDataHandling { cellType: CellType => }

/**
* The [[UserDefinedNoData]] type, derived from [[NoDataHandling]].
*/
sealed trait UserDefinedNoData[@specialized(Byte, Short, Int, Float, Double) T] extends NoDataHandling { cellType: CellType =>
val noDataValue: T
abstract override def toString: String = cellType.name + "ud" + noDataValue.toString
}

/**
* The [[BitCellType]] type, derived from [[BitCells]] and
* [[NoNoData]].
*/
case object BitCellType extends BitCells with NoNoData {
override final def numBytes(size: Int) = (size + 7) / 8
override final def numBytes(size: Int): Int = (size + 7) / 8
}

case object ByteCellType
Expand Down Expand Up @@ -398,36 +386,29 @@ case class DoubleUserDefinedNoDataCellType(noDataValue: Double)
object CellType {

/**
* Translate an integer representing a cell type into a
* [[CellType]]. This is the opposite of toAwtType.
*
* @param awtType An integer representing a cell type
* @return The CellType corresponding to awtType
*/
def fromAwtType(awtType: Int): CellType = awtType match {
case DataBuffer.TYPE_BYTE => UByteConstantNoDataCellType
case DataBuffer.TYPE_SHORT => ShortConstantNoDataCellType
case DataBuffer.TYPE_INT => IntConstantNoDataCellType
case DataBuffer.TYPE_FLOAT => FloatConstantNoDataCellType
case DataBuffer.TYPE_DOUBLE => DoubleConstantNoDataCellType
case _ => throw new IllegalArgumentException(s"AWT type $awtType is not supported")
}
* Translate a string representing a cell type into a [[CellType]].
*
* @param name A string representing a cell type, as reported by [[DataType.name]] e.g. "uint32"
* @return The CellType corresponding to `name`
*/
@deprecated("Use `fromName` instead", "1.1.0")
def fromString(name: String): CellType = fromName(name)

/**
* Translate a string representing a cell type into a [[CellType]].
*
* @param name An integer representing a cell type, e.g. "uint32"
* @return The CellType corresponding to name
*/
def fromString(name: String): CellType = name match {
case "bool" | "boolraw" => BitCellType // No NoData values
* Translate a string representing a cell type into a [[CellType]].
*
* @param name A string representing a cell type, as reported by [[DataType.name]] e.g. "uint32"
* @return The CellType corresponding to `name`
*/
def fromName(name: String): CellType = name match {
case "bool" | "boolraw" => BitCellType // No NoData values
case "int8raw" => ByteCellType
case "uint8raw" => UByteCellType
case "int16raw" => ShortCellType
case "uint16raw" => UShortCellType
case "float32raw" => FloatCellType
case "float64raw" => DoubleCellType
case "int8" => ByteConstantNoDataCellType // Constant NoData values
case "int8" => ByteConstantNoDataCellType // Constant NoData values
case "uint8" => UByteConstantNoDataCellType
case "int16" => ShortConstantNoDataCellType
case "uint16" => UShortConstantNoDataCellType
Expand Down Expand Up @@ -466,27 +447,78 @@ object CellType {
if (ndVal.isNaN) FloatConstantNoDataCellType
else FloatUserDefinedNoDataCellType(ndVal)
} catch {
case e: NumberFormatException => throw new IllegalArgumentException(s"Cell type $name is not supported")
case _: NumberFormatException => throw new IllegalArgumentException(s"Cell type $name is not supported")
}
case ct if ct.startsWith("float64ud") =>
try {
val ndVal = ct.stripPrefix("float64ud").toDouble
if (ndVal.isNaN) DoubleConstantNoDataCellType
else DoubleUserDefinedNoDataCellType(ndVal)
} catch {
case e: NumberFormatException => throw new IllegalArgumentException(s"Cell type $name is not supported")
case _: NumberFormatException => throw new IllegalArgumentException(s"Cell type $name is not supported")
}
case str =>
throw new IllegalArgumentException(s"Cell type $name is not supported")
}

/**
* Translate a [[CellType]] into the corresponding integer
* representation. This is the opposite of fromAwtType.
*
* @param cellType A CellType
* @return The corresponding integer representation of the given cell type
*/
* Translates a [[CellType]] into its canonical String representation.
*
* @param cellType item to convert
* @return String representation
*/
def toName(cellType: CellType): String = {
def forUserDefined[T <: AnyVal](base: CellType, value: T) = base.name + "ud" + value.toString

cellType match {
case BitCellType => "bool"
case ByteCellType => "int8raw"
case UByteCellType => "uint8raw"
case ShortCellType => "int16raw"
case UShortCellType => "uint16raw"
case IntCellType => "int32raw"
case FloatCellType => "float32raw"
case DoubleCellType => "float64raw"
case ByteConstantNoDataCellType => "int8"
case UByteConstantNoDataCellType => "uint8"
case ShortConstantNoDataCellType => "int16"
case UShortConstantNoDataCellType => "uint16"
case IntConstantNoDataCellType => "int32"
case FloatConstantNoDataCellType => "float32"
case DoubleConstantNoDataCellType => "float64"
case ByteUserDefinedNoDataCellType(nd) => forUserDefined(ByteConstantNoDataCellType, nd)
case UByteUserDefinedNoDataCellType(nd) => forUserDefined(UByteConstantNoDataCellType, nd)
case ShortUserDefinedNoDataCellType(nd) => forUserDefined(ShortConstantNoDataCellType, nd)
case UShortUserDefinedNoDataCellType(nd) => forUserDefined(UShortConstantNoDataCellType, nd)
case IntUserDefinedNoDataCellType(nd) => forUserDefined(IntConstantNoDataCellType, nd)
case FloatUserDefinedNoDataCellType(nd) => forUserDefined(FloatConstantNoDataCellType, nd)
case DoubleUserDefinedNoDataCellType(nd) => forUserDefined(DoubleConstantNoDataCellType, nd)
}
}

/**
* Translate an integer representing a cell type into a
* [[CellType]]. This is the opposite of toAwtType.
*
* @param awtType An integer representing a cell type
* @return The CellType corresponding to awtType
*/
def fromAwtType(awtType: Int): CellType = awtType match {
case DataBuffer.TYPE_BYTE => UByteConstantNoDataCellType
case DataBuffer.TYPE_SHORT => ShortConstantNoDataCellType
case DataBuffer.TYPE_INT => IntConstantNoDataCellType
case DataBuffer.TYPE_FLOAT => FloatConstantNoDataCellType
case DataBuffer.TYPE_DOUBLE => DoubleConstantNoDataCellType
case _ => throw new IllegalArgumentException(s"AWT type $awtType is not supported")
}

/**
* Translate a [[CellType]] into the corresponding integer
* representation. This is the opposite of fromAwtType.
*
* @param cellType A CellType
* @return The corresponding integer representation of the given cell type
*/
def toAwtType(cellType: CellType): Int = cellType match {
case _: BitCells => DataBuffer.TYPE_BYTE
case _: ByteCells => DataBuffer.TYPE_BYTE
Expand All @@ -497,4 +529,30 @@ object CellType {
case _: FloatCells => DataBuffer.TYPE_FLOAT
case _: DoubleCells => DataBuffer.TYPE_DOUBLE
}

/** Enumeration of pre-defined cell types without a `NoData` value. */
val noNoDataCellTypes = Seq(
BitCellType,
ByteCellType,
UByteCellType,
ShortCellType,
UShortCellType,
IntCellType,
FloatCellType,
DoubleCellType
)

/** Enumeration of pre-defined cell types with a default `NoData` value. */
val constantNoDataCellTypes = Seq(
ByteConstantNoDataCellType,
UByteConstantNoDataCellType,
ShortConstantNoDataCellType,
UShortConstantNoDataCellType,
IntConstantNoDataCellType,
FloatConstantNoDataCellType,
DoubleConstantNoDataCellType
)

/** Enumeration of all pre-defined cell types. */
val celltypes: Seq[DataType with NoDataHandling with Product] = noNoDataCellTypes ++ constantNoDataCellTypes
}

0 comments on commit bd30bec

Please sign in to comment.