
geotrellis.server.ogc.conf.package.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of geotrellis-server-stac-example_2.12 Show documentation
Show all versions of geotrellis-server-stac-example_2.12 Show documentation
GeoTrellis Server is a set of components designed to simplify viewing, processing, and serving raster data from arbitrary sources with an emphasis on doing so in a functional style.
The newest version!
/*
* Copyright 2020 Azavea
*
* 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 geotrellis.server.ogc
import geotrellis.server.ogc.wms.wmsScope
import geotrellis.server.ogc.style._
import geotrellis.proj4.CRS
import geotrellis.vector.Extent
import geotrellis.raster.{resample, CellType, ResampleMethod, TileLayout}
import geotrellis.raster.render.{ColorMap, ColorRamp}
import com.azavea.maml.ast._
import com.azavea.maml.ast.codec.tree._
import com.typesafe.config._
import geotrellis.raster.io.geotiff.{Auto, AutoHigherResolution, Base, OverviewStrategy}
import io.circe._
import io.circe.parser._
import io.circe.syntax._
import cats.syntax.either._
import pureconfig._
import pureconfig.error.{CannotConvert, ExceptionThrown}
import pureconfig.generic.FieldCoproductHint
import pureconfig.generic.auto._
import scala.util.{Success, Try}
import scala.collection.JavaConverters._
/**
* A grab bag of [[ConfigReader]] instances necessary to read the configuration
*/
package object conf {
/**
* Starting 0.11.0 https://github.com/pureconfig/pureconfig/blob/bfc74ce436297b2a9da091e04d362be61108a3cf/CHANGELOG.md#0110-may-9-2019 The default
* transformation in FieldCoproductHint changed from converting class names to lower case to converting them to kebab case.
*/
implicit def coproductHint[T] =
new FieldCoproductHint[T]("type") {
override def fieldValue(name: String): String = name.toLowerCase
}
implicit val circeJsonReader: ConfigReader[Json] =
ConfigReader[ConfigValue].emap { cv =>
val renderOptions = ConfigRenderOptions.concise().setJson(true)
val jsonString = cv.render(renderOptions)
parse(jsonString) match {
case Left(parsingFailure) => Left(CannotConvert(jsonString, "json", parsingFailure.getMessage))
case Right(json) => Right(json)
}
}
implicit val expressionReader: ConfigReader[Expression] =
ConfigReader[Json].map { expressionJson =>
expressionJson.as[Expression] match {
case Right(success) => success
case Left(err) => throw err
}
}
implicit val colorRampReader: ConfigReader[ColorRamp] =
ConfigReader[List[String]].map { colors =>
ColorRamp(colors.map(java.lang.Long.decode(_).toInt))
}
/**
* HOCON doesn't naturally handle unquoted strings which contain decimals ('.') very well. As a result, some special configuration handling is
* required here to allow unquoted strings specifically when we know we're trying to decode a ColorMap.
*
* @note
* It is currently difficult to handle double-keyed maps. A workaround has been provided, but it only works with doubles that explicitly decimal
* pad to tenths (0.0 is OK, 0 is to be avoided)
*/
implicit val mapDoubleIntReader: ConfigReader[Map[Double, Int]] =
ConfigReader[Map[String, ConfigValue]].map { cmap =>
val numericMap = cmap
.flatMap { case (k, v) =>
v.valueType match {
case ConfigValueType.OBJECT =>
val confmap = v.asInstanceOf[ConfigObject].asScala
confmap.map { case (ck, cv) =>
val key = k + "." + ck
val value = cv.unwrapped.asInstanceOf[String]
key -> value
}
case ConfigValueType.STRING =>
List(k -> v.unwrapped.asInstanceOf[String])
case _ =>
List(k -> v.toString)
}
}
.map { case (k, v) =>
val key = k.toDouble
val value = java.lang.Long.decode(v).toInt
key -> value
}
.toMap
numericMap
}
implicit val colormapReader: ConfigReader[ColorMap] =
ConfigReader[Map[Double, Int]].map { map =>
ColorMap(map)
}
implicit val clipDefinitionReader: ConfigReader[ClipDefinition] =
ConfigReader[String].emap { str =>
ClipDefinition.fromString(str) match {
case Some(cd) =>
Right(cd)
case None =>
Left(CannotConvert(str, "ClipDefinition", s"$str is not a valid ClipDefinition"))
}
}
implicit val keywordConfigReader: ConfigReader[opengis.wms.Keyword] =
ConfigReader[String].map { str =>
opengis.wms.Keyword(str)
}
implicit val nameConfigReader: ConfigReader[opengis.wms.Name] =
ConfigReader[String].map { str =>
opengis.wms.Name.fromString(str, wmsScope)
}
implicit val crsReader: ConfigReader[CRS] =
ConfigReader[Int].map { epsgCode =>
Try(CRS.fromEpsgCode(epsgCode)) match {
case Success(crs) => crs
case _ => throw new Exception(s"Invalid EPSG code: ${epsgCode}")
}
}
implicit val extentReader: ConfigReader[Extent] =
ConfigReader[(Double, Double, Double, Double)].map { case extent @ (xmin, ymin, xmax, ymax) =>
Try(Extent(xmin, ymin, xmax, ymax)) match {
case Success(extent) => extent
case _ => throw new Exception(s"Invalid extent: $extent. Should be (xmin, ymin, xmax, ymax)")
}
}
implicit val tileLayoutReader: ConfigReader[TileLayout] =
ConfigReader[(Int, Int, Int, Int)].map { case layout @ (layoutCols, layoutRows, tileCols, tileRows) =>
Try(TileLayout(layoutCols, layoutRows, tileCols, tileRows)) match {
case Success(layout) => layout
case _ => throw new Exception(s"Invalid layout: $layout. Should be (layoutCols, layoutRows, tileCols, tileRows)")
}
}
implicit val resampleMethodReader: ConfigReader[ResampleMethod] =
ConfigReader[String].map {
case "nearest-neighbor" => resample.NearestNeighbor
case "bilinear" => resample.Bilinear
case "cubic-convolution" => resample.CubicConvolution
case "cubic-spline" => resample.CubicSpline
case "lanczos" => resample.Lanczos
case "average" => resample.Average
case "mode" => resample.Mode
case "median" => resample.Median
case "max" => resample.Max
case "min" => resample.Min
case "sum" => resample.Sum
}
implicit val overviewStrategyReader: ConfigReader[OverviewStrategy] = {
def parse(strategy: String, input: String): OverviewStrategy =
Auto(Try(input.split(s"$strategy-").last.toInt).getOrElse(0))
def parseAuto(str: String): OverviewStrategy = parse("auto", str)
def parseLevel(str: String): OverviewStrategy = parse("level", str)
ConfigReader[String].map {
case "auto-higher-resolution" => AutoHigherResolution
case "base" => Base
case str if str.startsWith("auto") => parseAuto(str)
case str if str.startsWith("level") => parseLevel(str)
case _ => OverviewStrategy.DEFAULT
}
}
implicit val ogcTimeFormatReader: ConfigReader[OgcTimeFormat] =
ConfigReader[String].emap(_.asJson.as[OgcTimeFormat].leftMap(e => ExceptionThrown(e.fillInStackTrace())))
implicit val ogcTimeDefaultReader: ConfigReader[OgcTimeDefault] =
ConfigReader[String].emap(_.asJson.as[OgcTimeDefault].leftMap(e => ExceptionThrown(e.fillInStackTrace())))
implicit val cellTypeReader: ConfigReader[CellType] =
ConfigReader[String].emap(_.asJson.as[CellType].leftMap(e => ExceptionThrown(e.fillInStackTrace())))
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy