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

geotrellis.spark.io.json.KeyIndexFormats.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.json

import geotrellis.spark._
import geotrellis.spark.io.index._
import geotrellis.spark.io.index.hilbert._
import geotrellis.spark.io.index.rowmajor._
import geotrellis.spark.io.index.zcurve._

import com.typesafe.config.ConfigFactory
import spray.json._
import spray.json.DefaultJsonProtocol._

import scala.collection.mutable
import scala.reflect._

import KeyFormats._

case class KeyIndexFormatEntry[K: JsonFormat: ClassTag, T <: KeyIndex[K]: JsonFormat: ClassTag](typeName: String) {
  val keyClassTag = implicitly[ClassTag[K]]
  val jsonFormat = implicitly[JsonFormat[T]]
  val indexClassTag = implicitly[ClassTag[T]]
}

trait KeyIndexRegistrator {
  def register(keyIndexRegistry: KeyIndexRegistry)
}

class KeyIndexJsonFormat[K](entries: Seq[KeyIndexFormatEntry[K, _]]) extends RootJsonFormat[KeyIndex[K]] {
  def write(obj: KeyIndex[K]): JsValue =
    entries.find { entry =>
      entry.indexClassTag.runtimeClass.getCanonicalName == obj.getClass.getCanonicalName
    } match {
      case Some(entry) =>
        obj.toJson(entry.jsonFormat.asInstanceOf[JsonFormat[KeyIndex[K]]])
      case None =>
        throw new SerializationException(s"Cannot serialize this key index with this KeyIndexJsonFormat: $obj")
    }

  def read(value: JsValue): KeyIndex[K] =
    value.asJsObject.getFields("type", "properties") match {
      case Seq(JsString(typeName), properties) =>
        entries.find(_.typeName == typeName) match {
          case Some(entry) =>
            // Deserialize the key index based on the entry information.
            entry.jsonFormat.read(value).asInstanceOf[KeyIndex[K]]
          case None =>
            throw new DeserializationException(s"Cannot deserialize key index with type $typeName in json $value")
        }
      case _ =>
        throw new DeserializationException(s"Expected KeyIndex, got $value")
    }
}

class KeyIndexRegistry {
  private var _entries = mutable.ListBuffer[KeyIndexFormatEntry[_, _]]()
  def register(entry: KeyIndexFormatEntry[_, _]): Unit = {
    _entries += entry
  }

  def entries: List[KeyIndexFormatEntry[_, _]] =
    _entries.toList
}

trait KeyIndexFormats {
  object KeyIndexJsonFormatFactory {
    private val REG_SETTING_NAME = "geotrellis.spark.io.index.registrator"

    private lazy val registry: Map[ClassTag[_], List[KeyIndexFormatEntry[_, _]]] = {
      val entryRegistry = new KeyIndexRegistry

      entryRegistry register KeyIndexFormatEntry[SpatialKey, HilbertSpatialKeyIndex](HilbertSpatialKeyIndexFormat.TYPE_NAME)
      entryRegistry register KeyIndexFormatEntry[SpatialKey, ZSpatialKeyIndex](ZSpatialKeyIndexFormat.TYPE_NAME)
      entryRegistry register KeyIndexFormatEntry[SpatialKey, RowMajorSpatialKeyIndex](RowMajorSpatialKeyIndexFormat.TYPE_NAME)

      entryRegistry register KeyIndexFormatEntry[SpaceTimeKey, HilbertSpaceTimeKeyIndex](HilbertSpaceTimeKeyIndexFormat.TYPE_NAME)
      entryRegistry register KeyIndexFormatEntry[SpaceTimeKey, ZSpaceTimeKeyIndex](ZSpaceTimeKeyIndexFormat.TYPE_NAME)

      // User defined here
      val conf = ConfigFactory.load()
      if(conf.hasPath(REG_SETTING_NAME)) {
        val userRegistratorClassName = conf.getString(REG_SETTING_NAME)
        val userRegistrator =
          Class.forName(userRegistratorClassName)
            .getConstructor()
            .newInstance()
            .asInstanceOf[KeyIndexRegistrator]
        userRegistrator.register(entryRegistry)
      }

      entryRegistry
        .entries
        .groupBy(_.keyClassTag)
        .toMap
    }

    def getKeyIndexJsonFormat[K: ClassTag](): RootJsonFormat[KeyIndex[K]] = {
      for((key, entries) <- registry) {
        if(key == classTag[K]) {
          return new KeyIndexJsonFormat[K](entries.map(_.asInstanceOf[KeyIndexFormatEntry[K, _]]))
        }
      }
      throw new DeserializationException(s"Cannot deserialize key index for key type ${classTag[K]}. You need to register this key type using the config item $REG_SETTING_NAME")
    }
  }

  implicit def keyIndexJsonFormat[K: ClassTag]: RootJsonFormat[KeyIndex[K]] =
    KeyIndexJsonFormatFactory.getKeyIndexJsonFormat[K]

  implicit object HilbertSpatialKeyIndexFormat extends RootJsonFormat[HilbertSpatialKeyIndex] {
    final def TYPE_NAME = "hilbert"

    def write(obj: HilbertSpatialKeyIndex): JsValue =
      JsObject(
        "type"   -> JsString(TYPE_NAME),
        "properties" -> JsObject(
          "keyBounds"   -> obj.keyBounds.toJson,
          "xResolution" -> obj.xResolution.toJson,
          "yResolution" -> obj.yResolution.toJson
        )
      )

    def read(value: JsValue): HilbertSpatialKeyIndex =
      value.asJsObject.getFields("type", "properties") match {
        case Seq(JsString(typeName), properties) => {
          if (typeName != TYPE_NAME)
            throw new DeserializationException(s"Wrong KeyIndex type: ${TYPE_NAME} expected.")
          properties.convertTo[JsObject]
            .getFields("keyBounds", "xResolution", "yResolution") match {
            case Seq(kb, xr, yr) =>
              HilbertSpatialKeyIndex(
                kb.convertTo[KeyBounds[SpatialKey]],
                xr.convertTo[Int],
                yr.convertTo[Int]
              )
            case _ =>
              throw new DeserializationException(
                "Wrong KeyIndex constructor arguments: HilbertSpatialKeyIndex constructor arguments expected.")
          }
        }
        case _ =>
          throw new DeserializationException("Wrong KeyIndex type: HilbertSpatialKeyIndex expected.")
      }
  }

  implicit object HilbertSpaceTimeKeyIndexFormat extends RootJsonFormat[HilbertSpaceTimeKeyIndex] {
    final def TYPE_NAME = "hilbert"

    def write(obj: HilbertSpaceTimeKeyIndex): JsValue =
      JsObject(
        "type"   -> JsString(TYPE_NAME),
        "properties" -> JsObject(
          "keyBounds"          -> obj.keyBounds.toJson,
          "xResolution"        -> obj.xResolution.toJson,
          "yResolution"        -> obj.yResolution.toJson,
          "temporalResolution" -> obj.temporalResolution.toJson
        )
      )

    def read(value: JsValue): HilbertSpaceTimeKeyIndex =
      value.asJsObject.getFields("type", "properties") match {
        case Seq(JsString(typeName), properties) => {
          if (typeName != TYPE_NAME)
            throw new DeserializationException(s"Wrong KeyIndex type: ${TYPE_NAME} expected.")

          properties.convertTo[JsObject]
            .getFields("keyBounds", "xResolution", "yResolution", "temporalResolution") match {
            case Seq(kb, xr, yr, tr) =>
              HilbertSpaceTimeKeyIndex(
                kb.convertTo[KeyBounds[SpaceTimeKey]],
                xr.convertTo[Int],
                yr.convertTo[Int],
                tr.convertTo[Int]
              )
            case _ =>
              throw new DeserializationException(
                "Wrong KeyIndex constructor arguments: HilbertSpaceTimeKeyIndex constructor arguments expected.")
          }
        }
        case _ =>
          throw new DeserializationException("Wrong KeyIndex type: HilberSpaceTimeKeyIndex expected.")
      }
  }

  implicit object RowMajorSpatialKeyIndexFormat extends RootJsonFormat[RowMajorSpatialKeyIndex] {
    final def TYPE_NAME = "rowmajor"

    def write(obj: RowMajorSpatialKeyIndex): JsValue =
      JsObject(
        "type"   -> JsString(TYPE_NAME),
        "properties" -> JsObject("keyBounds" -> obj.keyBounds.toJson)
      )

    def read(value: JsValue): RowMajorSpatialKeyIndex =
      value.asJsObject.getFields("type", "properties") match {
        case Seq(JsString(typeName), properties) => {
          if (typeName != TYPE_NAME)
            throw new DeserializationException(s"Wrong KeyIndex type: ${TYPE_NAME} expected.")

          properties.convertTo[JsObject].getFields("keyBounds") match {
            case Seq(kb) =>
              new RowMajorSpatialKeyIndex(kb.convertTo[KeyBounds[SpatialKey]])
            case _ =>
              throw new DeserializationException(
                "Wrong KeyIndex constructor arguments: RowMajorSpatialKeyIndex constructor arguments expected.")
          }
        }
        case _ =>
          throw new DeserializationException("Wrong KeyIndex type: RowMajorSpatialKeyIndex expected.")
      }
  }

  implicit object ZSpaceTimeKeyIndexFormat extends RootJsonFormat[ZSpaceTimeKeyIndex] {
    final def TYPE_NAME = "zorder"

    def write(obj: ZSpaceTimeKeyIndex): JsValue =
      JsObject(
        "type"   -> JsString(TYPE_NAME),
        "properties" -> JsObject(
          "keyBounds"          -> obj.keyBounds.toJson,
          "temporalResolution" -> obj.temporalResolution.toJson
        )
      )

    def read(value: JsValue): ZSpaceTimeKeyIndex =
      value.asJsObject.getFields("type", "properties") match {
        case Seq(JsString(typeName), properties) => {
          if (typeName != TYPE_NAME)
            throw new DeserializationException(s"Wrong KeyIndex type: ${TYPE_NAME} expected.")

          properties.convertTo[JsObject].getFields("keyBounds", "temporalResolution") match {
            case Seq(keyBounds, temporalResolution) =>
              ZSpaceTimeKeyIndex.byMilliseconds(keyBounds.convertTo[KeyBounds[SpaceTimeKey]], temporalResolution.convertTo[Long])
            case _ =>
              throw new DeserializationException(
                "Wrong KeyIndex constructor arguments: ZSpaceTimeKeyIndex constructor arguments expected.")
          }
        }
        case _ =>
          throw new DeserializationException("Wrong KeyIndex type: ZSpaceTimeKeyIndex expected.")
      }
  }

  implicit object ZSpatialKeyIndexFormat extends RootJsonFormat[ZSpatialKeyIndex] {
    final def TYPE_NAME = "zorder"

    def write(obj: ZSpatialKeyIndex): JsValue =
      JsObject(
        "type"   -> JsString(TYPE_NAME),
        "properties" -> JsObject("keyBounds" -> obj.keyBounds.toJson)
      )

    def read(value: JsValue): ZSpatialKeyIndex =
      value.asJsObject.getFields("type", "properties") match {
        case Seq(JsString(typeName), properties) => {
          if (typeName != TYPE_NAME)
            throw new DeserializationException(s"Wrong KeyIndex type: ${TYPE_NAME} expected.")

          properties.convertTo[JsObject].getFields("keyBounds") match {
            case Seq(kb) =>
              new ZSpatialKeyIndex(kb.convertTo[KeyBounds[SpatialKey]])
            case _ =>
              throw new DeserializationException(
                "Wrong KeyIndex constructor arguments: ZSpatialKeyIndex constructor arguments expected.")
          }
        }
        case _ =>
          throw new DeserializationException("Wrong KeyIndex type: ZSpatialKeyIndex expected.")
      }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy