diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index cfa38396..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,93 +0,0 @@ -version: 2.1 - -executors: - executor-openjdk8: - docker: - - image: circleci/openjdk:8-jdk - environment: - # https://circleci.com/docs/2.0/java-oom/ - _JAVA_OPTIONS: "-Xms128m -Xmx2g" - -jobs: - openjdk8-build: - parameters: - scala-version: - type: string - executor: executor-openjdk8 - environment: - SCALA_VERSION: << parameters.scala-version >> - steps: - - checkout - # https://circleci.com/docs/2.0/building-docker-images/ - - setup_remote_docker: - docker_layer_caching: false - - restore_cache: - keys: - - sbt-cache-{{ checksum "project/Dependencies.scala" }} - - run: - name: Executing cibuild - command: ./scripts/cibuild - - save_cache: - key: sbt-cache-{{ checksum "project/Dependencies.scala" }} - paths: - - "~/.ivy2/cache" - - "~/.cache/coursier" - - "~/.sbt" - - "~/.m2" - - openjdk8-deploy: - parameters: - scala-version: - type: string - executor: executor-openjdk8 - environment: - SCALA_VERSION: << parameters.scala-version >> - steps: - - checkout - # https://circleci.com/docs/2.0/building-docker-images/ - - setup_remote_docker: - docker_layer_caching: false - - restore_cache: - keys: - - sbt-cache-{{ checksum "project/Dependencies.scala" }} - - run: - name: "Import signing key" - command: | - gpg --keyserver keyserver.ubuntu.com \ - --recv-keys "0x${GPG_KEY_ID}" && \ - echo "${GPG_KEY}" | base64 -d > signing_key.asc && \ - gpg --import signing_key.asc - - run: - name: Executing cibuild - command: ./scripts/cibuild - - run: - name: Executing cipublish - command: ./scripts/cipublish - -workflows: - build: - jobs: - - openjdk8-build: - matrix: - parameters: - scala-version: ["2.12.15"] - # required since openjdk8-deploy has tag filters AND requires - # openjdk8 - # https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag - filters: - tags: - only: - - /^(.*)$/ - - openjdk8-deploy: - matrix: - parameters: - scala-version: ["2.12.15"] - requires: - - openjdk8-build - filters: - tags: - only: - - /^(.*)$/ - branches: - only: - - main diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5ace4600 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..9a6e27f1 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,22 @@ +name-template: '$NEXT_MINOR_VERSION' +tag-template: 'v$NEXT_MINOR_VERSION' +categories: + - title: 'Added' + labels: + - 'feature' + - title: 'Changed' + labels: + - 'enhancement' + - 'dependency-update' + - title: 'Fixed' + labels: + - 'fix' + - 'bugfix' + - 'bug' +exclude-labels: + - 'skip-changelog' + - 'docs' + - 'build' +change-template: '- $TITLE [#$NUMBER](https://github.com/geotrellis/geotrelis-server/pull/$NUMBER) (@$AUTHOR)' +template: | + $CHANGES diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..6df10191 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: CI +on: + pull_request: + branches: ['**'] + push: + branches: ['**'] + tags: [v*] +jobs: + build: + name: Build and Test + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'geotrellis/geotrellis-server' + strategy: + matrix: + os: [ubuntu-latest] + java: [11, 21] + distribution: [temurin] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: coursier/cache-action@v6 + - uses: actions/setup-java@v4 + with: + distribution: ${{ matrix.distribution }} + java-version: ${{ matrix.java }} + + - name: Check formatting + run: sbt scalafmtCheckAll + + - name: Build project + run: sbt +test + + publish: + name: Publish Artifacts + needs: [build] + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + strategy: + matrix: + os: [ubuntu-latest] + java: [11] + distribution: [temurin] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: coursier/cache-action@v6 + - uses: actions/setup-java@v4 + with: + distribution: ${{ matrix.distribution }} + java-version: ${{ matrix.java }} + + - name: Release + run: sbt ci-release + env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + if: ${{ env.SONATYPE_PASSWORD != '' && env.SONATYPE_USERNAME != '' }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000..e3badf0f --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,22 @@ +name: Release Drafter + +on: + push: + branches: + - main + pull_request: + types: [opened, reopened, synchronize] + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.jabbarc b/.jabbarc deleted file mode 100644 index 296be5a6..00000000 --- a/.jabbarc +++ /dev/null @@ -1 +0,0 @@ -adopt@1.8.212-03 diff --git a/.scalafmt.conf b/.scalafmt.conf index 9d566476..8749f9de 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,10 +1,13 @@ -version = 3.1.1 -runner.dialect = scala212 +version=3.8.1 +runner.dialect = scala3 +align.openParenCallSite = true +align.openParenDefnSite = true maxColumn = 150 continuationIndent.defnSite = 2 -continuationIndent.callSite = 2 assumeStandardLibraryStripMargin = true danglingParentheses.preset = true -rewrite.rules = [SortImports, RedundantBraces, RedundantParens, SortModifiers] +rewrite.rules = [AvoidInfix, SortImports, RedundantParens, SortModifiers] +docstrings = JavaDoc +newlines.afterCurlyLambda = preserve docstrings.style = Asterisk -align.preset = more +docstrings.oneline = unfold diff --git a/README.md b/README.md index 11e67f9d..202131ac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # GeoTrellis Server -[![CircleCI](https://circleci.com/gh/geotrellis/geotrellis-server.svg?style=svg)](https://circleci.com/gh/geotrellis/geotrellis-server) [![Join the chat at https://gitter.im/geotrellis/geotrellis](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/geotrellis/geotrellis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Maven Central](https://img.shields.io/maven-central/v/com.azavea.geotrellis/geotrellis-server-core_2.12)](http://search.maven.org/#search%7Cga%7C1%7com.azavea.geotrellis) [![Snapshots](https://img.shields.io/nexus/s/https/oss.sonatype.org/com.azavea.geotrellis/geotrellis-server-core_2.12.svg)](https://oss.sonatype.org/content/repositories/snapshots/com/azavea/geotrellis/geotrellis-server-core_2.12/) +[![CI](https://github.com/geotrellis/geotrellis-server/actions/workflows/ci.yml/badge.svg)](https://github.com/geotrellis/geotrellis-server/actions/workflows/ci.yml) [![Maven Central](https://img.shields.io/maven-central/v/com.azavea.geotrellis/geotrellis-server-core_2.12)](http://search.maven.org/#search%7Cga%7C1%7com.azavea.geotrellis) [![Snapshots](https://img.shields.io/nexus/s/https/oss.sonatype.org/com.azavea.geotrellis/geotrellis-server-core_2.12.svg)](https://oss.sonatype.org/content/repositories/snapshots/com/azavea/geotrellis/geotrellis-server-core_2.12/) GeoTrellis Server is a set of components designed to simplify diff --git a/azure/src/main/scala/geotrellis/store/azure/conf/AzureConfig.scala b/azure/src/main/scala/geotrellis/store/azure/conf/AzureConfig.scala index c7233260..39bc6e9a 100755 --- a/azure/src/main/scala/geotrellis/store/azure/conf/AzureConfig.scala +++ b/azure/src/main/scala/geotrellis/store/azure/conf/AzureConfig.scala @@ -22,6 +22,6 @@ import pureconfig.generic.auto._ case class AzureConfig(storageConnectionString: String) object AzureConfig { - lazy val conf: AzureConfig = ConfigSource.default.at("geotrellis.azure").loadOrThrow[AzureConfig] + lazy val conf: AzureConfig = ConfigSource.default.at("geotrellis.azure").loadOrThrow[AzureConfig] implicit def cassandraConfigToClass(obj: AzureConfig.type): AzureConfig = conf } diff --git a/azure/src/main/scala/geotrellis/store/azure/util/AzureRangeReader.scala b/azure/src/main/scala/geotrellis/store/azure/util/AzureRangeReader.scala index a085a7b4..3d6b3543 100644 --- a/azure/src/main/scala/geotrellis/store/azure/util/AzureRangeReader.scala +++ b/azure/src/main/scala/geotrellis/store/azure/util/AzureRangeReader.scala @@ -27,7 +27,7 @@ import java.net.URI class AzureRangeReader(uri: AzureURI, blobServiceClient: BlobServiceClient) extends RangeReader { @transient lazy val blobContainerClient: BlobContainerClient = blobServiceClient.getBlobContainerClient(uri.getContainer) - @transient lazy val blobClient: BlobClient = blobContainerClient.getBlobClient(uri.getPath) + @transient lazy val blobClient: BlobClient = blobContainerClient.getBlobClient(uri.getPath) val totalLength: Long = blobClient.getProperties.getBlobSize diff --git a/azure/src/main/scala/geotrellis/store/azure/util/AzureURI.scala b/azure/src/main/scala/geotrellis/store/azure/util/AzureURI.scala index 238cf5c6..e590c49b 100755 --- a/azure/src/main/scala/geotrellis/store/azure/util/AzureURI.scala +++ b/azure/src/main/scala/geotrellis/store/azure/util/AzureURI.scala @@ -25,7 +25,7 @@ import java.net.URI case class AzureURI(uri: URI) { def getContainer: String = uri.getUserInfo - def getAccount: String = uri.getAuthority.split("@").last.split("\\.").head + def getAccount: String = uri.getAuthority.split("@").last.split("\\.").head def getPath: String = { val path = uri.getPath if (path.startsWith("/")) path.drop(1) else path @@ -44,7 +44,7 @@ object AzureURI { if (List("wasbs", "wasb") contains uri.getScheme) AzureURI(uri) else { // get URI path, remove the first slash - val path = uri.getPath + val path = uri.getPath val npath = (if (path.startsWith("/")) path.tail else path).split("/") // the first item in the path is the container val container = npath.head @@ -54,7 +54,7 @@ object AzureURI { } def fromString(uri: String): AzureURI = fromURI(new URI(uri)) - implicit def uriToAzureUri(uri: URI): AzureURI = fromURI(uri) + implicit def uriToAzureUri(uri: URI): AzureURI = fromURI(uri) implicit def stringToAzureUri(uri: String): AzureURI = fromString(uri) - implicit def azureUriToUri(uri: AzureURI): URI = uri.uri + implicit def azureUriToUri(uri: AzureURI): URI = uri.uri } diff --git a/bench/src/main/scala/geotrellis/server/TmsReificationBench.scala b/bench/src/main/scala/geotrellis/server/TmsReificationBench.scala index 7716769e..6a4a45e5 100644 --- a/bench/src/main/scala/geotrellis/server/TmsReificationBench.scala +++ b/bench/src/main/scala/geotrellis/server/TmsReificationBench.scala @@ -22,11 +22,10 @@ import com.azavea.maml.ast._ import com.azavea.maml.error._ import com.azavea.maml.eval.ConcurrentInterpreter import cats.effect._ -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger +import org.typelevel.log4cats.slf4j.Slf4jLogger import org.openjdk.jmh.annotations._ -import scala.concurrent.ExecutionContext import java.net.URI @BenchmarkMode(Array(Mode.AverageTime)) @@ -34,7 +33,7 @@ import java.net.URI class TmsReificationBench { implicit val logger = Slf4jLogger.getLogger[IO] - implicit var contextShift = IO.contextShift(ExecutionContext.global) + import cats.effect.unsafe.implicits.global // NDVI val ast: Expression = diff --git a/build.sbt b/build.sbt index 29d84a37..e4f64a23 100644 --- a/build.sbt +++ b/build.sbt @@ -6,22 +6,11 @@ import Dependencies._ scalaVersion := scalaVer ThisBuild / scalaVersion := scalaVer +ThisBuild / libraryDependencySchemes += "org.typelevel" %% "cats-parse" % VersionScheme.Always val currentYear = java.time.Year.now.getValue.toString lazy val commonSettings = Seq( - // We are overriding the default behavior of sbt-git which, by default, - // only appends the `-SNAPSHOT` suffix if there are uncommitted - // changes in the workspace. - version := { - if (git.gitHeadCommit.value.isEmpty) "0.0.1-SNAPSHOT" - else if (git.gitDescribedVersion.value.isEmpty) - git.gitHeadCommit.value.get.substring(0, 7) + "-SNAPSHOT" - else if (git.gitCurrentTags.value.isEmpty || git.gitUncommittedChanges.value) - git.gitDescribedVersion.value.get + "-SNAPSHOT" - else - git.gitDescribedVersion.value.get - }, scalaVersion := scalaVer, crossScalaVersions := crossScalaVer, scalacOptions := Seq( @@ -40,13 +29,10 @@ lazy val commonSettings = Seq( // "-Yrangepos", // required by SemanticDB compiler plugin // "-Ywarn-unused-import", // required by `RemoveUnused` rule ), - resolvers ++= Seq( - Resolver - .bintrayRepo("bkirwi", "maven"), // Required for `decline` dependency + resolvers ++= Resolver.sonatypeOssRepos("releases") ++ Resolver.sonatypeOssRepos("snapshots") ++ Seq( + Resolver.bintrayRepo("bkirwi", "maven"), // Required for `decline` dependency Resolver.bintrayRepo("azavea", "maven"), Resolver.bintrayRepo("azavea", "geotrellis"), - Resolver.sonatypeRepo("releases"), - Resolver.sonatypeRepo("snapshots"), "osgeo-snapshots" at "https://repo.osgeo.org/repository/snapshot/", "osgeo-releases" at "https://repo.osgeo.org/repository/release/", "eclipse-releases" at "https://repo.eclipse.org/content/groups/releases", @@ -112,7 +98,7 @@ lazy val publishSettings = Seq( organizationHomepage := Some(new URL("https://geotrellis.io/")), description := "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.", Test / publishArtifact := false -) ++ sonatypeSettings ++ credentialSettings +) ++ sonatypeSettings lazy val sonatypeSettings = Seq( publishMavenStyle := true, @@ -146,32 +132,16 @@ lazy val sonatypeSettings = Seq( ), licenses := Seq( "Apache-2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0.txt") - ), - publishTo := sonatypePublishTo.value -) - -lazy val credentialSettings = Seq( - credentials += Credentials( - "GnuPG Key ID", - "gpg", - System.getenv().get("GPG_KEY_ID"), - "ignored" - ), - credentials += Credentials( - "Sonatype Nexus Repository Manager", - "oss.sonatype.org", - System.getenv().get("SONATYPE_USERNAME"), - System.getenv().get("SONATYPE_PASSWORD") ) ) lazy val root = project .in(file(".")) + .aggregate(core, example, ogc, opengis, `ogc-example`, effects, stac, `stac-example`, azure) .settings(name := "geotrellis-server") .settings(commonSettings) .settings(publishSettings) .settings(noPublishSettings) - .aggregate(core, example, ogc, opengis, `ogc-example`, effects, stac, `stac-example`, azure) lazy val core = project .settings(moduleName := "geotrellis-server-core") diff --git a/core/src/main/scala/geotrellis/server/LayerExtent.scala b/core/src/main/scala/geotrellis/server/LayerExtent.scala index 3b1c6227..f194fd9c 100644 --- a/core/src/main/scala/geotrellis/server/LayerExtent.scala +++ b/core/src/main/scala/geotrellis/server/LayerExtent.scala @@ -28,7 +28,7 @@ import cats.data.Validated._ import cats.effect._ import cats.Parallel import cats.implicits._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger object LayerExtent { def apply[F[_]: Logger: Parallel: Monad, T: ExtentReification[F, *]]( @@ -49,10 +49,12 @@ object LayerExtent { s"[LayerExtent] Retrieved Teters for extent ($extent) and cellsize ($cellSize): ${paramMap.toString}" ) vars = Vars.varsWithBuffer(expr) - params <- vars.toList.parTraverse { case (varName, (_, buffer)) => - val thingify = implicitly[ExtentReification[F, T]].extentReification(paramMap(varName)) - thingify(extent, cellSize).map(varName -> _) - } map { _.toMap } + params <- vars.toList + .parTraverse { case (varName, (_, buffer)) => + val thingify = implicitly[ExtentReification[F, T]].extentReification(paramMap(varName)) + thingify(extent, cellSize).map(varName -> _) + } + .map { _.toMap } reified <- Expression.bindParams(expr, params.mapValues(RasterLit(_))) match { case Valid(expression) => interpreter(expression) case Invalid(errors) => throw new Exception(errors.map(_.repr).reduce) @@ -69,7 +71,9 @@ object LayerExtent { interpreter: Interpreter[F] ): (Extent, Option[CellSize]) => F[Interpreted[MultibandTile]] = apply[F, T](getParams.map(mkExpr(_)), getParams, interpreter, None) - /** Provide an expression and expect arguments to fulfill its needs */ + /** + * Provide an expression and expect arguments to fulfill its needs + */ def curried[F[_]: Logger: Parallel: Monad, T: ExtentReification[F, *]]( expr: Expression, interpreter: Interpreter[F], @@ -88,7 +92,9 @@ object LayerExtent { ): (Extent, Option[CellSize]) => F[Interpreted[MultibandTile]] = apply(getExpression, getParams, interpreter, None) - /** The identity endpoint (for simple display of raster) */ + /** + * The identity endpoint (for simple display of raster) + */ def withCellType[F[_]: Logger: Parallel: Monad: Concurrent, T: ExtentReification[F, *]]( param: T, cellType: CellType diff --git a/core/src/main/scala/geotrellis/server/LayerHistogram.scala b/core/src/main/scala/geotrellis/server/LayerHistogram.scala index 57a1cdde..957c31f9 100644 --- a/core/src/main/scala/geotrellis/server/LayerHistogram.scala +++ b/core/src/main/scala/geotrellis/server/LayerHistogram.scala @@ -33,11 +33,11 @@ import cats.syntax.flatMap._ import cats.syntax.traverse._ import cats.syntax.option._ import cats.instances.option._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger object LayerHistogram { case class NoSuitableHistogramResolution(cells: Int) extends Throwable - case class RequireIntersectingSources() extends Throwable + case class RequireIntersectingSources() extends Throwable // Added so that we can get combine implicit val extentSemigroup: Semigroup[Extent] = { _ combine _ } @@ -64,7 +64,7 @@ object LayerHistogram { .map(z => z.map(_.extent).reduce) ) intersectionO = SampleUtils.intersectExtents(extents) - _ <- intersectionO traverse { intersection => + _ <- intersectionO.traverse { intersection => logger.trace( s"[LayerHistogram] Intersection of provided layer extents calculated: $intersection" ) @@ -74,21 +74,23 @@ object LayerHistogram { s"[LayerHistogram] Largest cell size of provided layers calculated: $cellSize" ) mbtileForExtent = LayerExtent(getExpression, getParams, interpreter, None) - _ <- intersectionO traverse { intersection => + _ <- intersectionO.traverse { intersection => logger.trace( s"[LayerHistogram] calculating histogram from (approximately) ${intersection.area / (cellSize.width * cellSize.height)} cells" ) } - interpretedTile <- intersectionO traverse { intersection => + interpretedTile <- intersectionO.traverse { intersection => mbtileForExtent(intersection, cellSize.some) } - } yield interpretedTile.map { mbtileValidated => - mbtileValidated.map { mbTile => - mbTile.bands.map { band => - StreamingHistogram.fromTile(band) - }.toList + } yield interpretedTile + .map { mbtileValidated => + mbtileValidated.map { mbTile => + mbTile.bands.map { band => + StreamingHistogram.fromTile(band) + }.toList + } } - } getOrElse ??? + .getOrElse(???) } def generateExpression[F[_]: Logger: Parallel: Monad, T: ExtentReification[F, *]: HasRasterExtents[F, *]]( @@ -99,7 +101,9 @@ object LayerHistogram { ): F[Interpreted[List[Histogram[Double]]]] = apply[F, T](getParams.map(mkExpr(_)), getParams, interpreter, maxCells) - /** Provide an expression and expect arguments to fulfill its needs */ + /** + * Provide an expression and expect arguments to fulfill its needs + */ def curried[F[_]: Logger: Parallel: Monad, T: ExtentReification[F, *]: HasRasterExtents[F, *]]( expr: Expression, interpreter: Interpreter[F], @@ -113,7 +117,9 @@ object LayerHistogram { maxCells ) - /** The identity endpoint (for simple display of raster) */ + /** + * The identity endpoint (for simple display of raster) + */ def concurrent[F[_]: Logger: Parallel: Monad: Concurrent, T: ExtentReification[F, *]: HasRasterExtents[F, *]]( param: T, maxCells: Int diff --git a/core/src/main/scala/geotrellis/server/LayerTms.scala b/core/src/main/scala/geotrellis/server/LayerTms.scala index b9b6c8a5..1a65d760 100644 --- a/core/src/main/scala/geotrellis/server/LayerTms.scala +++ b/core/src/main/scala/geotrellis/server/LayerTms.scala @@ -25,9 +25,11 @@ import cats.effect._ import cats.implicits._ import cats.data.Validated._ import geotrellis.raster.{io => _, _} -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger -/** Provides methods for producing TMS tiles */ +/** + * Provides methods for producing TMS tiles + */ object LayerTms { /** @@ -62,12 +64,14 @@ object LayerTms { s"Retrieved parameters for TMS ($z, $x, $y): ${paramMap.toString}" ) vars = Vars.varsWithBuffer(expr) - params <- vars.toList.parTraverse { case (varName, (_, buffer)) => - val eval = - implicitly[TmsReification[F, T]] - .tmsReification(paramMap(varName), buffer) - eval(z, x, y).map(varName -> _) - } map { _.toMap } + params <- vars.toList + .parTraverse { case (varName, (_, buffer)) => + val eval = + implicitly[TmsReification[F, T]] + .tmsReification(paramMap(varName), buffer) + eval(z, x, y).map(varName -> _) + } + .map { _.toMap } reified <- Expression.bindParams(expr, params.mapValues(RasterLit(_))) match { case Valid(expression) => interpreter(expression) case Invalid(errors) => throw new Exception(errors.map(_.repr).reduce) @@ -84,7 +88,9 @@ object LayerTms { interpreter: Interpreter[F] ): (Int, Int, Int) => F[Interpreted[MultibandTile]] = apply[F, T](getParams.map(mkExpr(_)), getParams, interpreter, None) - /** Provide an expression and expect arguments to fulfill its needs */ + /** + * Provide an expression and expect arguments to fulfill its needs + */ def curried[F[_]: Logger: Parallel: Monad, T: TmsReification[F, *]]( expr: Expression, interpreter: Interpreter[F], @@ -95,7 +101,9 @@ object LayerTms { eval(z, x, y) } - /** The identity endpoint (for simple display of raster) */ + /** + * The identity endpoint (for simple display of raster) + */ def concurrent[F[_]: Logger: Parallel: Monad: Concurrent, T: TmsReification[F, *]]( getExpression: F[Expression], getParams: F[Map[String, T]], @@ -103,7 +111,9 @@ object LayerTms { ): (Int, Int, Int) => F[Interpreted[MultibandTile]] = apply(getExpression, getParams, interpreter, None) - /** The identity endpoint (for simple display of raster) */ + /** + * The identity endpoint (for simple display of raster) + */ def withCellType[F[_]: Logger: Parallel: Monad: Concurrent, T: TmsReification[F, *]]( param: T, cellType: CellType diff --git a/core/src/main/scala/geotrellis/server/extent/SampleUtils.scala b/core/src/main/scala/geotrellis/server/extent/SampleUtils.scala index f11fef18..55ad0d1e 100644 --- a/core/src/main/scala/geotrellis/server/extent/SampleUtils.scala +++ b/core/src/main/scala/geotrellis/server/extent/SampleUtils.scala @@ -23,16 +23,18 @@ import cats.data.{NonEmptyList => NEL} object SampleUtils { val logger = org.log4s.getLogger - /** Sample imagery within the provided extent */ + /** + * Sample imagery within the provided extent + */ final def sampleRasterExtent(uberExtent: Extent, cs: CellSize, maxCells: Int): (Extent, Extent, Extent, Extent) = { logger.trace(s"Finding sample extent for UberExtent $uberExtent for $cs with a maximum sample of $maxCells cells") - val sampleWidth = math.sqrt(maxCells.toDouble) * cs.width + val sampleWidth = math.sqrt(maxCells.toDouble) * cs.width val sampleHeight = math.sqrt(maxCells.toDouble) * cs.height logger.trace(s"orig height: ${uberExtent.height}, width: ${uberExtent.width}") logger.trace(s"ideal sample height: $sampleHeight, ideal sample width: $sampleWidth") // Sanity check here - if the desired sample extent is larger than the source extent, just use the source extent - val widthDelta = (if (sampleWidth > uberExtent.width) uberExtent.width else sampleWidth) / 2 + val widthDelta = (if (sampleWidth > uberExtent.width) uberExtent.width else sampleWidth) / 2 val heightDelta = (if (sampleHeight > uberExtent.height) uberExtent.height else sampleHeight) / 2 val tl = Extent(uberExtent.xmin, uberExtent.ymax - heightDelta, uberExtent.xmin + widthDelta, uberExtent.ymax) @@ -45,12 +47,14 @@ object SampleUtils { (tl, tr, bl, br) } - /** Choose the largest cellsize with the minCells amount */ + /** + * Choose the largest cellsize with the minCells amount + */ final def chooseLargestCellSize(rasterExtents: NEL[RasterExtent], minCells: Int): CellSize = rasterExtents.reduceLeft { (chosenRE: RasterExtent, nextRE: RasterExtent) => val (chosenCS, nextCS) = chosenRE.cellSize -> nextRE.cellSize - val chosenSize = chosenCS.height * chosenCS.width - val nextSize = nextCS.height * nextCS.width + val chosenSize = chosenCS.height * chosenCS.width + val nextSize = nextCS.height * nextCS.width if (nextSize > chosenSize && nextRE.size > minCells) nextRE @@ -58,12 +62,14 @@ object SampleUtils { chosenRE }.cellSize - /** Choose the largest cellsize */ + /** + * Choose the largest cellsize + */ final def chooseLargestCellSize(nativeCellSizes: NEL[CellSize]): CellSize = nativeCellSizes .reduceLeft { (chosenCS: CellSize, nextCS: CellSize) => val chosenSize = chosenCS.height * chosenCS.width - val nextSize = nextCS.height * nextCS.width + val nextSize = nextCS.height * nextCS.width if (nextSize > chosenSize) nextCS @@ -71,12 +77,14 @@ object SampleUtils { chosenCS } - /** Choose the smallest cellsize */ + /** + * Choose the smallest cellsize + */ final def chooseSmallestCellSize(nativeCellSizes: NEL[CellSize]): CellSize = nativeCellSizes .reduceLeft { (chosenCS: CellSize, nextCS: CellSize) => val chosenSize = chosenCS.height * chosenCS.width - val nextSize = nextCS.height * nextCS.width + val nextSize = nextCS.height * nextCS.width if (nextSize < chosenSize) nextCS @@ -86,7 +94,7 @@ object SampleUtils { final def intersectExtents(extents: NEL[Extent]): Option[Extent] = extents.tail.foldLeft(Option(extents.head)) { - case (Some(ex1), ex2) => ex1 intersection ex2 + case (Some(ex1), ex2) => ex1.intersection(ex2) case _ => None } @@ -97,6 +105,6 @@ object SampleUtils { } final def unionExtents(extents: NEL[Extent]): Option[Extent] = - Some(extents.tail.foldLeft(extents.head)((ex1, ex2) => ex1 combine ex2)) + Some(extents.tail.foldLeft(extents.head)((ex1, ex2) => ex1.combine(ex2))) } diff --git a/core/src/main/scala/geotrellis/server/vlm/RasterSourceUtils.scala b/core/src/main/scala/geotrellis/server/vlm/RasterSourceUtils.scala index cd7c79cb..e3900e8d 100644 --- a/core/src/main/scala/geotrellis/server/vlm/RasterSourceUtils.scala +++ b/core/src/main/scala/geotrellis/server/vlm/RasterSourceUtils.scala @@ -70,7 +70,7 @@ trait RasterSourceUtils { 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 parseAuto(str: String): OverviewStrategy = parse("auto", str) def parseLevel(str: String): OverviewStrategy = parse("level", str) Decoder.decodeString.map { diff --git a/core/src/main/scala/geotrellis/server/vlm/geotiff/GeoTiffNode.scala b/core/src/main/scala/geotrellis/server/vlm/geotiff/GeoTiffNode.scala index 854fa6dc..ae63591b 100644 --- a/core/src/main/scala/geotrellis/server/vlm/geotiff/GeoTiffNode.scala +++ b/core/src/main/scala/geotrellis/server/vlm/geotiff/GeoTiffNode.scala @@ -47,7 +47,7 @@ object GeoTiffNode extends RasterSourceUtils { implicit def cogNodeRasterExtents[F[_]: Sync]: HasRasterExtents[F, GeoTiffNode] = { self => Sync[F].delay { val rs = RasterSource(s"${self.uri}") - NonEmptyList.fromListUnsafe(rs.resolutions map { + NonEmptyList.fromListUnsafe(rs.resolutions.map { RasterExtent(rs.extent, _) }) } @@ -69,7 +69,7 @@ object GeoTiffNode extends RasterSourceUtils { (z: Int, x: Int, y: Int) => Sync[F].delay { val layout = tmsLevels(z) - val key = SpatialKey(x, y) + val key = SpatialKey(x, y) val raster = Raster( RasterSource(self.uri.toString) .reproject(targetCRS) diff --git a/core/src/main/scala/geotrellis/server/vlm/geotiff/util/CogUtils.scala b/core/src/main/scala/geotrellis/server/vlm/geotiff/util/CogUtils.scala index 4a9e12c5..bde40534 100644 --- a/core/src/main/scala/geotrellis/server/vlm/geotiff/util/CogUtils.scala +++ b/core/src/main/scala/geotrellis/server/vlm/geotiff/util/CogUtils.scala @@ -35,12 +35,14 @@ object CogUtils { for (zoom <- 0 to 64) yield scheme.levelForZoom(zoom).layout }.toArray - /** Read GeoTiff from URI while caching the header bytes in memcache */ + /** + * Read GeoTiff from URI while caching the header bytes in memcache + */ def fromUri(uri: String): IO[GeoTiff[MultibandTile]] = { val cacheSize = 1 << 18 for { headerBytes <- RangeReaderUtils.fromUri(uri).map(_.readRange(0, cacheSize)) - rr <- RangeReaderUtils.fromUri(uri) + rr <- RangeReaderUtils.fromUri(uri) } yield { val crr = CacheRangeReader(rr, headerBytes) GeoTiffReader.readMultiband(crr, streaming = true) @@ -52,7 +54,7 @@ object CogUtils { def fetch(uri: String, zoom: Int, x: Int, y: Int, crs: CRS = WebMercator): IO[Raster[MultibandTile]] = CogUtils.fromUri(uri).flatMap { tiff => - val transform = Proj4Transform(tiff.crs, crs) + val transform = Proj4Transform(tiff.crs, crs) val inverseTransform = Proj4Transform(crs, tiff.crs) val tmsTileRE = RasterExtent( extent = tmsLevels(zoom).mapTransform.keyToExtent(x, y), @@ -60,7 +62,7 @@ object CogUtils { rows = 256 ) val tiffTileRE = ReprojectRasterExtent(tmsTileRE, inverseTransform) - val overview = tiff.getClosestOverview(tiffTileRE.cellSize, Auto(0)) + val overview = tiff.getClosestOverview(tiffTileRE.cellSize, Auto(0)) cropGeoTiff(overview, tiffTileRE.extent).map { raster => raster.reproject(tmsTileRE, transform, inverseTransform) @@ -75,9 +77,9 @@ object CogUtils { def cropGeoTiff[T <: CellGrid[Int]](tiff: GeoTiff[T], extent: Extent): IO[Raster[T]] = IO { if (extent.intersects(tiff.extent)) { - val bounds = tiff.rasterExtent.gridBoundsFor(extent) + val bounds = tiff.rasterExtent.gridBoundsFor(extent) val clipExtent = tiff.rasterExtent.extentFor(bounds) - val clip = tiff.crop(List(bounds)).next._2 + val clip = tiff.crop(List(bounds)).next._2 Raster(clip, clipExtent) } else { throw new java.lang.IllegalArgumentException( diff --git a/core/src/main/scala/geotrellis/server/vlm/geotiff/util/RangeReaderUtils.scala b/core/src/main/scala/geotrellis/server/vlm/geotiff/util/RangeReaderUtils.scala index 7de0a25c..b7553f71 100644 --- a/core/src/main/scala/geotrellis/server/vlm/geotiff/util/RangeReaderUtils.scala +++ b/core/src/main/scala/geotrellis/server/vlm/geotiff/util/RangeReaderUtils.scala @@ -59,7 +59,7 @@ object RangeReaderUtils { new HttpRangeReader(new URL(uri), false) case "s3" => - val s3Uri = new AmazonS3URI(java.net.URLDecoder.decode(uri, "UTF-8")) + val s3Uri = new AmazonS3URI(java.net.URLDecoder.decode(uri, "UTF-8")) val s3Client = S3ClientProducer.get() S3RangeReader(s3Uri.getBucket, s3Uri.getKey, s3Client) diff --git a/core/src/main/scala/geotrellis/store/query/QueryF.scala b/core/src/main/scala/geotrellis/store/query/QueryF.scala index cb82fce2..7bbff119 100644 --- a/core/src/main/scala/geotrellis/store/query/QueryF.scala +++ b/core/src/main/scala/geotrellis/store/query/QueryF.scala @@ -34,35 +34,41 @@ import java.time.ZonedDateTime object QueryF { - /** Tree leaves */ - @JsonCodec case class Or[A](left: A, right: A) extends QueryF[A] - @JsonCodec case class And[A](left: A, right: A) extends QueryF[A] - @JsonCodec case class Intersects[A](projectedGeometry: ProjectedGeometry) extends QueryF[A] - @JsonCodec case class Contains[A](projectedGeometry: ProjectedGeometry) extends QueryF[A] - @JsonCodec case class Covers[A](projectedGeometry: ProjectedGeometry) extends QueryF[A] - @JsonCodec case class At[A](time: ZonedDateTime, fieldName: String = "time") extends QueryF[A] + /** + * Tree leaves + */ + @JsonCodec case class Or[A](left: A, right: A) extends QueryF[A] + @JsonCodec case class And[A](left: A, right: A) extends QueryF[A] + @JsonCodec case class Intersects[A](projectedGeometry: ProjectedGeometry) extends QueryF[A] + @JsonCodec case class Contains[A](projectedGeometry: ProjectedGeometry) extends QueryF[A] + @JsonCodec case class Covers[A](projectedGeometry: ProjectedGeometry) extends QueryF[A] + @JsonCodec case class At[A](time: ZonedDateTime, fieldName: String = "time") extends QueryF[A] @JsonCodec case class Between[A](from: ZonedDateTime, to: ZonedDateTime, fieldName: String = "time") extends QueryF[A] - @JsonCodec case class WithName[A](name: String) extends QueryF[A] - @JsonCodec case class WithNames[A](names: Set[String]) extends QueryF[A] - @JsonCodec case class Nothing[A]() extends QueryF[A] - @JsonCodec case class All[A]() extends QueryF[A] - - /** Build Tree syntax */ - def or(left: Query, right: Query): Query = Or(left, right).fix - def and(left: Query, right: Query): Query = And(left, right).fix - def nothing: Query = Nothing().fix - def all: Query = All().fix - def withName(name: String): Query = WithName(name).fix - def withNames(names: Set[String]): Query = WithNames(names).fix - def intersects(projectedGeometry: ProjectedGeometry): Query = Intersects(projectedGeometry).fix - def contains(projectedGeometry: ProjectedGeometry): Query = Contains(projectedGeometry).fix - def covers(projectedGeometry: ProjectedGeometry): Query = Covers(projectedGeometry).fix + @JsonCodec case class WithName[A](name: String) extends QueryF[A] + @JsonCodec case class WithNames[A](names: Set[String]) extends QueryF[A] + @JsonCodec case class Nothing[A]() extends QueryF[A] + @JsonCodec case class All[A]() extends QueryF[A] + + /** + * Build Tree syntax + */ + def or(left: Query, right: Query): Query = Or(left, right).fix + def and(left: Query, right: Query): Query = And(left, right).fix + def nothing: Query = Nothing().fix + def all: Query = All().fix + def withName(name: String): Query = WithName(name).fix + def withNames(names: Set[String]): Query = WithNames(names).fix + def intersects(projectedGeometry: ProjectedGeometry): Query = Intersects(projectedGeometry).fix + def contains(projectedGeometry: ProjectedGeometry): Query = Contains(projectedGeometry).fix + def covers(projectedGeometry: ProjectedGeometry): Query = Covers(projectedGeometry).fix def at(time: ZonedDateTime, fieldName: String = "time"): Query = At(time, fieldName).fix def between(from: ZonedDateTime, to: ZonedDateTime, fieldName: String = "time"): Query = Between(from, to, fieldName).fix - /** Pattern functor for QueryF */ + /** + * Pattern functor for QueryF + */ implicit val queryFFunctor: Functor[QueryF] = new Functor[QueryF] { def map[A, B](fa: QueryF[A])(f: A => B): QueryF[B] = fa match { @@ -83,10 +89,10 @@ object QueryF { val algebraJson: Algebra[QueryF, Json] = Algebra(_.asJson) val unfolder: Json.Folder[QueryF[Json]] = new Json.Folder[QueryF[Json]] { - def onNull: QueryF[Json] = QueryF.Nothing() - def onBoolean(value: Boolean): QueryF[Json] = QueryF.Nothing() - def onNumber(value: JsonNumber): QueryF[Json] = QueryF.Nothing() - def onString(value: String): QueryF[Json] = QueryF.Nothing() + def onNull: QueryF[Json] = QueryF.Nothing() + def onBoolean(value: Boolean): QueryF[Json] = QueryF.Nothing() + def onNumber(value: JsonNumber): QueryF[Json] = QueryF.Nothing() + def onString(value: String): QueryF[Json] = QueryF.Nothing() def onArray(value: Vector[Json]): QueryF[Json] = QueryF.Nothing() def onObject(value: JsonObject): QueryF[Json] = value.asJson @@ -96,7 +102,9 @@ object QueryF { val coalgebraJson: Coalgebra[QueryF, Json] = Coalgebra(_.foldWith(unfolder)) - /** Coalgebras that replace certain nodes */ + /** + * Coalgebras that replace certain nodes + */ def coalgebraOverrideName(name: String): Coalgebra[QueryF, Query] = Coalgebra { case WithName(_) => WithName(name) @@ -132,9 +140,9 @@ object QueryF { case _ => true } - def asJson(query: Query): Json = scheme.cata(algebraJson).apply(query) - def fromJson(json: Json): Query = scheme.ana(coalgebraJson).apply(json) - def isTemporal(query: Query): Boolean = scheme.cata(algebraIsTemporal).apply(query) - def isUniversal(query: Query): Boolean = scheme.cata(algebraIsUniversal).apply(query) + def asJson(query: Query): Json = scheme.cata(algebraJson).apply(query) + def fromJson(json: Json): Query = scheme.ana(coalgebraJson).apply(json) + def isTemporal(query: Query): Boolean = scheme.cata(algebraIsTemporal).apply(query) + def isUniversal(query: Query): Boolean = scheme.cata(algebraIsUniversal).apply(query) def overrideName(query: Query, name: String): Query = scheme.ana(coalgebraOverrideName(name)).apply(query) } diff --git a/core/src/main/scala/geotrellis/store/query/RasterSourceRepository.scala b/core/src/main/scala/geotrellis/store/query/RasterSourceRepository.scala index 5b10f14f..cbbac8f4 100644 --- a/core/src/main/scala/geotrellis/store/query/RasterSourceRepository.scala +++ b/core/src/main/scala/geotrellis/store/query/RasterSourceRepository.scala @@ -31,7 +31,9 @@ case class RasterSourceRepository[T <: RasterSource](store: List[T]) extends Rep object RasterSourceRepository { import geotrellis.store.query.QueryF._ - /** Algebra that can work with List[T] */ + /** + * Algebra that can work with List[T] + */ def algebra[T <: RasterSource]: Algebra[QueryF, List[T] => List[T]] = Algebra { case Nothing() => _ => Nil @@ -58,15 +60,19 @@ object RasterSourceRepository { case Contains(e) => _.filter(_.projectedGeometry.covers(e)) case And(e1, e2) => list => - val left = e1(list); left intersect e2(left) + val left = e1(list); left.intersect(e2(left)) case Or(e1, e2) => list => e1(list) ++ e2(list) } - /** An alias for [[scheme.cata]] since it can confuse people */ + /** + * An alias for [[scheme.cata]] since it can confuse people + */ def eval[T <: RasterSource](query: Query)(list: List[T]): List[T] = scheme.cata(algebra[T]).apply(query)(list) - /** An alias for [[scheme.hylo]] since it can confuse people */ + /** + * An alias for [[scheme.hylo]] since it can confuse people + */ def eval[T <: RasterSource](json: Json)(list: List[T]): List[T] = scheme.hylo(algebra[T], QueryF.coalgebraJson).apply(json)(list) } diff --git a/core/src/main/scala/geotrellis/store/query/RepositoryM.scala b/core/src/main/scala/geotrellis/store/query/RepositoryM.scala index 02745f65..d6d6c6d9 100644 --- a/core/src/main/scala/geotrellis/store/query/RepositoryM.scala +++ b/core/src/main/scala/geotrellis/store/query/RepositoryM.scala @@ -27,7 +27,7 @@ trait RepositoryM[M[_], G[_], T] { self => def mapK[F[_]](f: M ~> F): RepositoryM[F, G, T] = new RepositoryM[F, G, T] { - def store: F[G[T]] = f(self.store) + def store: F[G[T]] = f(self.store) def find(query: Query): F[G[T]] = f(self.find(query)) } } @@ -35,7 +35,7 @@ trait RepositoryM[M[_], G[_], T] { self => object RepositoryM { def empty[M[_]: Applicative, G[_]: MonoidK, T]: RepositoryM[M, G, T] = new RepositoryM[M, G, T] { - def store: M[G[T]] = MonoidK[G].empty[T].pure[M] + def store: M[G[T]] = MonoidK[G].empty[T].pure[M] def find(query: Query): M[G[T]] = store } @@ -44,7 +44,7 @@ object RepositoryM { def empty: RepositoryM[M, G, T] = RepositoryM.empty[M, G, T] def combine(l: RepositoryM[M, G, T], r: RepositoryM[M, G, T]): RepositoryM[M, G, T] = new RepositoryM[M, G, T] { - def store: M[G[T]] = l.store.map2(r.store)(_ <+> _) + def store: M[G[T]] = l.store.map2(r.store)(_ <+> _) def find(query: Query): M[G[T]] = l.find(query).map2(r.find(query))(_ <+> _) } } diff --git a/core/src/main/scala/geotrellis/store/query/package.scala b/core/src/main/scala/geotrellis/store/query/package.scala index 587d82d9..1dd0cb3e 100644 --- a/core/src/main/scala/geotrellis/store/query/package.scala +++ b/core/src/main/scala/geotrellis/store/query/package.scala @@ -26,45 +26,45 @@ import io.circe.{Decoder, Encoder} import java.time.ZonedDateTime package object query { - type Query = Fix[QueryF] + type Query = Fix[QueryF] type Repository[T] = RepositoryM[Id, List, T] implicit val queryEncoder: Encoder[Query] = Encoder.encodeJson.contramap(QueryF.asJson) implicit val queryDecoder: Decoder[Query] = Decoder.decodeJson.map(QueryF.fromJson) implicit class QueryOps(val self: Query) extends AnyVal { - def or(right: Query): Query = QueryF.or(self, right) - def and(right: Query): Query = QueryF.and(self, right) - def isTemporal: Boolean = QueryF.isTemporal(self) - def nonTemporal: Boolean = !isTemporal - def isUniversal: Boolean = QueryF.isUniversal(self) - def nonUniversal: Boolean = !isUniversal + def or(right: Query): Query = QueryF.or(self, right) + def and(right: Query): Query = QueryF.and(self, right) + def isTemporal: Boolean = QueryF.isTemporal(self) + def nonTemporal: Boolean = !isTemporal + def isUniversal: Boolean = QueryF.isUniversal(self) + def nonUniversal: Boolean = !isUniversal def overrideName(name: String): Query = QueryF.overrideName(self, name) } - def or(left: Query, right: Query): Query = QueryF.or(left, right) - def and(left: Query, right: Query): Query = QueryF.and(left, right) - def nothing: Query = QueryF.nothing - def all: Query = QueryF.all - def withName(name: String): Query = QueryF.withName(name) - def withNames(names: Set[String]): Query = QueryF.withNames(names) - def intersects(projectedGeometry: ProjectedGeometry): Query = QueryF.intersects(projectedGeometry) - def contains(projectedGeometry: ProjectedGeometry): Query = QueryF.contains(projectedGeometry) - def covers(projectedGeometry: ProjectedGeometry): Query = QueryF.covers(projectedGeometry) + def or(left: Query, right: Query): Query = QueryF.or(left, right) + def and(left: Query, right: Query): Query = QueryF.and(left, right) + def nothing: Query = QueryF.nothing + def all: Query = QueryF.all + def withName(name: String): Query = QueryF.withName(name) + def withNames(names: Set[String]): Query = QueryF.withNames(names) + def intersects(projectedGeometry: ProjectedGeometry): Query = QueryF.intersects(projectedGeometry) + def contains(projectedGeometry: ProjectedGeometry): Query = QueryF.contains(projectedGeometry) + def covers(projectedGeometry: ProjectedGeometry): Query = QueryF.covers(projectedGeometry) def at(time: ZonedDateTime, fieldName: String = "time"): Query = QueryF.at(time, fieldName) def between(from: ZonedDateTime, to: ZonedDateTime, fieldName: String = "time"): Query = QueryF.between(from, to, fieldName) implicit class RasterSourceOps(self: RasterSource) { - def projectedExtent: ProjectedExtent = ProjectedExtent(self.extent, self.crs) + def projectedExtent: ProjectedExtent = ProjectedExtent(self.extent, self.crs) def projectedGeometry: ProjectedGeometry = ProjectedGeometry(projectedExtent) } implicit class ProjectedExtentOps(val self: ProjectedExtent) extends AnyVal { def intersects(projectedExtent: ProjectedExtent): Boolean = self.extent.intersects(projectedExtent.reproject(self.crs)) - def covers(projectedExtent: ProjectedExtent): Boolean = self.extent.covers(projectedExtent.reproject(self.crs)) - def contains(projectedExtent: ProjectedExtent): Boolean = self.extent.contains(projectedExtent.reproject(self.crs)) + def covers(projectedExtent: ProjectedExtent): Boolean = self.extent.covers(projectedExtent.reproject(self.crs)) + def contains(projectedExtent: ProjectedExtent): Boolean = self.extent.contains(projectedExtent.reproject(self.crs)) } implicit class RepositoryMOps[M[_], G[_], T](val self: RepositoryM[M, G, T]) extends AnyVal { diff --git a/core/src/main/scala/geotrellis/store/query/vector/ProjectedGeometry.scala b/core/src/main/scala/geotrellis/store/query/vector/ProjectedGeometry.scala index d5721d3f..19a00635 100644 --- a/core/src/main/scala/geotrellis/store/query/vector/ProjectedGeometry.scala +++ b/core/src/main/scala/geotrellis/store/query/vector/ProjectedGeometry.scala @@ -25,8 +25,8 @@ case class ProjectedGeometry(geometry: Geometry, crs: CRS) { def reproject(dest: CRS): ProjectedGeometry = ProjectedGeometry(geometry.reproject(crs, dest), dest) def intersects(that: ProjectedGeometry): Boolean = geometry.intersects(that.reproject(crs).geometry) - def covers(that: ProjectedGeometry): Boolean = geometry.covers(that.reproject(crs).geometry) - def contains(that: ProjectedGeometry): Boolean = geometry.contains(that.reproject(crs).geometry) + def covers(that: ProjectedGeometry): Boolean = geometry.covers(that.reproject(crs).geometry) + def contains(that: ProjectedGeometry): Boolean = geometry.contains(that.reproject(crs).geometry) def toProjectedExtent: ProjectedExtent = ProjectedExtent(geometry.extent, crs) } diff --git a/core/src/test/scala/geotrellis/server/ExtentMergeStrategyTest.scala b/core/src/test/scala/geotrellis/server/ExtentMergeStrategyTest.scala index 511ffad0..e70eb8c7 100644 --- a/core/src/test/scala/geotrellis/server/ExtentMergeStrategyTest.scala +++ b/core/src/test/scala/geotrellis/server/ExtentMergeStrategyTest.scala @@ -33,9 +33,9 @@ import org.scalatest.matchers.should.Matchers class ExtentMergeStrategyTest extends AnyFunSuite with Matchers { test("total overlap - intersection") { - val e1 = Extent(0, 0, 100, 100) - val e2 = Extent(50, 50, 150, 150) - val e3 = Extent(75, 75, 90, 90) + val e1 = Extent(0, 0, 100, 100) + val e2 = Extent(50, 50, 150, 150) + val e3 = Extent(75, 75, 90, 90) val extents = List(e1, e2, e3) extents.permutations.toList.map { permutation => assert(SampleUtils.intersectExtents(NEL.fromListUnsafe(permutation)) == Some(Extent(75, 75, 90, 90))) @@ -43,9 +43,9 @@ class ExtentMergeStrategyTest extends AnyFunSuite with Matchers { } test("partial overlap - intersection") { - val e1 = Extent(0, 0, 100, 100) - val e2 = Extent(50, 50, 150, 150) - val e3 = Extent(125, 125, 200, 200) + val e1 = Extent(0, 0, 100, 100) + val e2 = Extent(50, 50, 150, 150) + val e3 = Extent(125, 125, 200, 200) val extents = List(e1, e2, e3) extents.permutations.toList.map { permutation => assert(SampleUtils.intersectExtents(NEL.fromListUnsafe(permutation)) == None) @@ -53,9 +53,9 @@ class ExtentMergeStrategyTest extends AnyFunSuite with Matchers { } test("no overlap - intersection") { - val e1 = Extent(0, 0, 100, 100) - val e2 = Extent(125, 125, 200, 200) - val e3 = Extent(225, 225, 300, 300) + val e1 = Extent(0, 0, 100, 100) + val e2 = Extent(125, 125, 200, 200) + val e3 = Extent(225, 225, 300, 300) val extents = List(e1, e2, e3) extents.permutations.toList.map { permutation => assert(SampleUtils.intersectExtents(NEL.fromListUnsafe(permutation)) == None) @@ -63,34 +63,34 @@ class ExtentMergeStrategyTest extends AnyFunSuite with Matchers { } test("total overlap - union") { - val e1 = Extent(0, 0, 100, 100) - val e2 = Extent(50, 50, 150, 150) - val e3 = Extent(75, 75, 90, 90) + val e1 = Extent(0, 0, 100, 100) + val e2 = Extent(50, 50, 150, 150) + val e3 = Extent(75, 75, 90, 90) val extents = List(e1, e2, e3) extents.permutations.toList.map { permutation => - assert((extents map { SampleUtils.unionExtents(NEL.fromListUnsafe(permutation)).get contains _ }) reduce { + assert(extents.map { SampleUtils.unionExtents(NEL.fromListUnsafe(permutation)).get contains _ }.reduce { _ && _ }) } } test("partial overlap - union") { - val e1 = Extent(0, 0, 100, 100) - val e2 = Extent(50, 50, 150, 150) - val e3 = Extent(125, 125, 200, 200) + val e1 = Extent(0, 0, 100, 100) + val e2 = Extent(50, 50, 150, 150) + val e3 = Extent(125, 125, 200, 200) val extents = List(e1, e2, e3) extents.permutations.toList.map { permutation => - assert((extents map { SampleUtils.unionExtents(NEL.fromListUnsafe(permutation)).get contains _ }) reduce { + assert(extents.map { SampleUtils.unionExtents(NEL.fromListUnsafe(permutation)).get contains _ }.reduce { _ && _ }) } } test("no overlap - union") { - val e1 = Extent(0, 0, 100, 100) - val e2 = Extent(125, 125, 200, 200) - val e3 = Extent(225, 225, 300, 300) + val e1 = Extent(0, 0, 100, 100) + val e2 = Extent(125, 125, 200, 200) + val e3 = Extent(225, 225, 300, 300) val extents = List(e1, e2, e3) extents.permutations.toList.map { permutation => - assert((extents map { SampleUtils.unionExtents(NEL.fromListUnsafe(permutation)).get contains _ }) reduce { + assert(extents.map { SampleUtils.unionExtents(NEL.fromListUnsafe(permutation)).get contains _ }.reduce { _ && _ }) } diff --git a/core/src/test/scala/geotrellis/server/HistogramHeuristicsTest.scala b/core/src/test/scala/geotrellis/server/HistogramHeuristicsTest.scala index 5243e53e..633995cb 100644 --- a/core/src/test/scala/geotrellis/server/HistogramHeuristicsTest.scala +++ b/core/src/test/scala/geotrellis/server/HistogramHeuristicsTest.scala @@ -30,7 +30,7 @@ class HistogramHeuristicsTest extends AnyFunSuite with Matchers { test("extents sampled from within overall extent") { val random = new Random(1337) for (x <- 0 to 1000) { - val scale = scala.math.abs(random.nextInt) + val scale = scala.math.abs(random.nextInt) val randomXMin = random.nextDouble val randomYMin = random.nextDouble @@ -39,9 +39,9 @@ class HistogramHeuristicsTest extends AnyFunSuite with Matchers { val randomYMaxOffset = random.nextInt(1000).toDouble + 1 val uberExtent = Extent(randomXMin, randomYMin, randomXMin + randomXMaxOffset, randomYMin + randomYMaxOffset) - val cs = CellSize(10 * random.nextDouble, 10 * random.nextDouble) - val maxCells = 4000 - val sample = SampleUtils.sampleRasterExtent(uberExtent, cs, maxCells) + val cs = CellSize(10 * random.nextDouble, 10 * random.nextDouble) + val maxCells = 4000 + val sample = SampleUtils.sampleRasterExtent(uberExtent, cs, maxCells) assert(uberExtent.covers(sample._1), s"$uberExtent must cover ${sample._1}") assert(uberExtent.covers(sample._2), s"$uberExtent must cover ${sample._2}") assert(uberExtent.covers(sample._3), s"$uberExtent must cover ${sample._3}") @@ -50,8 +50,8 @@ class HistogramHeuristicsTest extends AnyFunSuite with Matchers { } test("ability to sample for large cellsizes") { - val e = Extent(504885.0, 3872385.0, 733815.0, 4105515.0) - val cs = CellSize(479.937106918239, 479.69135802469134) + val e = Extent(504885.0, 3872385.0, 733815.0, 4105515.0) + val cs = CellSize(479.937106918239, 479.69135802469134) val samples = SampleUtils.sampleRasterExtent(e, cs, 255) assert(e.contains(samples._1), "overall extent must contain sample extent") assert(e.contains(samples._2), "overall extent must contain sample extent") @@ -61,10 +61,10 @@ class HistogramHeuristicsTest extends AnyFunSuite with Matchers { test("sampling budget should check all 4 corners") { val uberExtent = Extent(0, 0, 3, 3) - val tl = Extent(0, 2, 1, 3) - val tr = Extent(2, 2, 3, 3) - val bl = Extent(0, 0, 1, 1) - val br = Extent(2, 0, 3, 1) + val tl = Extent(0, 2, 1, 3) + val tr = Extent(2, 2, 3, 3) + val bl = Extent(0, 0, 1, 1) + val br = Extent(2, 0, 3, 1) assert( SampleUtils.sampleRasterExtent(uberExtent, cs = CellSize(1, 1), maxCells = 4) == (tl, tr, bl, br), "Expected one unit of cellsize in each corner of Extent" diff --git a/core/src/test/scala/geotrellis/server/LayerExtentTest.scala b/core/src/test/scala/geotrellis/server/LayerExtentTest.scala index d5059294..38d70685 100644 --- a/core/src/test/scala/geotrellis/server/LayerExtentTest.scala +++ b/core/src/test/scala/geotrellis/server/LayerExtentTest.scala @@ -27,11 +27,11 @@ import org.scalatest.matchers.should.Matchers class LayerExtentTest extends AnyFunSuite with Matchers { test("ability to read a selected extent") { - val rt = ResourceTile("8x8.tif") + val rt = ResourceTile("8x8.tif") val eval = LayerExtent.identity[IO, ResourceTile](rt) // We'll sample such that the bottom row (from 56 to 64) are excised from the result - val sampled = eval(Extent(0, 1, 8, 8), CellSize(1, 1).some).unsafeRunSync - val sample = sampled.toOption.get.band(0).toArray() + val sampled = eval(Extent(0, 1, 8, 8), CellSize(1, 1).some).unsafeRunSync + val sample = sampled.toOption.get.band(0).toArray() val sampleSum = sample.sum assert(sampleSum == 1596, s"Expected sum of 1596, got $sampleSum") } diff --git a/core/src/test/scala/geotrellis/server/NoDataHandlingTest.scala b/core/src/test/scala/geotrellis/server/NoDataHandlingTest.scala index d8f40413..8e35c104 100644 --- a/core/src/test/scala/geotrellis/server/NoDataHandlingTest.scala +++ b/core/src/test/scala/geotrellis/server/NoDataHandlingTest.scala @@ -22,25 +22,21 @@ import cats.effect.IO import com.azavea.maml.ast._ import com.azavea.maml.eval._ -import scala.concurrent.ExecutionContext - import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers class NoDataHandlingTest extends AnyFunSuite with Matchers with TileAsSourceImplicits { - implicit val cs = cats.effect.IO.contextShift(ExecutionContext.global) - val expr = Addition(List(RasterVar("t1"), RasterVar("t2"))) val eval = LayerTms.curried(expr, ConcurrentInterpreter.DEFAULT[IO], None) test( "NODATA should be respected - user-defined, integer-based source celltype" ) { - val t1 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) - val t2 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) + val t1 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) + val t2 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) val paramMap = Map("t1" -> t1, "t2" -> t2) // We'll sample such that the bottom row (from 56 to 64) are excised from the result - val res = eval(paramMap, 0, 0, 0).unsafeRunSync + val res = eval(paramMap, 0, 0, 0).unsafeRunSync val tileRes = res.toOption.get.band(0) assert( tileRes.toArrayDouble.head.isNaN, @@ -49,11 +45,11 @@ class NoDataHandlingTest extends AnyFunSuite with Matchers with TileAsSourceImpl } test("NODATA should be respected - different source celltypes") { - val t1 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) - val t2 = DoubleUserDefinedNoDataArrayTile((1 to 100).map(_.toDouble).toArray, 10, 10, DoubleUserDefinedNoDataCellType(2.0)) + val t1 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) + val t2 = DoubleUserDefinedNoDataArrayTile((1 to 100).map(_.toDouble).toArray, 10, 10, DoubleUserDefinedNoDataCellType(2.0)) val paramMap = Map("t1" -> t1, "t2" -> t2) // We'll sample such that the bottom row (from 56 to 64) are excised from the result - val res = eval(paramMap, 0, 0, 0).unsafeRunSync + val res = eval(paramMap, 0, 0, 0).unsafeRunSync val tileRes = res.toOption.get.band(0) assert( tileRes.toArrayDouble.apply(0).isNaN, diff --git a/core/src/test/scala/geotrellis/server/ResourceTile.scala b/core/src/test/scala/geotrellis/server/ResourceTile.scala index 4b6a2fda..a900ea26 100644 --- a/core/src/test/scala/geotrellis/server/ResourceTile.scala +++ b/core/src/test/scala/geotrellis/server/ResourceTile.scala @@ -60,7 +60,7 @@ object ResourceTile extends RasterSourceUtils { } .toIO { new Exception( - s"No tile avail for RasterExtent: RasterExtent(${(extent, cs)})" + s"No tile avail for RasterExtent: RasterExtent(${(extent, cellSize)})" ) } } @@ -74,7 +74,7 @@ object ResourceTile extends RasterSourceUtils { ): IO[NEL[RasterExtent]] = IO { val rs = RasterSource(self.uri) - rs.resolutions.map(RasterExtent(rs.extent, _)).toNel getOrElse { + rs.resolutions.map(RasterExtent(rs.extent, _)).toNel.getOrElse { throw new Exception("no resolutions") } } diff --git a/core/src/test/scala/geotrellis/server/package.scala b/core/src/test/scala/geotrellis/server/package.scala index 0d1def0d..524a8de1 100644 --- a/core/src/test/scala/geotrellis/server/package.scala +++ b/core/src/test/scala/geotrellis/server/package.scala @@ -17,13 +17,11 @@ package geotrellis import cats.effect.IO -import io.chrisdavenport.log4cats.Logger -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger - -import scala.concurrent.ExecutionContext.Implicits.global +import cats.effect.unsafe.IORuntime +import org.typelevel.log4cats.Logger +import org.typelevel.log4cats.slf4j.Slf4jLogger package object server { - implicit val cs = IO.contextShift(global) - implicit val logger: Logger[IO] = Slf4jLogger.getLogger + implicit val runtime: IORuntime = cats.effect.unsafe.implicits.global } diff --git a/core/src/test/scala/geotrellis/store/query/QueryFSpec.scala b/core/src/test/scala/geotrellis/store/query/QueryFSpec.scala index 646c1c55..7d0eeb8f 100644 --- a/core/src/test/scala/geotrellis/store/query/QueryFSpec.scala +++ b/core/src/test/scala/geotrellis/store/query/QueryFSpec.scala @@ -32,12 +32,12 @@ class QueryFSpec extends AnyFunSpec with Matchers { describe("QueryF evaluation specs") { def dtFromMonth(month: Int): ZonedDateTime = ZonedDateTime.of(2020, month, 1, 0, 0, 1, 0, ZoneOffset.UTC) - val extent = ProjectedGeometry(Extent(0, 0, 2, 2), LatLng) + val extent = ProjectedGeometry(Extent(0, 0, 2, 2), LatLng) val extent2 = ProjectedGeometry(Extent(1, 1, 4, 4), LatLng) - val dt = dtFromMonth(1) + val dt = dtFromMonth(1) it("should convert AST into json") { - val query = (intersects(extent) and intersects(extent2)) and at(dt) + val query = intersects(extent).and(intersects(extent2)).and(at(dt)) val actual = query.asJson val expected = @@ -216,21 +216,21 @@ class QueryFSpec extends AnyFunSpec with Matchers { |} |""".stripMargin).valueOr(throw _) - val expected = (intersects(extent) and intersects(extent2)) and at(dt) - val actual = json.as[Query].valueOr(throw _) + val expected = intersects(extent).and(intersects(extent2)).and(at(dt)) + val actual = json.as[Query].valueOr(throw _) actual shouldBe expected } it("should filter raster sources catalog both using JSON and direct AST query representations") { def dtFromMonth(month: Int): ZonedDateTime = ZonedDateTime.of(2020, month, 1, 0, 0, 1, 0, ZoneOffset.UTC) - val dt1 = dtFromMonth(1) - val dt2 = dtFromMonth(2) - val dt3 = dtFromMonth(3) - val ex1 = ProjectedExtent(Extent(0, 0, 2, 2), LatLng) - val ex2 = ProjectedExtent(Extent(1, 1, 4, 4), LatLng) - val ex3 = ProjectedExtent(Extent(2, 2, 5, 5), LatLng) - val ex4 = ProjectedExtent(Extent(6, 6, 10, 10), LatLng) + val dt1 = dtFromMonth(1) + val dt2 = dtFromMonth(2) + val dt3 = dtFromMonth(3) + val ex1 = ProjectedExtent(Extent(0, 0, 2, 2), LatLng) + val ex2 = ProjectedExtent(Extent(1, 1, 4, 4), LatLng) + val ex3 = ProjectedExtent(Extent(2, 2, 5, 5), LatLng) + val ex4 = ProjectedExtent(Extent(6, 6, 10, 10), LatLng) val store = EmptyRasterSource("first", ex1, dt1.some) :: @@ -238,14 +238,14 @@ class QueryFSpec extends AnyFunSpec with Matchers { EmptyRasterSource("third", ex3, dt2.some) :: EmptyRasterSource("fourth", ex4, dt3.some) :: Nil - val query = (intersects(ProjectedGeometry(ex2)) and intersects(ProjectedGeometry(ex3))) and at(dt2) + val query = intersects(ProjectedGeometry(ex2)).and(intersects(ProjectedGeometry(ex3))).and(at(dt2)) // scheme.cata(RasterSourceRepository.algebra[EmptyRasterSource]).apply(query)(store) val result = RasterSourceRepository.eval(query)(store) result shouldBe EmptyRasterSource("second", ex2, dt2.some) :: EmptyRasterSource("third", ex3, dt2.some) :: Nil val repository = RasterSourceRepository(store) - val rresult = repository.find(query) + val rresult = repository.find(query) rresult shouldBe result diff --git a/effects/src/main/scala/geotrellis/raster/effects/MosaicRasterSourceF.scala b/effects/src/main/scala/geotrellis/raster/effects/MosaicRasterSourceF.scala index a9bfd51f..40656307 100644 --- a/effects/src/main/scala/geotrellis/raster/effects/MosaicRasterSourceF.scala +++ b/effects/src/main/scala/geotrellis/raster/effects/MosaicRasterSourceF.scala @@ -64,7 +64,9 @@ abstract class MosaicRasterSourceF[F[_]: Monad: Parallel] extends RasterSourceF[ cellTypes >>= { _.tail.foldLeft(cellTypes.map(_.head))((l, r) => (l, r.pure[F]).mapN(_ union _)) } } - /** All available RasterSources metadata. */ + /** + * All available RasterSources metadata. + */ def metadata: F[MosaicMetadata] = ( name.pure[F], crs, @@ -155,8 +157,8 @@ object MosaicRasterSourceF { // Option[Raster[_]]s later implicit val rasterSemigroup: Semigroup[Raster[MultibandTile]] = { (l: Raster[MultibandTile], r: Raster[MultibandTile]) => val targetRE = - RasterExtent(l.rasterExtent.extent combine r.rasterExtent.extent, List(l.rasterExtent.cellSize, r.rasterExtent.cellSize).maxBy(_.resolution)) - l.resample(targetRE) merge r.resample(targetRE) + RasterExtent(l.rasterExtent.extent.combine(r.rasterExtent.extent), List(l.rasterExtent.cellSize, r.rasterExtent.cellSize).maxBy(_.resolution)) + l.resample(targetRE).merge(r.resample(targetRE)) } implicit def gridExtentSemigroup[N: Integral]: Semigroup[GridExtent[N]] = { (l: GridExtent[N], r: GridExtent[N]) => @@ -166,8 +168,8 @@ object MosaicRasterSourceF { throw GeoAttrsError(s"illegal cellheights: ${l.cellheight} and ${r.cellheight}") val newExtent = l.extent.combine(r.extent) - val newRows = Integral[N].fromDouble(math.round(newExtent.height / l.cellheight).toDouble) - val newCols = Integral[N].fromDouble(math.round(newExtent.width / l.cellwidth).toDouble) + val newRows = Integral[N].fromDouble(math.round(newExtent.height / l.cellheight).toDouble) + val newCols = Integral[N].fromDouble(math.round(newExtent.width / l.cellwidth).toDouble) new GridExtent[N](newExtent, l.cellwidth, l.cellheight, newCols, newRows) } @@ -183,9 +185,9 @@ object MosaicRasterSourceF { ): MosaicRasterSourceF[F] = new MosaicRasterSourceF[F] { val sources: F[NonEmptyList[RasterSourceF[F]]] = sourcesList - val crs: F[CRS] = targetCRS - def gridExtent: F[GridExtent[Long]] = targetGridExtent - val name: SourceName = sourceName + val crs: F[CRS] = targetCRS + def gridExtent: F[GridExtent[Long]] = targetGridExtent + val name: SourceName = sourceName } /** @@ -197,13 +199,15 @@ object MosaicRasterSourceF { targetCRS: F[CRS], sourceName: SourceName = EmptyName ): MosaicRasterSourceF[F] = { - val combinedExtent = sourcesList >>= { _.parTraverse(_.extent).map(_.toList.reduce(_ combine _)) } - val minCellSize = sourcesList >>= { _.parTraverse(_.cellSize).map(_.toList.maxBy(_.resolution)) } + val combinedExtent = sourcesList >>= { _.parTraverse(_.extent).map(_.toList.reduce(_ combine _)) } + val minCellSize = sourcesList >>= { _.parTraverse(_.cellSize).map(_.toList.maxBy(_.resolution)) } val combinedGridExtent = (combinedExtent, minCellSize).mapN(GridExtent[Long]) instanceGridExtent(sourcesList, targetCRS, combinedGridExtent, sourceName) } - /** All apply methods reproject the input sourcesList to the targetGridExtent */ + /** + * All apply methods reproject the input sourcesList to the targetGridExtent + */ def applyGridExtent[F[_]: Monad: Parallel]( sourcesList: F[NonEmptyList[RasterSourceF[F]]], targetCRS: F[CRS], @@ -233,7 +237,7 @@ object MosaicRasterSourceF { val crs: F[CRS] = targetCRS def gridExtent: F[GridExtent[Long]] = { val combinedExtent: F[Extent] = sources.flatMap(_.parTraverse(_.extent).map(_.reduceLeft(_ combine _))) - val minCellSize: F[CellSize] = sources.flatMap(_.parTraverse(_.cellSize).map(_.toList.maxBy(_.resolution))) + val minCellSize: F[CellSize] = sources.flatMap(_.parTraverse(_.cellSize).map(_.toList.maxBy(_.resolution))) (combinedExtent, minCellSize).mapN(GridExtent[Long]) } } diff --git a/effects/src/main/scala/geotrellis/raster/effects/MosaicRasterSourceIO.scala b/effects/src/main/scala/geotrellis/raster/effects/MosaicRasterSourceIO.scala index 783da21e..61d9cbbd 100644 --- a/effects/src/main/scala/geotrellis/raster/effects/MosaicRasterSourceIO.scala +++ b/effects/src/main/scala/geotrellis/raster/effects/MosaicRasterSourceIO.scala @@ -16,19 +16,17 @@ package geotrellis.raster.effects -import io.chrisdavenport.log4cats.Logger -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger - +import org.typelevel.log4cats.Logger +import org.typelevel.log4cats.slf4j.Slf4jLogger import geotrellis.raster.{MosaicRasterSource => MosaicRasterSourceS, _} import geotrellis.vector._ import geotrellis.raster.resample._ import geotrellis.proj4.CRS import geotrellis.raster.io.geotiff.OverviewStrategy import cats.syntax.parallel._ -import cats.syntax.flatMap._ import cats.data.NonEmptyList -import cats.effect.{ContextShift, IO} -import geotrellis.store.util.BlockingThreadPool +import cats.effect.IO +import cats.effect.unsafe.IORuntime /** * Single threaded instance of a reader for reading windows out of collections of rasters @@ -42,11 +40,11 @@ import geotrellis.store.util.BlockingThreadPool */ abstract class MosaicRasterSourceIO extends RasterSource { - implicit lazy val cs: ContextShift[IO] = IO.contextShift(BlockingThreadPool.executionContext) - implicit val logger: Logger[IO] = Slf4jLogger.getLogger + implicit def runtime: IORuntime + implicit val logger: Logger[IO] = Slf4jLogger.getLogger def sources: NonEmptyList[RasterSource] - lazy val sourcesIO: NonEmptyList[IO[RasterSource]] = sources.map(cs.shift >> IO(_)) + lazy val sourcesIO: NonEmptyList[IO[RasterSource]] = sources.map(IO.blocking(_)) def crs: CRS def gridExtent: GridExtent[Long] @@ -72,7 +70,9 @@ abstract class MosaicRasterSourceIO extends RasterSource { .map(_.toList.reduce(_ union _)) .unsafeRunSync() - /** All available RasterSources metadata. */ + /** + * All available RasterSources metadata. + */ def metadata: MosaicMetadata = MosaicMetadata(name, crs, bandCount, cellType, gridExtent, resolutions, sources) def attributes: Map[String, String] = Map.empty @@ -144,7 +144,7 @@ abstract class MosaicRasterSourceIO extends RasterSource { */ def relativeGridBounds(gb: GridBounds[Long], extent: Extent): GridBounds[Long] = { val GridBounds(colMin, rowMin, colMax, rowMax) = gb - val (sourceColOffset, sourceRowOffset) = gridExtent.mapToGrid(extent.xmin, extent.ymax) + val (sourceColOffset, sourceRowOffset) = gridExtent.mapToGrid(extent.xmin, extent.ymax) GridBounds( colMin - sourceColOffset, @@ -203,12 +203,13 @@ object MosaicRasterSourceIO { targetGridExtent: GridExtent[Long], sourceName: SourceName, stacAttributes: Map[String, String] - ): MosaicRasterSourceIO = + )(implicit ceRuntime: IORuntime): MosaicRasterSourceIO = new MosaicRasterSourceIO { - val sources: NonEmptyList[RasterSource] = sourcesList - val crs: CRS = targetCRS - val gridExtent: GridExtent[Long] = targetGridExtent - val name: SourceName = sourceName + implicit val runtime: IORuntime = ceRuntime + val sources: NonEmptyList[RasterSource] = sourcesList + val crs: CRS = targetCRS + val gridExtent: GridExtent[Long] = targetGridExtent + val name: SourceName = sourceName override val attributes: Map[String, String] = stacAttributes } @@ -221,21 +222,27 @@ object MosaicRasterSourceIO { targetCRS: CRS, sourceName: SourceName, stacAttributes: Map[String, String] - ): MosaicRasterSourceIO = { - val combinedExtent = sourcesList.map(_.extent).toList.reduce(_ combine _) - val minCellSize = sourcesList.map(_.cellSize).toList.maxBy(_.resolution) + )(implicit ceRuntime: IORuntime): MosaicRasterSourceIO = { + val combinedExtent = sourcesList.map(_.extent).toList.reduce(_ combine _) + val minCellSize = sourcesList.map(_.cellSize).toList.maxBy(_.resolution) val combinedGridExtent = GridExtent[Long](combinedExtent, minCellSize) instance(sourcesList, targetCRS, combinedGridExtent, sourceName, stacAttributes) } - def instance(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS): MosaicRasterSourceIO = + def instance(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS)(implicit ceRuntime: IORuntime): MosaicRasterSourceIO = instance(sourcesList, targetCRS, EmptyName, Map.empty) - def instance(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS, targetGridExtent: GridExtent[Long]): MosaicRasterSourceIO = + def instance(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS, targetGridExtent: GridExtent[Long])(implicit + ceRuntime: IORuntime + ): MosaicRasterSourceIO = instance(sourcesList, targetCRS, targetGridExtent, EmptyName, Map.empty) - /** All apply methods reproject the input sourcesList to the targetGridExtent */ - def apply(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS, targetGridExtent: GridExtent[Long]): MosaicRasterSourceIO = + /** + * All apply methods reproject the input sourcesList to the targetGridExtent + */ + def apply(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS, targetGridExtent: GridExtent[Long])(implicit + ceRuntime: IORuntime + ): MosaicRasterSourceIO = apply(sourcesList, targetCRS, targetGridExtent, EmptyName) def apply( @@ -243,27 +250,32 @@ object MosaicRasterSourceIO { targetCRS: CRS, targetGridExtent: GridExtent[Long], rasterSourceName: SourceName - ): MosaicRasterSourceIO = + )(implicit ceRuntime: IORuntime): MosaicRasterSourceIO = new MosaicRasterSourceIO { - val name = rasterSourceName - lazy val sources = sourcesList.map(cs.shift >> IO(_)).parTraverse(_.map(_.reprojectToGrid(targetCRS, gridExtent))).unsafeRunSync() - val crs = targetCRS + implicit val runtime: IORuntime = ceRuntime + val name = rasterSourceName + lazy val sources = sourcesList.map(IO.blocking(_)).parTraverse(_.map(_.reprojectToGrid(targetCRS, gridExtent))).unsafeRunSync()(runtime) + val crs = targetCRS val gridExtent: GridExtent[Long] = targetGridExtent } - def apply(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS): MosaicRasterSourceIO = + def apply(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS)(implicit ceRuntime: IORuntime): MosaicRasterSourceIO = apply(sourcesList, targetCRS, EmptyName) - def apply(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS, rasterSourceName: SourceName): MosaicRasterSourceIO = + def apply(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS, rasterSourceName: SourceName)(implicit + ceRuntime: IORuntime + ): MosaicRasterSourceIO = new MosaicRasterSourceIO { - val name = rasterSourceName - val sources = sourcesList.map(cs.shift >> IO(_)).parTraverse(_.map(_.reprojectToGrid(targetCRS, sourcesList.head.gridExtent))).unsafeRunSync() - val crs = targetCRS + implicit val runtime: IORuntime = ceRuntime + val name = rasterSourceName + val sources = + sourcesList.map(IO.blocking(_)).parTraverse(_.map(_.reprojectToGrid(targetCRS, sourcesList.head.gridExtent))).unsafeRunSync()(runtime) + val crs = targetCRS def gridExtent: GridExtent[Long] = { val reprojectedSources = sourcesIO.toList - val combinedExtent = reprojectedSources.parTraverse(_.map(_.extent)).map(_.reduce(_ combine _)).unsafeRunSync() - val minCellSize = reprojectedSources.parTraverse(_.map(_.cellSize)).map(_.maxBy(_.resolution)).unsafeRunSync() + val combinedExtent = reprojectedSources.parTraverse(_.map(_.extent)).map(_.reduce(_ combine _)).unsafeRunSync()(runtime) + val minCellSize = reprojectedSources.parTraverse(_.map(_.cellSize)).map(_.maxBy(_.resolution)).unsafeRunSync()(runtime) GridExtent[Long](combinedExtent, minCellSize) } } diff --git a/effects/src/main/scala/geotrellis/raster/effects/RasterMetadataF.scala b/effects/src/main/scala/geotrellis/raster/effects/RasterMetadataF.scala index 213ae02d..ac9de1b7 100644 --- a/effects/src/main/scala/geotrellis/raster/effects/RasterMetadataF.scala +++ b/effects/src/main/scala/geotrellis/raster/effects/RasterMetadataF.scala @@ -30,15 +30,15 @@ abstract class RasterMetadataF[F[_]: Monad] { def crs: F[CRS] def bandCount: F[Int] def cellType: F[CellType] - def size: F[Long] = (cols, rows).mapN(_ * _) - def dimensions: F[(Long, Long)] = (cols, rows).mapN((c, r) => (c, r)) + def size: F[Long] = (cols, rows).mapN(_ * _) + def dimensions: F[(Long, Long)] = (cols, rows).mapN((c, r) => (c, r)) def gridBounds: F[GridBounds[Long]] = (cols, rows).mapN { case (c, r) => GridBounds(0, 0, c - 1, r - 1) } - def cellSize: F[CellSize] = gridExtent.map(_.cellSize) + def cellSize: F[CellSize] = gridExtent.map(_.cellSize) def gridExtent: F[GridExtent[Long]] def resolutions: F[List[CellSize]] def extent: F[Extent] = gridExtent.map(_.extent) - def cols: F[Long] = gridExtent.map(_.cols) - def rows: F[Long] = gridExtent.map(_.rows) + def cols: F[Long] = gridExtent.map(_.cols) + def rows: F[Long] = gridExtent.map(_.rows) /** * Return the "base" metadata, usually it is a zero band metadata, a metadata that is valid for the entire source and for the zero band diff --git a/effects/src/main/scala/geotrellis/raster/effects/RasterSourceF.scala b/effects/src/main/scala/geotrellis/raster/effects/RasterSourceF.scala index 4dc22619..1b6a8ef2 100644 --- a/effects/src/main/scala/geotrellis/raster/effects/RasterSourceF.scala +++ b/effects/src/main/scala/geotrellis/raster/effects/RasterSourceF.scala @@ -31,7 +31,9 @@ import cats.instances.list._ abstract class RasterSourceF[F[_]: Monad] extends RasterMetadataF[F] with Serializable { - /** All available RasterSource metadata */ + /** + * All available RasterSource metadata + */ def metadata: F[_ <: RasterMetadata] protected def reprojection( diff --git a/effects/src/main/scala/geotrellis/raster/effects/UnsafeLift.scala b/effects/src/main/scala/geotrellis/raster/effects/UnsafeLift.scala index f3d357b3..d9a79522 100644 --- a/effects/src/main/scala/geotrellis/raster/effects/UnsafeLift.scala +++ b/effects/src/main/scala/geotrellis/raster/effects/UnsafeLift.scala @@ -22,7 +22,9 @@ import cats.effect.IO import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success, Try} -/** Type class that allows to handle unsafe calls */ +/** + * Type class that allows to handle unsafe calls + */ trait UnsafeLift[F[_]] { def apply[A](value: => A): F[A] } diff --git a/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffRasterSource.scala b/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffRasterSource.scala index a20e5e85..e3f8fca0 100644 --- a/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffRasterSource.scala +++ b/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffRasterSource.scala @@ -43,24 +43,28 @@ case class GeoTiffRasterSource[F[_]: Monad: UnsafeLift]( def name: GeoTiffPath = dataPath // memoize tiff, not useful only in a local fs case - @transient lazy val tiff: MultibandGeoTiff = GeoTiffReader.readMultiband(RangeReader(dataPath.value), streaming = true) + @transient lazy val tiff: MultibandGeoTiff = GeoTiffReader.readMultiband(RangeReader(dataPath.value), streaming = true) @transient lazy val tiffF: F[MultibandGeoTiff] = Option(baseTiff).flatten.getOrElse(UnsafeLift[F].apply(tiff)) - def bandCount: F[Int] = tiffF.map(_.bandCount) - def cellType: F[CellType] = dstCellType.fold(tiffF.map(_.cellType))(_.pure[F]) - def tags: F[Tags] = tiffF.map(_.tags) + def bandCount: F[Int] = tiffF.map(_.bandCount) + def cellType: F[CellType] = dstCellType.fold(tiffF.map(_.cellType))(_.pure[F]) + def tags: F[Tags] = tiffF.map(_.tags) def metadata: F[GeoTiffMetadata] = (name.pure[F], crs, bandCount, cellType, gridExtent, resolutions, tags).mapN(GeoTiffMetadata) - /** Returns the GeoTiff head tags. */ + /** + * Returns the GeoTiff head tags. + */ def attributes: F[Map[String, String]] = tags.map(_.headTags) - /** Returns the GeoTiff per band tags. */ + /** + * Returns the GeoTiff per band tags. + */ def attributesForBand(band: Int): F[Map[String, String]] = tags.map(_.bandTags.lift(band).getOrElse(Map.empty)) def crs: F[CRS] = tiffF.map(_.crs) lazy val gridExtent: F[GridExtent[Long]] = tiffF.map(_.rasterExtent.toGridType[Long]) - lazy val resolutions: F[List[CellSize]] = tiffF.map(tiff => tiff.cellSize :: tiff.overviews.map(_.cellSize)) + lazy val resolutions: F[List[CellSize]] = tiffF.map(tiff => tiff.cellSize :: tiff.overviews.map(_.cellSize)) def reprojection( targetCRS: CRS, @@ -78,7 +82,7 @@ case class GeoTiffRasterSource[F[_]: Monad: UnsafeLift]( def read(extent: Extent, bands: Seq[Int]): F[Raster[MultibandTile]] = (tiffF, gridExtent).tupled >>= { case (tiff, gridExtent) => - val bounds = gridExtent.gridBoundsFor(extent, clamp = false).toGridType[Int] + val bounds = gridExtent.gridBoundsFor(extent, clamp = false).toGridType[Int] val geoTiffTile = tiff.tile.asInstanceOf[GeoTiffMultibandTile] UnsafeLift[F].apply { diff --git a/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffReprojectRasterSource.scala b/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffReprojectRasterSource.scala index de7c8599..89b3e264 100644 --- a/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffReprojectRasterSource.scala +++ b/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffReprojectRasterSource.scala @@ -49,26 +49,30 @@ case class GeoTiffReprojectRasterSource[F[_]: Monad: UnsafeLift]( def name: GeoTiffPath = dataPath // memoize tiff, not useful only in a local fs case - @transient lazy val tiff: MultibandGeoTiff = GeoTiffReader.readMultiband(RangeReader(dataPath.value), streaming = true) + @transient lazy val tiff: MultibandGeoTiff = GeoTiffReader.readMultiband(RangeReader(dataPath.value), streaming = true) @transient lazy val tiffF: F[MultibandGeoTiff] = Option(baseTiff).flatten.getOrElse(UnsafeLift[F].apply(tiff)) - def bandCount: F[Int] = tiffF.map(_.bandCount) - def cellType: F[CellType] = dstCellType.fold(tiffF.map(_.cellType))(_.pure[F]) - def tags: F[Tags] = tiffF.map(_.tags) + def bandCount: F[Int] = tiffF.map(_.bandCount) + def cellType: F[CellType] = dstCellType.fold(tiffF.map(_.cellType))(_.pure[F]) + def tags: F[Tags] = tiffF.map(_.tags) def metadata: F[GeoTiffMetadata] = (name.pure[F], crs, bandCount, cellType, gridExtent, resolutions, tags).mapN(GeoTiffMetadata) - /** Returns the GeoTiff head tags. */ + /** + * Returns the GeoTiff head tags. + */ def attributes: F[Map[String, String]] = tags.map(_.headTags) - /** Returns the GeoTiff per band tags. */ + /** + * Returns the GeoTiff per band tags. + */ def attributesForBand(band: Int): F[Map[String, String]] = tags.map(_.bandTags.lift(band).getOrElse(Map.empty)) - lazy val crs: F[CRS] = Monad[F].pure(targetCRS) - protected lazy val baseCRS: F[CRS] = tiffF.map(_.crs) + lazy val crs: F[CRS] = Monad[F].pure(targetCRS) + protected lazy val baseCRS: F[CRS] = tiffF.map(_.crs) protected lazy val baseGridExtent: F[GridExtent[Long]] = tiffF.map(_.rasterExtent.toGridType[Long]) // TODO: remove transient notation with Proj4 1.1 release - @transient protected lazy val transform = (baseCRS, crs).mapN((baseCRS, crs) => Transform(baseCRS, crs)) + @transient protected lazy val transform = (baseCRS, crs).mapN((baseCRS, crs) => Transform(baseCRS, crs)) @transient protected lazy val backTransform = (crs, baseCRS).mapN((crs, baseCRS) => Transform(crs, baseCRS)) override lazy val gridExtent: F[GridExtent[Long]] = { @@ -118,7 +122,7 @@ case class GeoTiffReprojectRasterSource[F[_]: Monad: UnsafeLift]( val geoTiffTile = closestTiffOverview.tile.asInstanceOf[GeoTiffMultibandTile] val intersectingWindows = { for { - queryPixelBounds <- bounds + queryPixelBounds <- bounds targetPixelBounds <- queryPixelBounds.intersection(gridBounds) } yield { val targetExtent = gridExtent.extentFor(targetPixelBounds) @@ -134,7 +138,7 @@ case class GeoTiffReprojectRasterSource[F[_]: Monad: UnsafeLift]( // A tmp workaround for https://github.com/locationtech/proj4j/pull/29 // Stacktrace details: https://github.com/geotrellis/geotrellis-contrib/pull/206#pullrequestreview-260115791 - val sourceExtent = Proj4Transform.synchronized(bufferedTargetExtent.reprojectAsPolygon(backTransform, 0.001).getEnvelopeInternal) + val sourceExtent = Proj4Transform.synchronized(bufferedTargetExtent.reprojectAsPolygon(backTransform, 0.001).getEnvelopeInternal) val sourcePixelBounds = closestTiffOverview.rasterExtent.gridBoundsFor(sourceExtent) (sourcePixelBounds, targetRasterExtent) } @@ -144,8 +148,8 @@ case class GeoTiffReprojectRasterSource[F[_]: Monad: UnsafeLift]( .crop(intersectingWindows.keys.toSeq, bands.toArray) .map { case (sourcePixelBounds, tile) => val targetRasterExtent = intersectingWindows(sourcePixelBounds) - val sourceRaster = Raster(tile, closestTiffOverview.rasterExtent.extentFor(sourcePixelBounds)) - val rr = implicitly[RasterRegionReproject[MultibandTile]] + val sourceRaster = Raster(tile, closestTiffOverview.rasterExtent.extentFor(sourcePixelBounds)) + val rr = implicitly[RasterRegionReproject[MultibandTile]] rr.regionReproject( sourceRaster, baseCRS, diff --git a/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffResampleRasterSource.scala b/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffResampleRasterSource.scala index a8e8cb2a..d781d109 100644 --- a/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffResampleRasterSource.scala +++ b/effects/src/main/scala/geotrellis/raster/effects/geotiff/GeoTiffResampleRasterSource.scala @@ -43,21 +43,25 @@ case class GeoTiffResampleRasterSource[F[_]: Monad: UnsafeLift]( @transient private[raster] val baseTiff: Option[F[MultibandGeoTiff]] = None ) extends RasterSourceF[F] { def resampleMethod: Option[ResampleMethod] = Some(method) - def name: GeoTiffPath = dataPath + def name: GeoTiffPath = dataPath // memoize tiff, not useful only in a local fs case - @transient lazy val tiff: MultibandGeoTiff = GeoTiffReader.readMultiband(RangeReader(dataPath.value), streaming = true) + @transient lazy val tiff: MultibandGeoTiff = GeoTiffReader.readMultiband(RangeReader(dataPath.value), streaming = true) @transient lazy val tiffF: F[MultibandGeoTiff] = Option(baseTiff).flatten.getOrElse(UnsafeLift[F].apply(tiff)) - def bandCount: F[Int] = tiffF.map(_.bandCount) - def cellType: F[CellType] = dstCellType.fold(tiffF.map(_.cellType))(_.pure[F]) - def tags: F[Tags] = tiffF.map(_.tags) + def bandCount: F[Int] = tiffF.map(_.bandCount) + def cellType: F[CellType] = dstCellType.fold(tiffF.map(_.cellType))(_.pure[F]) + def tags: F[Tags] = tiffF.map(_.tags) def metadata: F[GeoTiffMetadata] = (name.pure[F], crs, bandCount, cellType, gridExtent, resolutions, tags).mapN(GeoTiffMetadata) - /** Returns the GeoTiff head tags. */ + /** + * Returns the GeoTiff head tags. + */ def attributes: F[Map[String, String]] = tags.map(_.headTags) - /** Returns the GeoTiff per band tags. */ + /** + * Returns the GeoTiff per band tags. + */ def attributesForBand(band: Int): F[Map[String, String]] = tags.map(_.bandTags.lift(band).getOrElse(Map.empty)) def crs: F[CRS] = tiffF.map(_.crs) @@ -110,7 +114,7 @@ case class GeoTiffResampleRasterSource[F[_]: Monad: UnsafeLift]( val windows = { for { - queryPixelBounds <- bounds + queryPixelBounds <- bounds targetPixelBounds <- queryPixelBounds.intersection(gridBounds) } yield { val targetExtent = gridExtent.extentFor(targetPixelBounds) @@ -118,15 +122,15 @@ case class GeoTiffResampleRasterSource[F[_]: Monad: UnsafeLift]( // so the resample would behave properly on borders // Buffer by half of CS to avoid resampling out of bounds val bufferedTargetExtent = targetExtent.buffer(cellSize.width / 2, cellSize.height / 2) - val sourcePixelBounds = closestTiffOverview.rasterExtent.gridBoundsFor(bufferedTargetExtent) - val targetRasterExtent = RasterExtent(targetExtent, targetPixelBounds.width.toInt, targetPixelBounds.height.toInt) + val sourcePixelBounds = closestTiffOverview.rasterExtent.gridBoundsFor(bufferedTargetExtent) + val targetRasterExtent = RasterExtent(targetExtent, targetPixelBounds.width.toInt, targetPixelBounds.height.toInt) (sourcePixelBounds, targetRasterExtent) } }.toMap geoTiffTile.crop(windows.keys.toSeq, bands.toArray).map { case (gb, tile) => val targetRasterExtent = windows(gb) - val sourceExtent = closestTiffOverview.rasterExtent.extentFor(gb, clamp = false) + val sourceExtent = closestTiffOverview.rasterExtent.extentFor(gb, clamp = false) Raster(tile, sourceExtent).resample(targetRasterExtent, method) } } diff --git a/effects/src/main/scala/geotrellis/raster/effects/syntax/package.scala b/effects/src/main/scala/geotrellis/raster/effects/syntax/package.scala deleted file mode 100644 index a7733be5..00000000 --- a/effects/src/main/scala/geotrellis/raster/effects/syntax/package.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2021 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.raster.effects - -import cats.effect.{Blocker, ContextShift} -import cats.{FlatMap, Monad, Parallel, Traverse} -import cats.syntax.flatMap._ - -package object syntax { - implicit class ContextShiftOps[F[_]: FlatMap: ContextShift, A](val fa: F[A]) { - def shift: F[A] = ContextShift[F].shift >> fa - } - - implicit class UnsafeLiftOps[T](t: => T) { - def lift[F[_]: UnsafeLift]: F[T] = UnsafeLift[F].apply(t) - def lifts[F[_]: FlatMap: UnsafeLift: ContextShift]: F[T] = ContextShift[F].shift >> lift[F] - } - - implicit class ParallelTraversableBlockerOps[T[_], A](val ta: T[A]) extends AnyVal { - def parTraverseBlocking[M[_]: Monad: ContextShift, B](f: A => M[B])(implicit T: Traverse[T], P: Parallel[M], B: Blocker): M[T[B]] = - Parallel.parTraverse(ta)(a => B.blockOn(f(a))) - } -} diff --git a/example/src/main/scala/geotrellis/server/example/ExampleConf.scala b/example/src/main/scala/geotrellis/server/example/ExampleConf.scala index b27fd4d5..583196d9 100644 --- a/example/src/main/scala/geotrellis/server/example/ExampleConf.scala +++ b/example/src/main/scala/geotrellis/server/example/ExampleConf.scala @@ -34,5 +34,5 @@ object ExampleConf { .fromConfig(ConfigFactory.load(configPath.getOrElse("application.conf"))) .loadF[F, ExampleConf] - def loadResourceF[F[_]: Sync](configPath: Option[String]): Resource[F, ExampleConf] = Resource.liftF(loadF[F](configPath)) + def loadResourceF[F[_]: Sync](configPath: Option[String]): Resource[F, ExampleConf] = Resource.eval(loadF[F](configPath)) } diff --git a/example/src/main/scala/geotrellis/server/example/ndvi/NdviServer.scala b/example/src/main/scala/geotrellis/server/example/ndvi/NdviServer.scala index 6c7ab39b..5817e9ce 100644 --- a/example/src/main/scala/geotrellis/server/example/ndvi/NdviServer.scala +++ b/example/src/main/scala/geotrellis/server/example/ndvi/NdviServer.scala @@ -20,10 +20,11 @@ import geotrellis.server.example._ import geotrellis.server.vlm.geotiff.GeoTiffNode import cats.effect._ -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger +import cats.syntax.option._ +import org.typelevel.log4cats.slf4j.Slf4jLogger import org.http4s._ import org.http4s.server._ -import org.http4s.server.blaze.BlazeServerBuilder +import org.http4s.blaze.server.BlazeServerBuilder import org.http4s.server.middleware.{CORS, CORSConfig} import org.http4s.syntax.kleisli._ import com.azavea.maml.eval._ @@ -34,19 +35,19 @@ object NdviServer extends IOApp { implicit val logger = Slf4jLogger.getLogger[IO] - private val corsConfig = CORSConfig( - anyOrigin = true, - anyMethod = false, - allowedMethods = Some(Set("GET")), - allowCredentials = true, - maxAge = 1.day.toSeconds - ) + private val corsConfig = + CORSConfig.default + .withAnyOrigin(true) + .withAnyMethod(false) + .withAllowedMethods(Set(Method.GET).some) + .withAllowCredentials(true) + .withMaxAge(1.day) private val commonMiddleware: HttpMiddleware[IO] = { (routes: HttpRoutes[IO]) => CORS(routes) } - val createServer: Resource[IO, Server[IO]] = { + val createServer: Resource[IO, Server] = { for { conf <- ExampleConf.loadResourceF[IO](None) _ <- Resource.eval { @@ -57,7 +58,7 @@ object NdviServer extends IOApp { mamlNdviRendering = new NdviService[IO, GeoTiffNode]( ConcurrentInterpreter.DEFAULT ) - server <- BlazeServerBuilder[IO](executionContext) + server <- BlazeServerBuilder[IO] .enableHttp2(true) .bindHttp(conf.http.port, conf.http.interface) .withHttpApp( @@ -67,7 +68,9 @@ object NdviServer extends IOApp { } yield server } - /** The 'main' method for a cats-effect IOApp */ + /** + * The 'main' method for a cats-effect IOApp + */ override def run(args: List[String]): IO[ExitCode] = createServer.use(_ => IO.never).as(ExitCode.Success) } diff --git a/example/src/main/scala/geotrellis/server/example/ndvi/NdviService.scala b/example/src/main/scala/geotrellis/server/example/ndvi/NdviService.scala index eb29e9ff..e7ae2308 100644 --- a/example/src/main/scala/geotrellis/server/example/ndvi/NdviService.scala +++ b/example/src/main/scala/geotrellis/server/example/ndvi/NdviService.scala @@ -29,7 +29,7 @@ import org.http4s.circe._ import io.circe._ import io.circe.parser._ import io.circe.syntax._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import cats.data._ import Validated._ import cats._ @@ -38,7 +38,7 @@ import cats.implicits._ import java.net.URLDecoder -class NdviService[F[_]: Sync: Logger: Parallel, T: Encoder: Decoder: TmsReification[F, *]]( +class NdviService[F[_]: Concurrent: Logger: Parallel, T: Encoder: Decoder: TmsReification[F, *]]( interpreter: Interpreter[F] ) extends Http4sDsl[F] { val logger = Logger[F] @@ -77,7 +77,7 @@ class NdviService[F[_]: Sync: Logger: Parallel, T: Encoder: Decoder: TmsReificat ) +& NirQueryParamMatcher(nir) => val paramMap = Map("red" -> red, "nir" -> nir) - eval(paramMap, z, x, y).attempt flatMap { + eval(paramMap, z, x, y).attempt.flatMap { case Right(Valid(mbtile)) => // Image results have multiple bands. We need to pick one Ok(mbtile.band(0).renderPng(ColorRamps.Viridis).bytes) diff --git a/example/src/main/scala/geotrellis/server/example/persistence/PersistenceServer.scala b/example/src/main/scala/geotrellis/server/example/persistence/PersistenceServer.scala index 22f821b1..44c4f662 100644 --- a/example/src/main/scala/geotrellis/server/example/persistence/PersistenceServer.scala +++ b/example/src/main/scala/geotrellis/server/example/persistence/PersistenceServer.scala @@ -22,12 +22,13 @@ import geotrellis.server.vlm.geotiff._ import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap import com.azavea.maml.ast.Expression import cats.effect._ -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger +import org.typelevel.log4cats.slf4j.Slf4jLogger import org.http4s._ import org.http4s.server._ -import org.http4s.server.blaze.BlazeServerBuilder +import org.http4s.blaze.server.BlazeServerBuilder import org.http4s.server.middleware.{CORS, CORSConfig} import org.http4s.syntax.kleisli._ +import cats.syntax.option._ import java.util.UUID import scala.concurrent.duration._ @@ -35,13 +36,13 @@ import scala.concurrent.duration._ object PersistenceServer extends IOApp { implicit val logger = Slf4jLogger.getLogger[IO] - private val corsConfig = CORSConfig( - anyOrigin = true, - anyMethod = false, - allowedMethods = Some(Set("GET")), - allowCredentials = true, - maxAge = 1.day.toSeconds - ) + private val corsConfig = + CORSConfig.default + .withAnyOrigin(true) + .withAnyMethod(false) + .withAllowedMethods(Set(Method.GET).some) + .withAllowCredentials(true) + .withMaxAge(1.day) private val commonMiddleware: HttpMiddleware[IO] = { (routes: HttpRoutes[IO]) => CORS(routes) @@ -66,7 +67,7 @@ object PersistenceServer extends IOApp { ]( mamlStore ) - server <- BlazeServerBuilder[IO](executionContext) + server <- BlazeServerBuilder[IO] .enableHttp2(true) .bindHttp(conf.http.port, conf.http.interface) .withHttpApp( @@ -76,9 +77,11 @@ object PersistenceServer extends IOApp { } yield server } - /** The 'main' method for a cats-effect IOApp */ + /** + * The 'main' method for a cats-effect IOApp + */ override def run(args: List[String]): IO[ExitCode] = - createServer use { _ => + createServer.use { _ => IO(ExitCode.Success) } } diff --git a/example/src/main/scala/geotrellis/server/example/persistence/PersistenceService.scala b/example/src/main/scala/geotrellis/server/example/persistence/PersistenceService.scala index 0642a708..4941ada5 100644 --- a/example/src/main/scala/geotrellis/server/example/persistence/PersistenceService.scala +++ b/example/src/main/scala/geotrellis/server/example/persistence/PersistenceService.scala @@ -24,7 +24,7 @@ import com.azavea.maml.ast.codec.tree._ import org.http4s._ import org.http4s.dsl.Http4sDsl import org.http4s.circe._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import io.circe._ import io.circe.parser._ import io.circe.syntax._ @@ -37,7 +37,7 @@ import cats.ApplicativeError import java.util.UUID import scala.util.Try -class PersistenceService[F[_]: Sync: Logger: ApplicativeError[*[_], Throwable], S: MamlStore[F, *], T: TmsReification[F, *]: Decoder]( +class PersistenceService[F[_]: Concurrent: Logger: ApplicativeError[*[_], Throwable], S: MamlStore[F, *], T: TmsReification[F, *]: Decoder]( val store: S ) extends Http4sDsl[F] { val logger = Logger[F] @@ -68,13 +68,13 @@ class PersistenceService[F[_]: Sync: Logger: ApplicativeError[*[_], Throwable], s"Attempting to store expression (${req.bodyText}) at key ($key)" ) res <- MamlStore[F, S].putMaml(store, key, expr) - } yield res).attempt flatMap { + } yield res).attempt.flatMap { case Right(created) => Created() case Left(InvalidMessageBodyFailure(_, _)) | Left(MalformedMessageBodyFailure(_, _)) => - req.bodyText.compile.toList flatMap { reqBody => + req.bodyText.compile.toList.flatMap { reqBody => BadRequest(s"""Unable to parse ${reqBody - .mkString("")} as a MAML expression""") + .mkString("")} as a MAML expression""") } case Left(err) => logger.debug(err.toString) @@ -83,14 +83,14 @@ class PersistenceService[F[_]: Sync: Logger: ApplicativeError[*[_], Throwable], case req @ GET -> Root / IdVar(key) => logger.info(s"Attempting to retrieve expression at key ($key)") - MamlStore[F, S].getMaml(store, key) flatMap { + MamlStore[F, S].getMaml(store, key).flatMap { case Some(expr) => Ok(expr.asJson) case None => NotFound() } case req @ GET -> Root / IdVar(key) / "parameters" => logger.info(s"Attempting to retrieve expression parameters at key ($key)") - MamlStore[F, S].getMaml(store, key) flatMap { + MamlStore[F, S].getMaml(store, key).flatMap { case Some(expr) => Ok(Vars.vars(expr).asJson) case None => NotFound() } diff --git a/ogc-example/src/main/resources/application.conf b/ogc-example/src/main/resources/application.conf index f69d9ac1..fd7028f8 100644 --- a/ogc-example/src/main/resources/application.conf +++ b/ogc-example/src/main/resources/application.conf @@ -1,3 +1,334 @@ +wms = { + parent-layer-meta = { + name = "Geotrellis WMS Parent Layer" + title = "WMS Parent Title" + description = "Top level metadata that is inherited by children layers" + supported-projections = [ + 4326, + 3410, + 3978 + ] + } + service-metadata = { + name = "WMS" + title = "GeoTrellis Service" + online-resource = {} + keyword-list = { + keyword = ["geotrellis", "catalog"] + } + contact-information = { + contact-person-primary = { + contact-person = "Eugene Cheipesh" + contact-organization = "Azavea" + } + contact-position = "Developer" + contact-address = { + address-type = "Office" + address = "990 Spring Garden St." + city = "Philadelphia" + state-or-province = "PA", + post-code = "19087", + country = "USA" + } + } + } + layer-definitions = [ + ${layers.us-ned} + ] +} + +wcs = { + service-metadata = { + identification = { + title = "WCS" + description = "Geotrellis WCS Service" + keywords = [] + profile = ["http://azavea.com/wcs-profile"] + fees = "" + access-constraints = [] + } + provider = { + name = "Azavea" + site = "https://www.azavea.com" + } + } + layer-definitions = [ + ${layers.us-ned} + ] + supported-projections = [ + 4326, + 3410, + 3978, + 4617, + 3979, + 3413, + 26916 + ] +} + +wmts = { + service-metadata = { + identification = { + title = "WMTS" + description = "Geotrellis WMTS Service" + keywords = [] + profile = ["http://azavea.com/wmts-profile"] + fees = "" + access-constraints = [] + } + provider = { + name = "Azavea" + site = "https://www.azavea.com" + } + } + layer-definitions = [ + ${layers.us-ned} + ] + tile-matrix-sets = [ + { + identifier = "GoogleMapsCompatible", + supported-crs = 4326, + title = "GoogleMapCompatible", + abstract = "Google Maps compatible tile matrix set", + well-known-scale-set = "urn:ogc:def:crs:OGC:2:84", + tile-matrix = [ + { + identifier = "0", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [1, 1, 256, 256] + }, + { + identifier = "1", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [2, 2, 256, 256] + }, + { + identifier = "2", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [4, 4, 256, 256] + }, + { + identifier = "3", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [8, 8, 256, 256] + }, + { + identifier = "4", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [16, 16, 256, 256] + }, + { + identifier = "5", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [32, 32, 256, 256] + }, + { + identifier = "6", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [64, 64, 256, 256] + }, + { + identifier = "7", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [128, 128, 256, 256] + }, + { + identifier = "8", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [256, 256, 256, 256] + }, + { + identifier = "9", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [512, 512, 256, 256] + }, + { + identifier = "10", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [1024, 1024, 256, 256] + }, + { + identifier = "11", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [2048, 2048, 256, 256] + }, + { + identifier = "12", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [4096, 4096, 256, 256] + }, + { + identifier = "13", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [8192, 8192, 256, 256] + }, + { + identifier = "14", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [16384, 16384, 256, 256] + }, + { + identifier = "15", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [32768, 32768, 256, 256] + }, + { + identifier = "16", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [65536, 65536, 256, 256] + }, + { + identifier = "17", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [131072, 131072, 256, 256] + }, + { + identifier = "18", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [262144, 262144, 256, 256] + }, + { + identifier = "19", + extent = [-180.0, -180.0, 180.0, 180.0], + tile-layout = [524288, 524288, 256, 256] + } + ] + } + ] +} + +layers = { + us-ned = { + type = "rastersourceconf" + name = "us-ned" + title = "My Tiles" + source = "gt+file:///Users/grigory.pomadchin/Downloads/gt/tiles-new-latlon?layer=tiles&zoom=19&band_count=3" + default-style = "elevation-ramp" + styles = [ + { + type = "colorrampconf" + name = "elevation-ramp" + title = "Elevation Ramp" + colors = ${color-ramps.elevation} + stops = 90 + }, + { + type = "colorrampconf" + name = "elevation-ramp-clamped" + title = "Elevation Ramp: 1000 - 3000m" + colors = ${color-ramps.red-to-blue} + stops = 90 + min-render = 1000 + max-render = 3000 + clamp-with-color = true + }, + + ] + } +} + +color-ramps = { + "red-to-blue": [ + 0x2A2E7FFF, 0x3D5AA9FF, 0x4698D3FF, 0x39C6F0FF, + 0x76C9B3FF, 0xA8D050FF, 0xF6EB14FF, 0xFCB017FF, + 0xF16022FF, 0xEE2C24FF, 0x7D1416FF + ] + "elevation": [ + 0x375A91FF, 0x2D82B9FF, 0x78B978FF, 0x91C882FF, + 0xB9DC91FF, 0xFFFFAAFF, 0xE6DC8CFF, 0xCDBE73FF, + 0xB49B55FF, 0x9B7D37FF, 0x6E3C00FF, 0xA59678FF, + 0xD7D7D7FF, 0xFFFFFFFF + ] +} + +color-maps = { + "nlcd": { + 11: 0x526095FF, + 12: 0xFFFFFFFF, + 21: 0xD28170FF, + 22: 0xEE0006FF, + 23: 0x990009FF, + 31: 0xBFB8B1FF, + 32: 0x969798FF, + 33: 0x382959FF, + 41: 0x579D57FF, + 42: 0x2A6B3DFF, + 43: 0xA6BF7BFF, + 51: 0xBAA65CFF, + 61: 0x45511FFF, + 71: 0xD0CFAAFF, + 81: 0xCCC82FFF, + 82: 0x9D5D1DFF, + 83: 0xCD9747FF, + 84: 0xA7AB9FFF, + 85: 0xE68A2AFF, + 91: 0xB6D8F5FF, + 92: 0xB6D8F5FF + } + "dem": { + -200: 0x375A91FF, + -100: 0x2D82B9FF, + 0: 0x78B978FF, + 20: 0x91C882FF, + 60: 0xB9DC91FF, + 250: 0xFFFFAAFF, + 650: 0xE6DC8CFF, + 800: 0xCDBE73FF, + 1200: 0xB49B55FF, + 1500: 0x9B7D37FF, + 1800: 0x6E3C00FF, + 2000: 0xA59678FF, + 2500: 0xD7D7D7FF, + 3000: 0xFFFFFFFF + } + "ndvi": { + -1.0: 0x1947B0FF, + -0.7: 0x3961B7FF, + -0.5: 0x5A7BBFFF, + -0.4: 0x7B95C6FF, + -0.3: 0x9CB0CEFF, + -0.2: 0xBDCAD5FF, + -0.1: 0xDEE4DDFF, + "0.0": 0xFFFFE5FF, + "0.1": 0xDAE4CAFF, + "0.2": 0xB6C9AFFF, + "0.3": 0x91AF94FF, + "0.4": 0x6D9479FF, + "0.5": 0x487A5EFF, + "0.7": 0x245F43FF, + "1.0": 0x004529FF + } +} + +interpolated-color-maps = { + "ndvi": { + "poles": { + "-1.0": 0x770000FF, + "0.0": 0xFF0000FF, + "0.5": 0xFFFF00FF, + "1.0": 0x006600FF + } + "clip-definition": "clip-none" + } + "almost-nlcd": { + "poles": { + "0.0": 0x0000FFFF, + "50.0": 0x00FF00FF + "100.0": 0xFF0000FF, + } + "clip-definition": "clip-both" + } + "income": { + "poles": { + "0.0": 0xFF0000FF, + "75000.0": 0x777700FF + "200000.0": 0x00FF00FF, + } + "clip-definition": "clip-both" + } +} + + wms = { parent-layer-meta = { name = "Geotrellis WMS Parent Layer" @@ -37,14 +368,7 @@ wms = { } } layer-definitions = [ - ${layers.nlcd-2011}, - ${layers.addition-house-income}, - ${layers.us-ned}, - ${layers.us-ned-slope}, - ${layers.us-ned-hillshade}, - ${layers.us-census-median-household-income}, - ${layers.markham}, - ${layers.lc8-rgb} + ${layers.issue-394} ] } @@ -64,12 +388,7 @@ wcs = { } } layer-definitions = [ - ${layers.us-ned}, - ${layers.us-ned-slope}, - ${layers.us-ned-hillshade}, - ${layers.us-census-median-household-income}, - ${layers.markham}, - ${layers.lc8-rgb} + ${layers.issue-394} ] supported-projections = [ 4326, @@ -98,12 +417,7 @@ wmts = { } } layer-definitions = [ - ${layers.us-ned}, - ${layers.us-ned-slope}, - ${layers.us-ned-hillshade}, - ${layers.us-census-median-household-income}, - ${layers.markham}, - ${layers.lc8-rgb} + ${layers.issue-394} ] tile-matrix-sets = [ { @@ -214,6 +528,22 @@ wmts = { } layers = { + issue-394 = { + type = "rastersourceconf" + name = "issue-394" + title = "ISSUE-394" + source = "gt+file:///Users/grigory.pomadchin/Downloads/gt/tiles-new-latlon?layer=tiles&zoom=19&band_count=3" + default-style = "elevation-ramp" + styles = [ + { + type = "colorrampconf" + name = "red-to-blue" + title = "Red To Blue" + colors = ${color-ramps.red-to-blue} + stops = 64 + } + ] + } nlcd-2011 = { type = "rastersourceconf" name = "NLCD 2011", diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/FocalParameters.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/FocalParameters.scala index 2b07e0eb..ea54656e 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/FocalParameters.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/FocalParameters.scala @@ -52,9 +52,9 @@ case class FocalParameters(azimuth: Option[Double], altitude: Option[Double], zF object FocalParameters { def fromParams(params: ParamMap): Validated[NonEmptyList[ParamError], Option[FocalParameters]] = { - val azimuth: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("azimuth") + val azimuth: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("azimuth") val altitude: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("altitude") - val zFactor: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("zfactor") + val zFactor: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("zfactor") val target: Validated[NonEmptyList[ParamError], Option[TargetCell]] = params .validatedOptionalParam("target") .andThen { f => diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/Main.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/Main.scala index 45b59fa5..43df5e02 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/Main.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/Main.scala @@ -25,18 +25,19 @@ import cats.implicits._ import com.monovore.decline._ import org.http4s._ import org.http4s.server._ -import org.http4s.server.blaze.BlazeServerBuilder +import org.http4s.blaze.server.BlazeServerBuilder import org.http4s.server.middleware.CORS import org.http4s.syntax.kleisli._ import org.backuity.ansi.AnsiFormatter.FormattedHelper import com.google.common.util.concurrent.ThreadFactoryBuilder -import io.chrisdavenport.log4cats.Logger -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger +import org.typelevel.log4cats.Logger +import org.typelevel.log4cats.slf4j.Slf4jLogger import scala.concurrent.duration._ import scala.concurrent.ExecutionContext import java.net.URL import java.util.concurrent.Executors +import cats.effect.unsafe.implicits.global object Main extends CommandApp( @@ -95,9 +96,6 @@ object Main .build() ) ) - implicit val cs: ContextShift[IO] = - IO.contextShift(executionContext) - implicit val timer: Timer[IO] = IO.timer(executionContext) val commonMiddleware: HttpMiddleware[IO] = { (routes: HttpRoutes[IO]) => CORS(routes) @@ -109,7 +107,7 @@ object Main downLog: String ): IO[Unit] = opt.fold(logger.info(downLog))(_ => logger.info(upLog)) - def createServer: Resource[IO, Server[IO]] = + def createServer: Resource[IO, Server] = for { conf <- Conf.loadResourceF[IO](configPath) simpleSources = conf.layers.values.collect { case rsc: RasterSourceConf => rsc.toLayer }.toList @@ -164,7 +162,8 @@ object Main wmtsModel, new URL(publicUrl) ) - server <- BlazeServerBuilder[IO](executionContext) + server <- BlazeServerBuilder[IO] + .withExecutionContext(executionContext) .withIdleTimeout(Duration.Inf) .withResponseHeaderTimeout(Duration.Inf) .enableHttp2(true) diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/OgcService.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/OgcService.scala index 40f516e5..34413f47 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/OgcService.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/OgcService.scala @@ -24,12 +24,12 @@ import org.http4s._ import org.http4s.dsl.Http4sDsl import cats.Parallel import cats.effect._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import org.log4s.getLogger import java.net.URL -class OgcService[F[_]: Concurrent: Parallel: Logger]( +class OgcService[F[_]: Async: Parallel: Logger]( wmsModel: Option[WmsModel[F]], wcsModel: Option[WcsModel[F]], wmtsModel: Option[WmtsModel[F]], @@ -37,13 +37,13 @@ class OgcService[F[_]: Concurrent: Parallel: Logger]( ) extends Http4sDsl[F] { val logger = getLogger - val wcsView = wcsModel.map(new WcsView(_, serviceUrl)) - val wmsView = wmsModel.map(new WmsView(_, serviceUrl)) + val wcsView = wcsModel.map(new WcsView(_, serviceUrl)) + val wmsView = wmsModel.map(new WmsView(_, serviceUrl)) val wmtsView = wmtsModel.map(new WmtsView(_, serviceUrl)) // Predicates for choosing a service - def isWcsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wcs" - def isWmsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wms" + def isWcsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wcs" + def isWmsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wms" def isWmtsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wmts" def routes: HttpRoutes[F] = diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/RGBParameters.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/RGBParameters.scala index d04ad829..56de4f82 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/RGBParameters.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/RGBParameters.scala @@ -44,19 +44,13 @@ case class RGBParameters( case r :: g :: b :: Nil => val id: Expression => Expression = identity val rmap = - clampRed.map(_.bind).getOrElse(id) andThen - normalizeRed.map(_.bind).getOrElse(id) andThen - rescaleRed.map(_.bind).getOrElse(id) + clampRed.map(_.bind).getOrElse(id).andThen(normalizeRed.map(_.bind).getOrElse(id)).andThen(rescaleRed.map(_.bind).getOrElse(id)) val gmap = - clampGreen.map(_.bind).getOrElse(id) andThen - normalizeGreen.map(_.bind).getOrElse(id) andThen - rescaleGreen.map(_.bind).getOrElse(id) + clampGreen.map(_.bind).getOrElse(id).andThen(normalizeGreen.map(_.bind).getOrElse(id)).andThen(rescaleGreen.map(_.bind).getOrElse(id)) val bmap = - clampBlue.map(_.bind).getOrElse(id) andThen - normalizeBlue.map(_.bind).getOrElse(id) andThen - rescaleBlue.map(_.bind).getOrElse(id) + clampBlue.map(_.bind).getOrElse(id).andThen(normalizeBlue.map(_.bind).getOrElse(id)).andThen(rescaleBlue.map(_.bind).getOrElse(id)) e.copy(children = rmap(r) :: gmap(g) :: bmap(b) :: Nil) case _ => e diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala index 68b7749f..d7505e3c 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala @@ -30,27 +30,33 @@ import geotrellis.proj4.CRS sealed trait OgcServiceConf { def layerDefinitions: List[OgcSourceConf] def layerSources(rasterOgcSources: List[RasterOgcSource]): Repository[OgcSource] = { - val rasterLayers: List[RasterOgcSource] = layerDefinitions.collect { case rsc: RasterSourceConf => rsc.toLayer } + val rasterLayers: List[RasterOgcSource] = layerDefinitions.collect { case rsc: RasterSourceConf => rsc.toLayer } val mapAlgebraLayers: List[MapAlgebraSource] = layerDefinitions.collect { case masc: MapAlgebraSourceConf => masc.model(rasterOgcSources) } ogc.OgcSourceRepository(rasterLayers ++ mapAlgebraLayers) } } -/** WMS Service configuration */ +/** + * WMS Service configuration + */ case class WmsConf( parentLayerMeta: WmsParentLayerMeta, serviceMetadata: opengis.wms.Service, layerDefinitions: List[OgcSourceConf] ) extends OgcServiceConf -/** WMTS Service configuration */ +/** + * WMTS Service configuration + */ case class WmtsConf( serviceMetadata: ows.ServiceMetadata, layerDefinitions: List[OgcSourceConf], tileMatrixSets: List[GeotrellisTileMatrixSet] ) extends OgcServiceConf -/** WCS Service configuration */ +/** + * WCS Service configuration + */ case class WcsConf( serviceMetadata: ows.ServiceMetadata, layerDefinitions: List[OgcSourceConf], diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala index cc1dea1e..ddc03a9b 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala @@ -20,14 +20,18 @@ import geotrellis.server.ogc.style._ import geotrellis.raster.render.{ColorMap, ColorRamp} -/** The trait implemented by different style configuration options */ +/** + * The trait implemented by different style configuration options + */ sealed trait StyleConf { def name: String def title: String def toStyle: OgcStyle } -/** Styling in which a color scheme is known but not the values that these colors should map to */ +/** + * Styling in which a color scheme is known but not the values that these colors should map to + */ final case class ColorRampConf( name: String, title: String, @@ -42,7 +46,9 @@ final case class ColorRampConf( ColorRampStyle(name, title, colors, stops, minRender, maxRender, clampWithColor, legends) } -/** Styling in which both a color scheme and the data-to-color mapping is known */ +/** + * Styling in which both a color scheme and the data-to-color mapping is known + */ final case class ColorMapConf( name: String, title: String, diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/conf/package.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/conf/package.scala index 9eb40b43..3d43f56a 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/conf/package.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/conf/package.scala @@ -38,7 +38,9 @@ import pureconfig.generic.auto._ import scala.util.{Success, Try} import scala.collection.JavaConverters._ -/** A grab bag of [[ConfigReader]] instances necessary to read the configuration */ +/** + * A grab bag of [[ConfigReader]] instances necessary to read the configuration + */ package object conf { /** @@ -53,7 +55,7 @@ package object conf { implicit val circeJsonReader: ConfigReader[Json] = ConfigReader[ConfigValue].emap { cv => val renderOptions = ConfigRenderOptions.concise().setJson(true) - val jsonString = cv.render(renderOptions) + val jsonString = cv.render(renderOptions) parse(jsonString) match { case Left(parsingFailure) => Left(CannotConvert(jsonString, "json", parsingFailure.getMessage)) case Right(json) => Right(json) @@ -89,7 +91,7 @@ package object conf { case ConfigValueType.OBJECT => val confmap = v.asInstanceOf[ConfigObject].asScala confmap.map { case (ck, cv) => - val key = k + "." + ck + val key = k + "." + ck val value = cv.unwrapped.asInstanceOf[String] key -> value } @@ -100,7 +102,7 @@ package object conf { } } .map { case (k, v) => - val key = k.toDouble + val key = k.toDouble val value = java.lang.Long.decode(v).toInt key -> value } @@ -175,7 +177,7 @@ package object conf { 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 parseAuto(str: String): OverviewStrategy = parse("auto", str) def parseLevel(str: String): OverviewStrategy = parse("level", str) ConfigReader[String].map { diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/wcs/WcsView.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/wcs/WcsView.scala index e55b6009..83f28afb 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/wcs/WcsView.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/wcs/WcsView.scala @@ -20,20 +20,19 @@ import geotrellis.server.ogc.{OutputFormat, ToMediaType} import geotrellis.server.ogc.params.ParamError import geotrellis.server.ogc.ows.OwsDataRecord import geotrellis.server.utils._ - import org.backuity.ansi.AnsiFormatter.FormattedHelper import org.http4s.scalaxml._ import org.http4s._ import org.http4s.dsl.Http4sDsl import cats.effect._ -import cats.Parallel +import cats.{ApplicativeThrow, Parallel} import cats.data.Validated import cats.syntax.apply._ import cats.syntax.flatMap._ import cats.syntax.functor._ import cats.syntax.option._ import cats.syntax.applicativeError._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import opengis.ows.{AllowedValues, AnyValue, DomainType, ValueType} import opengis._ import org.http4s.headers.`Content-Type` @@ -41,7 +40,7 @@ import scalaxb._ import java.net._ -class WcsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( +class WcsView[F[_]: Async: Parallel: ApplicativeThrow: Logger]( wcsModel: WcsModel[F], serviceUrl: URL ) extends Http4sDsl[F] { @@ -158,12 +157,12 @@ class WcsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( case Validated.Valid(wcsParams) => wcsParams match { case _: GetCapabilitiesWcsParams => - logger.debug(ansi"%bold{GetCapabilities: $serviceUrl}") *> + (logger.debug(ansi"%bold{GetCapabilities: $serviceUrl}") *> new CapabilitiesView[F]( wcsModel, serviceUrl, extendedParameters ::: extendedRGBParameters - ).toXML flatMap { Ok(_) } + ).toXML).flatMap { Ok(_) } case p: DescribeCoverageWcsParams => logger.debug(ansi"%bold{DescribeCoverage: ${req.uri}}") *> @@ -171,10 +170,10 @@ class WcsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( case p: GetCoverageWcsParams => for { - _ <- logger.debug(ansi"%bold{GetCoverage: ${req.uri}}") + _ <- logger.debug(ansi"%bold{GetCoverage: ${req.uri}}") getCoverage <- getCoverage.build(p).attempt - result <- handleError(getCoverage, p.format) - _ <- logger.debug(s"getcoverage result: $result") + result <- handleError(getCoverage, p.format) + _ <- logger.debug(s"getcoverage result: $result") } yield result } } diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/wms/WmsView.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/wms/WmsView.scala index 7cf03102..4daacd19 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/wms/WmsView.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/wms/WmsView.scala @@ -20,7 +20,6 @@ import geotrellis.server.ogc._ import geotrellis.server.ogc.utils._ import geotrellis.server.ogc.params.ParamError import geotrellis.server.ogc.wms.WmsParams.{GetCapabilitiesParams, GetFeatureInfoParams, GetMapParams} - import geotrellis.raster.{io => _, _} import geotrellis.vector.{io => _} import com.azavea.maml.error._ @@ -28,12 +27,12 @@ import org.http4s.scalaxml._ import org.http4s._ import org.http4s.dsl.Http4sDsl import cats.effect._ -import cats.Parallel +import cats.{ApplicativeThrow, Parallel} import cats.syntax.flatMap._ import cats.syntax.functor._ import cats.syntax.option._ import cats.data.Validated._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import com.github.blemale.scaffeine.{Cache, Scaffeine} import org.backuity.ansi.AnsiFormatter.FormattedHelper import opengis._ @@ -44,7 +43,7 @@ import java.net.URL import scala.concurrent.duration._ import scala.xml.Elem -class WmsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( +class WmsView[F[_]: Async: Parallel: ApplicativeThrow: Logger]( wmsModel: WmsModel[F], serviceUrl: URL ) extends Http4sDsl[F] { diff --git a/ogc-example/src/main/scala/geotrellis/server/ogc/wmts/WmtsView.scala b/ogc-example/src/main/scala/geotrellis/server/ogc/wmts/WmtsView.scala index f2726d2e..29c6aa66 100644 --- a/ogc-example/src/main/scala/geotrellis/server/ogc/wmts/WmtsView.scala +++ b/ogc-example/src/main/scala/geotrellis/server/ogc/wmts/WmtsView.scala @@ -21,7 +21,6 @@ import geotrellis.server.ogc._ import geotrellis.server.ogc.params.ParamError import geotrellis.server.ogc.wmts.WmtsParams.{GetCapabilities, GetTile} import geotrellis.server.utils._ - import geotrellis.layer.SpatialKey import geotrellis.raster.Raster import com.azavea.maml.eval._ @@ -31,7 +30,7 @@ import org.http4s.dsl.Http4sDsl import org.http4s.circe._ import _root_.io.circe.syntax._ import cats.effect._ -import cats.Parallel +import cats.{ApplicativeThrow, Parallel} import cats.data.Validated.{Invalid, Valid} import cats.syntax.apply._ import cats.syntax.applicative._ @@ -39,16 +38,15 @@ import cats.syntax.functor._ import cats.syntax.flatMap._ import cats.syntax.applicativeError._ import cats.syntax.parallel._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import com.github.blemale.scaffeine.{Cache, Scaffeine} import org.backuity.ansi.AnsiFormatter.FormattedHelper import org.http4s.headers.`Content-Type` import scala.concurrent.duration._ - import java.net._ -class WmtsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( +class WmtsView[F[_]: Async: Parallel: ApplicativeThrow: Logger]( wmtsModel: WmtsModel[F], serviceUrl: URL ) extends Http4sDsl[F] { @@ -68,13 +66,13 @@ class WmtsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( logger.warn(msg) *> BadRequest(msg) case Valid(_: GetCapabilities) => - logger.debug(ansi"%bold{GetCapabilities: ${req.uri}}") *> - new CapabilitiesView(wmtsModel, serviceUrl).toXML flatMap (Ok(_)) + (logger.debug(ansi"%bold{GetCapabilities: ${req.uri}}") *> + new CapabilitiesView(wmtsModel, serviceUrl).toXML).flatMap(Ok(_)) case Valid(wmtsReq: GetTile) => logger.debug(ansi"%bold{GetTile: ${req.uri}}") - val tileCol = wmtsReq.tileCol - val tileRow = wmtsReq.tileRow + val tileCol = wmtsReq.tileCol + val tileRow = wmtsReq.tileRow val layerName = wmtsReq.layer val res = @@ -96,23 +94,26 @@ class WmtsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( LayerHistogram(mas.algebra.pure[F], mas.parameters.pure[F], ConcurrentInterpreter.DEFAULT[F], 512) } - (evalWmts(0, tileCol, tileRow), evalHisto).parMapN { - case (Valid(mbtile), Valid(hists)) => Valid((mbtile, hists)) - case (Invalid(errs), _) => Invalid(errs) - case (_, Invalid(errs)) => Invalid(errs) - }.attempt flatMap { - case Right(Valid((mbtile, hists))) => // success - val extent = layer.layout.mapTransform(SpatialKey(tileCol, tileRow)) - val rendered = Raster(mbtile, extent).render(layer.crs, layer.style, wmtsReq.format, hists) - tileCache.put(wmtsReq, rendered) - Ok(rendered).map(_.putHeaders(`Content-Type`(ToMediaType(wmtsReq.format)))) - case Right(Invalid(errs)) => // maml-specific errors - logger.debug(errs.toList.toString) - BadRequest(errs.asJson) - case Left(err) => // exceptions - logger.error(err.stackTraceString) - InternalServerError(err.stackTraceString) - } + (evalWmts(0, tileCol, tileRow), evalHisto) + .parMapN { + case (Valid(mbtile), Valid(hists)) => Valid((mbtile, hists)) + case (Invalid(errs), _) => Invalid(errs) + case (_, Invalid(errs)) => Invalid(errs) + } + .attempt + .flatMap { + case Right(Valid((mbtile, hists))) => // success + val extent = layer.layout.mapTransform(SpatialKey(tileCol, tileRow)) + val rendered = Raster(mbtile, extent).render(layer.crs, layer.style, wmtsReq.format, hists) + tileCache.put(wmtsReq, rendered) + Ok(rendered).map(_.putHeaders(`Content-Type`(ToMediaType(wmtsReq.format)))) + case Right(Invalid(errs)) => // maml-specific errors + logger.debug(errs.toList.toString) + BadRequest(errs.asJson) + case Left(err) => // exceptions + logger.error(err.stackTraceString) + InternalServerError(err.stackTraceString) + } }.headOption.getOrElse(BadRequest(s"Layer ($layerName) not found")) } diff --git a/ogc-example/src/test/scala/geotrellis/server/HillshadeSpec.scala b/ogc-example/src/test/scala/geotrellis/server/HillshadeSpec.scala index 216a6344..c490436d 100644 --- a/ogc-example/src/test/scala/geotrellis/server/HillshadeSpec.scala +++ b/ogc-example/src/test/scala/geotrellis/server/HillshadeSpec.scala @@ -46,7 +46,7 @@ class HillshadeSpec extends AnyFunSpec with Matchers { describe("HillshadeSpec") { ignore("RasterSource reproject hillshade") { val uri = "gt+s3://azavea-datahub/catalog?layer=us-ned-tms-epsg3857&zoom=14&band_count=1" - val rs = new GeoTrellisRasterSource(uri) + val rs = new GeoTrellisRasterSource(uri) val raster = rs.reprojectToRegion( LatLng, @@ -65,7 +65,7 @@ class HillshadeSpec extends AnyFunSpec with Matchers { val hillshadeProjectedRaster = ProjectedRaster(raster, LatLng) val interpreter = Interpreter.DEFAULT - val res = interpreter(FocalHillshade(List(RasterLit(hillshadeProjectedRaster)), 315, 45)).as[MultibandTile] + val res = interpreter(FocalHillshade(List(RasterLit(hillshadeProjectedRaster)), 315, 45)).as[MultibandTile] res match { case Valid(t) => GeoTiff(Raster(t, raster.extent), LatLng).write("/tmp/rs-reproject-hillshade.tiff") diff --git a/ogc-example/src/test/scala/geotrellis/server/ogc/conf/ColorMapConfigurationSpec.scala b/ogc-example/src/test/scala/geotrellis/server/ogc/conf/ColorMapConfigurationSpec.scala index 55b6162f..89b7f233 100644 --- a/ogc-example/src/test/scala/geotrellis/server/ogc/conf/ColorMapConfigurationSpec.scala +++ b/ogc-example/src/test/scala/geotrellis/server/ogc/conf/ColorMapConfigurationSpec.scala @@ -33,7 +33,7 @@ class ColorMapConfigurationSpec extends AnyFunSpec with Matchers { val unquoted = """{-1.0: 0x1947B0FF,-0.7: 0x3961B7FF,-0.5: 0x5A7BBFFF,-0.4: 0x7B95C6FF,-0.3: 0x9CB0CEFF,-0.2: 0xBDCAD5FF,-0.1: 0xDEE4DDFF, 0.0: 0xFFFFE5FF,0.1: 0xDAE4CAFF,0.2: 0xB6C9AFFF,0.3: 0x91AF94FF,0.4: 0x6D9479FF,0.5: 0x487A5EFF,0.7: 0x245F43FF,1.0: 0x004529FF}""" - val quotedSource = ConfigSource.string(quoted) + val quotedSource = ConfigSource.string(quoted) val unquotedSource = ConfigSource.string(unquoted) quotedSource.load[ColorMap].right.get.colors shouldBe (unquotedSource.load[ColorMap].right.get.colors) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/FeatureCollection.scala b/ogc/src/main/scala/geotrellis/server/ogc/FeatureCollection.scala index 5e2193ff..d4558a94 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/FeatureCollection.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/FeatureCollection.scala @@ -21,21 +21,23 @@ import geotrellis.vector.{Feature, Geometry} import io.circe.{Encoder, Json} import io.circe.syntax._ -/** TODO: consider moving it into GeoTrellis */ +/** + * TODO: consider moving it into GeoTrellis + */ case class FeatureCollection[G <: Geometry, D](list: List[Feature[G, D]]) object FeatureCollection { private def writeFeatureCollectionJson[G <: Geometry, D: Encoder](obj: Feature[G, D]): Json = Json.obj( - "type" -> "Feature".asJson, - "geometry" -> GeometryFormats.geometryEncoder(obj.geom), + "type" -> "Feature".asJson, + "geometry" -> GeometryFormats.geometryEncoder(obj.geom), "properties" -> obj.data.asJson ) implicit def featureCollectionEncoder[G <: Geometry, D: Encoder]: Encoder[FeatureCollection[G, D]] = Encoder.encodeJson.contramap { fc => Json.obj( - "type" -> "FeatureCollection".asJson, + "type" -> "FeatureCollection".asJson, "features" -> fc.list.map(writeFeatureCollectionJson[G, D]).asJson ) } diff --git a/ogc/src/main/scala/geotrellis/server/ogc/InfoFormat.scala b/ogc/src/main/scala/geotrellis/server/ogc/InfoFormat.scala index 566e7f48..1a698892 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/InfoFormat.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/InfoFormat.scala @@ -24,7 +24,7 @@ sealed trait InfoFormat { } object InfoFormat { - case object XML extends InfoFormat { val name: String = "text/xml" } + case object XML extends InfoFormat { val name: String = "text/xml" } case object Json extends InfoFormat { val name: String = "application/json" } def fromStringUnsafe(str: String): InfoFormat = diff --git a/ogc/src/main/scala/geotrellis/server/ogc/OgcLayer.scala b/ogc/src/main/scala/geotrellis/server/ogc/OgcLayer.scala index ff5d588a..d17573e4 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/OgcLayer.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/OgcLayer.scala @@ -30,7 +30,7 @@ import cats.effect._ import cats.data.{NonEmptyList => NEL} import cats.syntax.apply._ import cats.syntax.functor._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger /** * OgcLayer instances are sufficient to produce visual rasters as the end product of 'get map' requests. They are produced from a combination of a WMS @@ -74,7 +74,7 @@ case class MapAlgebraOgcLayer( object SimpleOgcLayer { implicit def simpleOgcReification[F[_]: Sync: Logger]: ExtentReification[F, SimpleOgcLayer] = { self => (extent: Extent, cellSize: Option[CellSize]) => - Logger[F].trace( + (Logger[F].trace( s"attempting to retrieve layer $self at extent $extent with $cellSize" ) *> Logger[F].trace(s"Requested extent geojson: ${extent.toGeoJson}") *> { @@ -98,9 +98,9 @@ object SimpleOgcLayer { } <* Logger[F].trace( s"Successfully retrieved layer $self at extent $extent with f $cellSize" - ) map { raster => - ProjectedRaster(raster, self.crs) - } + )).map { raster => + ProjectedRaster(raster, self.crs) + } } implicit def simpleOgcHasRasterExtents[F[_]: Sync]: HasRasterExtents[F, SimpleOgcLayer] = { self => diff --git a/ogc/src/main/scala/geotrellis/server/ogc/OgcSource.scala b/ogc/src/main/scala/geotrellis/server/ogc/OgcSource.scala index cb34f591..9df808b7 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/OgcSource.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/OgcSource.scala @@ -59,10 +59,10 @@ trait OgcSource { def timeDefault: OgcTimeDefault def isTemporal: Boolean = timeMetadataKey.nonEmpty && time.nonEmpty - def nativeProjectedExtent: ProjectedExtent = ProjectedExtent(nativeExtent, nativeCrs.head) + def nativeProjectedExtent: ProjectedExtent = ProjectedExtent(nativeExtent, nativeCrs.head) def nativeProjectedGeometry: ProjectedGeometry = ProjectedGeometry(nativeProjectedExtent) - def projectedExtent: ProjectedExtent = nativeProjectedExtent - def projectedGeometry: ProjectedGeometry = nativeProjectedGeometry + def projectedExtent: ProjectedExtent = nativeProjectedExtent + def projectedGeometry: ProjectedGeometry = nativeProjectedGeometry } trait RasterOgcSource extends OgcSource { @@ -73,10 +73,10 @@ trait RasterOgcSource extends OgcSource { reprojected.extent } - lazy val nativeRE: GridExtent[Long] = source.gridExtent - lazy val nativeCrs: Set[CRS] = Set(source.crs) - lazy val nativeExtent: Extent = source.extent - lazy val metadata: RasterMetadata = source.metadata + lazy val nativeRE: GridExtent[Long] = source.gridExtent + lazy val nativeCrs: Set[CRS] = Set(source.crs) + lazy val nativeExtent: Extent = source.extent + lazy val metadata: RasterMetadata = source.metadata lazy val attributes: Map[String, String] = metadata.attributes def toLayer(crs: CRS, style: Option[OgcStyle], temporalSequence: List[OgcTime]): SimpleOgcLayer @@ -97,7 +97,7 @@ case class SimpleSource( timeFormat: OgcTimeFormat ) extends RasterOgcSource { val timeDefault: OgcTimeDefault = OgcTimeDefault.Oldest - lazy val time: OgcTime = source.time(timeMetadataKey).format(timeFormat).sorted + lazy val time: OgcTime = source.time(timeMetadataKey).format(timeFormat).sorted def toLayer(crs: CRS, style: Option[OgcStyle], temporalSequence: List[OgcTime]): SimpleOgcLayer = SimpleOgcLayer(name, title, crs, source, style, resampleMethod, overviewStrategy) @@ -206,8 +206,10 @@ case class MapAlgebraSourceMetadata( sources: Map[String, RasterMetadata] ) extends RasterMetadata { - /** MapAlgebra metadata usually doesn't contain a metadata that is common for all RasterSources */ - def attributes: Map[String, String] = Map.empty + /** + * MapAlgebra metadata usually doesn't contain a metadata that is common for all RasterSources + */ + def attributes: Map[String, String] = Map.empty def attributesForBand(band: Int): Map[String, String] = Map.empty } @@ -230,8 +232,8 @@ case class MapAlgebraSource( // each of the underlying ogcSources uses it's own timeMetadataKey val timeMetadataKey: Option[String] = None - lazy val sources: Map[String, RasterSource] = ogcSources.mapValues(_.source) - lazy val sourcesList: List[RasterSource] = sources.values.toList + lazy val sources: Map[String, RasterSource] = ogcSources.mapValues(_.source) + lazy val sourcesList: List[RasterSource] = sources.values.toList lazy val ogcSourcesList: List[RasterOgcSource] = ogcSources.values.toList def extentIn(crs: CRS): Extent = @@ -241,9 +243,9 @@ case class MapAlgebraSource( def bboxIn(crs: CRS): BoundingBox = { val reprojectedSources: NEL[RasterSource] = NEL.fromListUnsafe(sourcesList.map(_.reproject(crs))) - val extents = reprojectedSources.map(_.extent) - val extentIntersection = SampleUtils.intersectExtents(extents) - val cellSize = SampleUtils.chooseLargestCellSize(reprojectedSources.map(_.cellSize)) + val extents = reprojectedSources.map(_.extent) + val extentIntersection = SampleUtils.intersectExtents(extents) + val cellSize = SampleUtils.chooseLargestCellSize(reprojectedSources.map(_.cellSize)) extentIntersection match { case Some(extent) => CapabilitiesView.boundingBox(crs, extent, cellSize) @@ -270,16 +272,16 @@ case class MapAlgebraSource( lazy val nativeRE: GridExtent[Long] = { val reprojectedSources: NEL[RasterSource] = NEL.fromListUnsafe(sourcesList.map(_.reproject(nativeCrs.head))) - val cellSize = SampleUtils.chooseSmallestCellSize(reprojectedSources.map(_.cellSize)) + val cellSize = SampleUtils.chooseSmallestCellSize(reprojectedSources.map(_.cellSize)) new GridExtent[Long](nativeExtent, cellSize) } val time: OgcTime = ogcSources.values.toList.map(_.time).foldLeft[OgcTime](OgcTimeEmpty)(_ |+| _).format(timeFormat).sorted - val attributes: Map[String, String] = Map.empty - lazy val nativeCrs: Set[CRS] = ogcSourcesList.flatMap(_.nativeCrs).toSet - lazy val minBandCount: Int = sourcesList.map(_.bandCount).min - lazy val cellTypes: Set[CellType] = sourcesList.map(_.cellType).toSet + val attributes: Map[String, String] = Map.empty + lazy val nativeCrs: Set[CRS] = ogcSourcesList.flatMap(_.nativeCrs).toSet + lazy val minBandCount: Int = sourcesList.map(_.bandCount).min + lazy val cellTypes: Set[CellType] = sourcesList.map(_.cellType).toSet lazy val resolutions: List[CellSize] = sourcesList.flatMap(_.resolutions).distinct } diff --git a/ogc/src/main/scala/geotrellis/server/ogc/OgcSourceRepository.scala b/ogc/src/main/scala/geotrellis/server/ogc/OgcSourceRepository.scala index 95a4618c..b8109b6e 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/OgcSourceRepository.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/OgcSourceRepository.scala @@ -47,8 +47,8 @@ object OgcSourceRepository { _.time match { case OgcTimePositions(list) => val sorted = list.sorted - val start = sorted.head - val end = sorted.last + val start = sorted.head + val end = sorted.last t1 <= start && start <= t2 || t1 <= end && end <= t2 case OgcTimeInterval(start, end, _) => t1 <= start && start <= t2 || t1 <= end && end <= t2 @@ -62,15 +62,19 @@ object OgcSourceRepository { case Contains(e) => _.filter(_.nativeProjectedGeometry.covers(e)) case And(e1, e2) => list => - val left = e1(list); left intersect e2(left) + val left = e1(list); left.intersect(e2(left)) case Or(e1, e2) => list => e1(list) ++ e2(list) } - /** An alias for [[scheme.cata]] since it can confuse people */ + /** + * An alias for [[scheme.cata]] since it can confuse people + */ def eval(query: Query)(list: List[OgcSource]): List[OgcSource] = scheme.cata(algebra).apply(query)(list) - /** An alias for [[scheme.hylo]] since it can confuse people */ + /** + * An alias for [[scheme.hylo]] since it can confuse people + */ def eval(json: Json)(list: List[OgcSource]): List[OgcSource] = scheme.hylo(algebra, QueryF.coalgebraJson).apply(json)(list) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/OgcTime.scala b/ogc/src/main/scala/geotrellis/server/ogc/OgcTime.scala index 8f8a2a22..d92ed0a2 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/OgcTime.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/OgcTime.scala @@ -27,7 +27,7 @@ import java.time.{Duration, ZonedDateTime} import scala.util.Try sealed trait OgcTime { - def isEmpty: Boolean = false + def isEmpty: Boolean = false def nonEmpty: Boolean = !isEmpty } @@ -56,7 +56,9 @@ object OgcTime { implicit class OgcTimeOps(val self: OgcTime) extends AnyVal { - /** Reformat OgcTime if possible. */ + /** + * Reformat OgcTime if possible. + */ def format(format: OgcTimeFormat): OgcTime = format match { case OgcTimeFormat.Interval => @@ -94,17 +96,21 @@ object OgcTime { } case object OgcTimeEmpty extends OgcTime { - override val isEmpty: Boolean = true + override val isEmpty: Boolean = true override val nonEmpty: Boolean = !isEmpty implicit val ogcTimeEmptySemigroup: Semigroup[OgcTimeEmpty.type] = { (l, _) => l } } -/** Represents the TimePosition used in TimeSequence requests */ +/** + * Represents the TimePosition used in TimeSequence requests + */ final case class OgcTimePositions(list: NonEmptyList[ZonedDateTime]) extends OgcTime { def sorted: NonEmptyList[ZonedDateTime] = list.sorted - /** Compute (if possible) the period of the [[ZonedDateTime]] lists. */ + /** + * Compute (if possible) the period of the [[ZonedDateTime]] lists. + */ def computeIntervalPeriod: Option[PeriodDuration] = { val periods = sorted.toList @@ -121,7 +127,7 @@ final case class OgcTimePositions(list: NonEmptyList[ZonedDateTime]) extends Ogc def toOgcTimeInterval: OgcTimeInterval = OgcTimeInterval(sorted.head, sorted.last, computeIntervalPeriod) - def toList: List[String] = list.toList.map(_.toInstant.toString) + def toList: List[String] = list.toList.map(_.toInstant.toString) override def toString: String = toList.mkString(", ") } @@ -131,7 +137,7 @@ object OgcTimePositions { } def apply(timePeriod: ZonedDateTime): OgcTimePositions = OgcTimePositions(NonEmptyList(timePeriod, Nil)) - def apply(timeString: String): OgcTimePositions = apply(ZonedDateTime.parse(timeString)) + def apply(timeString: String): OgcTimePositions = apply(ZonedDateTime.parse(timeString)) def apply(times: List[ZonedDateTime]): OgcTime = times match { case head :: tail => OgcTimePositions(NonEmptyList(head, tail)) @@ -178,7 +184,9 @@ final case class OgcTimeInterval(start: ZonedDateTime, end: ZonedDateTime, inter object OgcTimeInterval { - /** Safe [[PeriodDuration]] parser. */ + /** + * Safe [[PeriodDuration]] parser. + */ private def periodDurationParse(string: String): Option[PeriodDuration] = Try(PeriodDuration.parse(string)).toOption /** diff --git a/ogc/src/main/scala/geotrellis/server/ogc/OgcTimeDefault.scala b/ogc/src/main/scala/geotrellis/server/ogc/OgcTimeDefault.scala index 8b7c786a..b64f969c 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/OgcTimeDefault.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/OgcTimeDefault.scala @@ -28,8 +28,8 @@ sealed trait OgcTimeDefault { } object OgcTimeDefault { - case object Oldest extends OgcTimeDefault - case object Newest extends OgcTimeDefault + case object Oldest extends OgcTimeDefault + case object Newest extends OgcTimeDefault case class Time(time: ZonedDateTime) extends OgcTimeDefault def fromString(str: String): OgcTimeDefault = str match { diff --git a/ogc/src/main/scala/geotrellis/server/ogc/OgcTimeFormat.scala b/ogc/src/main/scala/geotrellis/server/ogc/OgcTimeFormat.scala index 752f4f99..54f3b007 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/OgcTimeFormat.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/OgcTimeFormat.scala @@ -20,20 +20,28 @@ import io.circe.Codec import io.circe.generic.extras.Configuration import io.circe.generic.extras.semiauto.deriveEnumerationCodec -/** ADT to change [[OgcTime]] internal representation */ +/** + * ADT to change [[OgcTime]] internal representation + */ sealed trait OgcTimeFormat object OgcTimeFormat { - /** Represent [[OgcTime]] as [[OgcTimePositions]]. */ + /** + * Represent [[OgcTime]] as [[OgcTimePositions]]. + */ case object Positions extends OgcTimeFormat - /** Represent [[OgcTime]] as [[OgcTimeInterval]]. */ + /** + * Represent [[OgcTime]] as [[OgcTimeInterval]]. + */ case object Interval extends OgcTimeFormat - /** Don't change the internal [[OgcTime]] representation. */ + /** + * Don't change the internal [[OgcTime]] representation. + */ case object Default extends OgcTimeFormat - implicit private val config: Configuration = Configuration.default.copy(transformConstructorNames = _.toLowerCase) + implicit private val config: Configuration = Configuration.default.copy(transformConstructorNames = _.toLowerCase) implicit val ogcTimeFormatCodec: Codec[OgcTimeFormat] = deriveEnumerationCodec } diff --git a/ogc/src/main/scala/geotrellis/server/ogc/OutputFormat.scala b/ogc/src/main/scala/geotrellis/server/ogc/OutputFormat.scala index f38d28cc..a4114091 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/OutputFormat.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/OutputFormat.scala @@ -27,7 +27,7 @@ sealed trait OutputFormat object OutputFormat { case object GeoTiff extends OutputFormat { override def toString = "image/geotiff" } - case object Jpg extends OutputFormat { override def toString = "image/jpeg" } + case object Jpg extends OutputFormat { override def toString = "image/jpeg" } object Png { final val PngEncodingRx = """image/png(?:;encoding=(\w+))?""".r @@ -42,7 +42,7 @@ object OutputFormat { } def stringToEncodding(enc: String): Option[PngColorEncoding] = - Option(enc) map { + Option(enc).map { case "rgba" => RgbaPngEncoding case "greya" => GreyaPngEncoding case "rgb" => RgbPngEncoding(None) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/ows/OwsDataRecord.scala b/ogc/src/main/scala/geotrellis/server/ogc/ows/OwsDataRecord.scala index b32328a9..93a4d983 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/ows/OwsDataRecord.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/ows/OwsDataRecord.scala @@ -21,7 +21,9 @@ import scalaxb.{CanWriteXML, DataRecord} import scala.reflect.{classTag, ClassTag} -/** A function that reduces boilerplate by generating a namespace and a key for a common ows [[DataRecord]] */ +/** + * A function that reduces boilerplate by generating a namespace and a key for a common ows [[DataRecord]] + */ object OwsDataRecord { def apply[T: CanWriteXML: ClassTag](value: T): DataRecord[T] = apply[T](classTag[T].toString.split("\\.").lastOption.flatMap(_.split("Type").headOption), value) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/ows/ServiceMetadata.scala b/ogc/src/main/scala/geotrellis/server/ogc/ows/ServiceMetadata.scala index 42ecca42..020b7e87 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/ows/ServiceMetadata.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/ows/ServiceMetadata.scala @@ -18,7 +18,9 @@ package geotrellis.server.ogc.ows import java.net.URI -/** Service-level metadata; roughly corresponds to ows service identification and service providers */ +/** + * Service-level metadata; roughly corresponds to ows service identification and service providers + */ case class ServiceMetadata( identification: Identification, provider: Provider @@ -39,7 +41,9 @@ case class Provider( contact: Option[ResponsiblePartySubset] ) -/** corresponds roughly to opengis.ows.ResponsiblePartySubsetType */ +/** + * corresponds roughly to opengis.ows.ResponsiblePartySubsetType + */ case class ResponsiblePartySubset( name: Option[String], position: Option[String], diff --git a/ogc/src/main/scala/geotrellis/server/ogc/package.scala b/ogc/src/main/scala/geotrellis/server/ogc/package.scala index ed04a068..09a53efe 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/package.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/package.scala @@ -30,8 +30,8 @@ package object ogc { implicit val ZonedDateTimeOrder: Order[ZonedDateTime] = Order.fromOrdering[ZonedDateTime] implicit class ExtentOps(val self: Extent) extends AnyVal { - def swapXY: Extent = Extent(xmin = self.ymin, ymin = self.xmin, xmax = self.ymax, ymax = self.xmax) - def buffer(cellSize: CellSize): Extent = self.buffer(cellSize.width / 2, cellSize.height / 2) + def swapXY: Extent = Extent(xmin = self.ymin, ymin = self.xmin, xmax = self.ymax, ymax = self.xmax) + def buffer(cellSize: CellSize): Extent = self.buffer(cellSize.width / 2, cellSize.height / 2) def buffer(cellSize: Option[CellSize]): Extent = cellSize.fold(self)(buffer) } diff --git a/ogc/src/main/scala/geotrellis/server/ogc/params/ParamMap.scala b/ogc/src/main/scala/geotrellis/server/ogc/params/ParamMap.scala index 72be53e9..351285fa 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/params/ParamMap.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/params/ParamMap.scala @@ -30,7 +30,9 @@ case class ParamMap(params: Map[String, Seq[String]]) { def getParams(field: String): Option[List[String]] = _params.get(field).map(_.toList) - /** Get a field that must appear only once, otherwise error */ + /** + * Get a field that must appear only once, otherwise error + */ def validatedParam(field: String): ValidatedNel[ParamError, String] = (getParams(field) match { case Some(v :: Nil) => Valid(v) @@ -38,7 +40,9 @@ case class ParamMap(params: Map[String, Seq[String]]) { case None => Invalid(ParamError.MissingParam(field)) }).toValidatedNel - /** Get a field that must appear only once, otherwise error */ + /** + * Get a field that must appear only once, otherwise error + */ def validatedOptionalParam(field: String): ValidatedNel[ParamError, Option[String]] = (getParams(field) match { case None => Valid(Option.empty[String]) @@ -68,7 +72,9 @@ case class ParamMap(params: Map[String, Seq[String]]) { case None => Valid(None) }).toValidatedNel - /** Get a field that must appear only once, parse the value successfully, otherwise error */ + /** + * Get a field that must appear only once, parse the value successfully, otherwise error + */ def validatedParam[T](field: String, parseValue: String => Option[T]): ValidatedNel[ParamError, T] = (getParams(field) match { case Some(v :: Nil) => @@ -80,7 +86,9 @@ case class ParamMap(params: Map[String, Seq[String]]) { case None => Invalid(ParamError.MissingParam(field)) }).toValidatedNel - /** Get a field that must appear only once, and should be one of a list of values, otherwise error */ + /** + * Get a field that must appear only once, and should be one of a list of values, otherwise error + */ def validatedParam(field: String, validValues: Set[String]): ValidatedNel[ParamError, String] = (getParams(field) match { case Some(v :: Nil) if validValues.contains(v.toLowerCase) => Valid(v.toLowerCase) @@ -104,7 +112,7 @@ case class ParamMap(params: Map[String, Seq[String]]) { Valid(default) case Some(versions :: Nil) => val requestedVersions = versions.split(",") - val intersection = requestedVersions.toSet & supportedVersions + val intersection = requestedVersions.toSet & supportedVersions if (intersection.isEmpty) { Invalid(ParamError.NoSupportedVersionError(requestedVersions.toList, supportedVersions.toList)) } else { diff --git a/ogc/src/main/scala/geotrellis/server/ogc/style/ClipDefinition.scala b/ogc/src/main/scala/geotrellis/server/ogc/style/ClipDefinition.scala index 9e440b3f..044f6f37 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/style/ClipDefinition.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/style/ClipDefinition.scala @@ -20,10 +20,10 @@ import org.log4s._ import cats.syntax.option._ abstract class ClipDefinition(repr: String) extends Product with Serializable -case object ClipNone extends ClipDefinition("clip-none") -case object ClipLeft extends ClipDefinition("clip-left") -case object ClipRight extends ClipDefinition("clip-right") -case object ClipBoth extends ClipDefinition("clip-both") +case object ClipNone extends ClipDefinition("clip-none") +case object ClipLeft extends ClipDefinition("clip-left") +case object ClipRight extends ClipDefinition("clip-right") +case object ClipBoth extends ClipDefinition("clip-both") object ClipDefinition { private val logger = getLogger diff --git a/ogc/src/main/scala/geotrellis/server/ogc/style/ColorRampStyle.scala b/ogc/src/main/scala/geotrellis/server/ogc/style/ColorRampStyle.scala index ed4814c1..e039fde7 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/style/ColorRampStyle.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/style/ColorRampStyle.scala @@ -62,12 +62,12 @@ case class ColorRampStyle( val numStops: Int = stops.getOrElse(colorRamp.colors.length) // The colors, interpolated (colors added at start and end for out of bounds values) - val minColor = if (clampWithColor) colorRamp.colors.head else 0x00000000 - val maxColor = if (clampWithColor) colorRamp.colors.last else 0x00000000 + val minColor = if (clampWithColor) colorRamp.colors.head else 0x00000000 + val maxColor = if (clampWithColor) colorRamp.colors.last else 0x00000000 val interpolatedColors: Vector[Int] = minColor +: colorRamp.stops(numStops) :+ maxColor val interpolatedBreaks: Array[Double] = breaks(hists, interpolatedColors.length) :+ Double.MaxValue - val cmap = ColorRamp(interpolatedColors).toColorMap(interpolatedBreaks) + val cmap = ColorRamp(interpolatedColors).toColorMap(interpolatedBreaks) format match { case format: OutputFormat.Png => format.render(raster.tile.band(bandIndex = 0), cmap) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/style/InterpolatedColorMap.scala b/ogc/src/main/scala/geotrellis/server/ogc/style/InterpolatedColorMap.scala index 6b1d36ed..e425547e 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/style/InterpolatedColorMap.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/style/InterpolatedColorMap.scala @@ -51,46 +51,51 @@ case class InterpolatedColorMap( object InterpolatedColorMap { - /** RGB color interpolation logic */ + /** + * RGB color interpolation logic + */ private def RgbLerp(color1: RGBA, color2: RGBA, proportion: Double): RGBA = { - val r = (color1.red + (color2.red - color1.red) * proportion).toInt - val g = (color1.green + (color2.green - color1.green) * proportion).toInt - val b = (color1.blue + (color2.blue - color1.blue) * proportion).toInt + val r = (color1.red + (color2.red - color1.red) * proportion).toInt + val g = (color1.green + (color2.green - color1.green) * proportion).toInt + val b = (color1.blue + (color2.blue - color1.blue) * proportion).toInt val a: Double = (color1.alpha + (color2.alpha - color1.alpha) * proportion) / 2.55 RGBA.fromRGBAPct(r, g, b, a) } - /** For production of colors along a continuum */ - def interpolation(poles: Map[Double, Int], clipDefinition: ClipDefinition): Double => Int = { dbl: Double => - val decomposed = poles.toArray.sortBy(_._1).unzip - val breaks: Array[Double] = decomposed._1 - val colors: Array[Int] = decomposed._2 + /** + * For production of colors along a continuum + */ + def interpolation(poles: Map[Double, Int], clipDefinition: ClipDefinition): Double => Int = { + dbl: Double => + val decomposed = poles.toArray.sortBy(_._1).unzip + val breaks: Array[Double] = decomposed._1 + val colors: Array[Int] = decomposed._2 - val insertionPoint: Int = binarySearch(breaks, dbl) - if (insertionPoint == -1) { - // MIN VALUE - clipDefinition match { - case ClipNone | ClipRight => colors(0) - case ClipLeft | ClipBoth => 0x00000000 - } - } else if (abs(insertionPoint) - 1 == breaks.length) { - // MAX VALUE - clipDefinition match { - case ClipNone | ClipLeft => colors.last - case ClipRight | ClipBoth => 0x00000000 - } - } else if (insertionPoint < 0) { - // MUST INTERPOLATE - val lowerIdx = abs(insertionPoint) - 2 - val higherIdx = abs(insertionPoint) - 1 - val lower = breaks(lowerIdx) - val higher = breaks(higherIdx) - val proportion = (dbl - lower) / (higher - lower) + val insertionPoint: Int = binarySearch(breaks, dbl) + if (insertionPoint == -1) { + // MIN VALUE + clipDefinition match { + case ClipNone | ClipRight => colors(0) + case ClipLeft | ClipBoth => 0x00000000 + } + } else if (abs(insertionPoint) - 1 == breaks.length) { + // MAX VALUE + clipDefinition match { + case ClipNone | ClipLeft => colors.last + case ClipRight | ClipBoth => 0x00000000 + } + } else if (insertionPoint < 0) { + // MUST INTERPOLATE + val lowerIdx = abs(insertionPoint) - 2 + val higherIdx = abs(insertionPoint) - 1 + val lower = breaks(lowerIdx) + val higher = breaks(higherIdx) + val proportion = (dbl - lower) / (higher - lower) - RgbLerp(RGBA(colors(lowerIdx)), RGBA(colors(higherIdx)), proportion).int - } else { - // Direct hit - colors(insertionPoint) - } + RgbLerp(RGBA(colors(lowerIdx)), RGBA(colors(higherIdx)), proportion).int + } else { + // Direct hit + colors(insertionPoint) + } } } diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wcs/CapabilitiesView.scala b/ogc/src/main/scala/geotrellis/server/ogc/wcs/CapabilitiesView.scala index 8641a353..70bb6d3d 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wcs/CapabilitiesView.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wcs/CapabilitiesView.scala @@ -36,7 +36,7 @@ class CapabilitiesView[F[_]: Functor](wcsModel: WcsModel[F], serviceUrl: URL, ex def toXML: F[Elem] = { val serviceIdentification = ServiceIdentification( Title = LanguageStringType(wcsModel.serviceMetadata.identification.title) :: Nil, - Abstract = LanguageStringType( + AbstractValue = LanguageStringType( wcsModel.serviceMetadata.identification.description ) :: Nil, Keywords = KeywordsType( @@ -230,19 +230,19 @@ class CapabilitiesView[F[_]: Functor](wcsModel: WcsModel[F], serviceUrl: URL, ex object CapabilitiesView { def coverageSummaries[F[_]: Functor](wcsModel: WcsModel[F]): F[List[CoverageSummaryType]] = wcsModel.sources.store.map(_.map { src => - val crs = src.nativeCrs.head - val wgs84extent = ReprojectRasterExtent(src.nativeRE, crs, LatLng).extent + val crs = src.nativeCrs.head + val wgs84extent = ReprojectRasterExtent(src.nativeRE, crs, LatLng).extent val uniqueCrs: List[CRS] = (crs :: LatLng :: wcsModel.supportedProjections).distinct CoverageSummaryType( Title = LanguageStringType(src.title) :: Nil, - Abstract = Nil, + AbstractValue = Nil, Keywords = Nil, WGS84BoundingBox = WGS84BoundingBoxType( LowerCorner = wgs84extent.ymin :: wgs84extent.xmin :: Nil, UpperCorner = wgs84extent.ymax :: wgs84extent.xmax :: Nil ) :: Nil, - SupportedCRS = new URI("urn:ogc:def:crs:OGC::imageCRS") :: (uniqueCrs flatMap { crs => URN.fromCrs(crs).map(new URI(_)) }), + SupportedCRS = new URI("urn:ogc:def:crs:OGC::imageCRS") :: (uniqueCrs.flatMap { crs => URN.fromCrs(crs).map(new URI(_)) }), SupportedFormat = OutputFormat.all.reverse, coveragesummarytypeoption = DataRecord(None, "Identifier".some, src.name) ) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wcs/CoverageView.scala b/ogc/src/main/scala/geotrellis/server/ogc/wcs/CoverageView.scala index 116be66a..ca45b36b 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wcs/CoverageView.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wcs/CoverageView.scala @@ -42,7 +42,7 @@ class CoverageView[F[_]: Functor](wcsModel: WcsModel[F], serviceUrl: URL, identi def toXML: F[Elem] = { val sources = if (identifiers == Nil) wcsModel.sources.store else wcsModel.sources.find(withNames(identifiers.toSet)) val sourcesMap: F[Map[String, List[OgcSource]]] = sources.map(_.groupBy(_.name)) - val coverageTypeMap = sourcesMap.map(_.mapValues(CoverageView.sourceDescription(wcsModel.supportedProjections, _))) + val coverageTypeMap = sourcesMap.map(_.mapValues(CoverageView.sourceDescription(wcsModel.supportedProjections, _))) coverageTypeMap.map { coverageType => scalaxb .toXML[CoverageDescriptions]( @@ -71,7 +71,7 @@ object CoverageView { ) } .reduce { (re1, re2) => - val e = re1.extent combine re2.extent + val e = re1.extent.combine(re2.extent) val cs = if (re1.cellSize.resolution < re2.cellSize.resolution) re1.cellSize @@ -89,10 +89,10 @@ object CoverageView { } def sourceDescription(supportedProjections: List[CRS], sources: List[OgcSource]): CoverageDescriptionType = { - val source = sources.head - val nativeCrs = source.nativeCrs.head - val re = source.nativeRE - val ex = re.extent + val source = sources.head + val nativeCrs = source.nativeCrs.head + val re = source.nativeRE + val ex = re.extent val Dimensions(w, h) = re.dimensions /** @@ -126,7 +126,7 @@ object CoverageView { CoverageDescriptionType( Title = LanguageStringType(source.title) :: Nil, - Abstract = Nil, + AbstractValue = Nil, Keywords = Nil, Identifier = source.name, Metadata = Nil, @@ -137,7 +137,7 @@ object CoverageView { LowerCorner = 0d :: 0d :: Nil, UpperCorner = w.toDouble :: h.toDouble :: Nil, attributes = Map( - "@crs" -> DataRecord(new URI("urn:ogc:def:crs:OGC::imageCRS")), + "@crs" -> DataRecord(new URI("urn:ogc:def:crs:OGC::imageCRS")), "@dimensions" -> DataRecord(BigInt(2)) ) ) @@ -149,7 +149,7 @@ object CoverageView { LowerCorner = lex.ymin :: lex.xmin :: Nil, UpperCorner = lex.ymax :: lex.xmax :: Nil, attributes = Map( - "@crs" -> DataRecord(new URI(URN.unsafeFromCrs(crs))), + "@crs" -> DataRecord(new URI(URN.unsafeFromCrs(crs))), "@dimensions" -> DataRecord(BigInt(2)) ) ) @@ -169,7 +169,7 @@ object CoverageView { LowerCorner = lex.ymin :: lex.xmin :: Nil, UpperCorner = lex.ymax :: lex.xmax :: Nil, attributes = Map( - "@crs" -> DataRecord(new URI(URN.unsafeFromCrs(crs))), + "@crs" -> DataRecord(new URI(URN.unsafeFromCrs(crs))), "@dimensions" -> DataRecord(BigInt(2)) ) ) @@ -181,7 +181,7 @@ object CoverageView { LowerCorner = lex.xmin :: lex.ymin :: Nil, UpperCorner = lex.xmax :: lex.ymax :: Nil, attributes = Map( - "@crs" -> DataRecord(new URI(URN.unsafeFromCrs(crs))), + "@crs" -> DataRecord(new URI(URN.unsafeFromCrs(crs))), "@dimensions" -> DataRecord(BigInt(2)) ) ) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wcs/GetCoverage.scala b/ogc/src/main/scala/geotrellis/server/ogc/wcs/GetCoverage.scala index 41adc9b5..b15cd1c2 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wcs/GetCoverage.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wcs/GetCoverage.scala @@ -34,13 +34,13 @@ import cats.syntax.traverse._ import cats.syntax.flatMap._ import cats.instances.option._ import com.github.blemale.scaffeine.{Cache, Scaffeine} -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import scala.concurrent.duration._ -class GetCoverage[F[_]: Concurrent: Parallel: Logger](wcsModel: WcsModel[F]) { +class GetCoverage[F[_]: Async: Parallel: Logger](wcsModel: WcsModel[F]) { def renderLayers(params: GetCoverageWcsParams): F[Option[Array[Byte]]] = { - val e = params.extent + val e = params.extent val cs = params.cellSize wcsModel .getLayers(params) @@ -52,7 +52,7 @@ class GetCoverage[F[_]: Concurrent: Parallel: Logger](wcsModel: WcsModel[F]) { LayerExtent(mal.algebra.pure[F], mal.parameters.pure[F], ConcurrentInterpreter.DEFAULT[F], mal.targetCellType) } .traverse { eval => - eval(e, cs) map { + eval(e, cs).map { case Valid(mbtile) => val bytes = Raster(mbtile, e).render(params.crs, None, params.format, Nil) requestCache.put(params, bytes) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsModel.scala b/ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsModel.scala index 8c68a429..c03e5bab 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsModel.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsModel.scala @@ -25,7 +25,9 @@ import cats.syntax.functor._ import geotrellis.store.query.RepositoryM import geotrellis.proj4.CRS -/** This class holds all the information necessary to construct a response to a WCS request */ +/** + * This class holds all the information necessary to construct a response to a WCS request + */ case class WcsModel[F[_]: Functor]( serviceMetadata: ows.ServiceMetadata, sources: RepositoryM[F, List, OgcSource], @@ -39,7 +41,7 @@ case class WcsModel[F[_]: Functor]( case rs: RasterOgcSource => rs.toLayer(p.crs, None, p.temporalSequence) case mas: MapAlgebraSource => val (name, title, algebra, resampleMethod, overviewStrategy) = (mas.name, mas.title, mas.algebra, mas.resampleMethod, mas.overviewStrategy) - val simpleLayers = mas.sources.mapValues(rs => SimpleOgcLayer(name, title, p.crs, rs, None, resampleMethod, overviewStrategy)) + val simpleLayers = mas.sources.mapValues(rs => SimpleOgcLayer(name, title, p.crs, rs, None, resampleMethod, overviewStrategy)) val extendedParameters = extendedParametersBinding.flatMap(_.apply(p.params)) MapAlgebraOgcLayer( name, diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsParams.scala b/ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsParams.scala index 57400d89..985a6db5 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsParams.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsParams.scala @@ -44,9 +44,8 @@ case class DescribeCoverageWcsParams(version: String, identifiers: Seq[String]) /** * "EPSG:4326" or "WGS84" use the latitude first, longitude second axis order. According to the WCS spec for 1.1, some CRS have inverted axis box: - * 1.0.0: minx,miny,maxx,maxy - * 1.1.0, 1.1.2: OGC 07-067r5 (WCS 1.1.2) referes to OGC 06-121r3 which says "The number of axes included, and the order of these axes, shall be as - * specified by the referenced CRS." That means inverted for geographic. + * 1.0.0: minx,miny,maxx,maxy 1.1.0, 1.1.2: OGC 07-067r5 (WCS 1.1.2) referes to OGC 06-121r3 which says "The number of axes included, and the order of + * these axes, shall be as specified by the referenced CRS." That means inverted for geographic. * * Reference to QGIS: https://github.com/qgis/QGIS/blob/final-3_10_2/src/providers/wcs/qgswcsprovider.cpp#L674 Parameters descriptions can be also * found here: https://mapserver.org/ogc/wcs_server.html @@ -72,13 +71,13 @@ case class GetCoverageWcsParams( ) extends WcsParams { def toQuery: Query = { - val query = withName(identifier) and intersects(ProjectedGeometry(extent, crs)) + val query = withName(identifier).and(intersects(ProjectedGeometry(extent, crs))) temporalSequence.headOption match { // For now, since a RasterSource maps 1 to 1 to OgcSource, we only create a // temporal filter on the first TimeInterval in the list. Revisit when we are // able to utilize all requested TimeIntervals. - case Some(timeInterval: OgcTimeInterval) => query and between(timeInterval.start, timeInterval.end) - case Some(OgcTimePositions(list)) => query and list.toList.map(at(_)).reduce(_ or _) + case Some(timeInterval: OgcTimeInterval) => query.and(between(timeInterval.start, timeInterval.end)) + case Some(OgcTimePositions(list)) => query.and(list.toList.map(at(_)).reduce(_ or _)) case _ => query } } @@ -116,15 +115,17 @@ case class GetCoverageWcsParams( object WcsParams { - val wcsVersion = "1.1.1" + val wcsVersion = "1.1.1" val wcsVersions = Set(wcsVersion, "1.1.0") - /** Defines valid request types, and the WcsParams to build from them. */ + /** + * Defines valid request types, and the WcsParams to build from them. + */ private val requestMap: Map[String, ParamMap => ValidatedNel[ParamError, WcsParams]] = Map( - "getcapabilities" -> GetCapabilitiesWcsParams.build, + "getcapabilities" -> GetCapabilitiesWcsParams.build, "describecoverage" -> DescribeCoverageWcsParams.build, - "getcoverage" -> GetCoverageWcsParams.build + "getcoverage" -> GetCoverageWcsParams.build ) private val validRequests = requestMap.keys.toSet @@ -132,8 +133,8 @@ object WcsParams { def apply(queryParams: Map[String, Seq[String]]): ValidatedNel[ParamError, WcsParams] = { val params = ParamMap(queryParams) - val serviceParam = params.validatedParam("service", validValues = Set("wcs")) - val requestParam = params.validatedParam("request", validValues = validRequests) + val serviceParam = params.validatedParam("service", validValues = Set("wcs")) + val requestParam = params.validatedParam("request", validValues = validRequests) val firstStageValidation = (serviceParam, requestParam).mapN { case (_, b) => b } firstStageValidation @@ -154,11 +155,12 @@ object DescribeCoverageWcsParams { val versionParam = params.validatedVersion(WcsParams.wcsVersion, WcsParams.wcsVersions) versionParam - .andThen { version: String => - params - .validatedParam("identifiers") - .map(_.split(",").toSeq) - .map(ids => (version, ids)) + .andThen { + version: String => + params + .validatedParam("identifiers") + .map(_.split(",").toSeq) + .map(ids => (version, ids)) } .map { case (version, identifiers) => DescribeCoverageWcsParams(version, identifiers) } } @@ -169,7 +171,9 @@ object GetCoverageWcsParams { params.validatedParam[(Vector[Double], Option[String])]( field, bboxStr => - /** Usually the CRS is the 5th element in the bbox param. */ + /** + * Usually the CRS is the 5th element in the bbox param. + */ try { val v = bboxStr.split(",").toVector if (v.length == 4) (v.map(_.toDouble), None).some @@ -184,87 +188,102 @@ object GetCoverageWcsParams { val versionParam = params.validatedVersion(WcsParams.wcsVersion, WcsParams.wcsVersions) versionParam - .andThen { version: String => - /** - * Collected the bbox, id, and possibly the CRS in one shot. This is because the boundingbox param could contain the CRS as the 5th element. - */ - val idAndBboxAndCrsOption = { - val identifier = params.validatedParam("identifier") - val bboxAndCrsOption = getBboxAndCrsOption(params, "boundingbox") - (identifier, bboxAndCrsOption).mapN { case (id, (bbox, crsOption)) => (id, bbox, crsOption) } - } - - /** gridBaseCRS is an optional param, in case it is missing we can always grab it from the data source. */ - val gridBaseCRS = params.validatedOptionalParam("gridbasecrs").andThen(_.traverse(CRSUtils.ogcToCRS)) - - /** If the CRS is not provided, than it is in the CRS of the dataset. */ - val idAndBboxAndCrs: Validated[NEL[ParamError], (String, Vector[Double], CRS)] = - idAndBboxAndCrsOption - .andThen { case (id, bbox, crsOption) => - /** If the CRS wasn't in the boundingbox parameter, pull it out of the CRS field. */ - crsOption match { - case Some(crsDesc) => CRSUtils.ogcToCRS(crsDesc).map(crs => (id, bbox, crs)) - case None => - gridBaseCRS match { - case Valid(Some(crs)) => gridBaseCRS.map(_ => (id, bbox, crs)) - case _ => Invalid(ParamError.MissingParam("BoundingBox CRS")).toValidatedNel - } + .andThen { + version: String => + /** + * Collected the bbox, id, and possibly the CRS in one shot. This is because the boundingbox param could contain the CRS as the 5th element. + */ + val idAndBboxAndCrsOption = { + val identifier = params.validatedParam("identifier") + val bboxAndCrsOption = getBboxAndCrsOption(params, "boundingbox") + (identifier, bboxAndCrsOption).mapN { case (id, (bbox, crsOption)) => (id, bbox, crsOption) } + } + + /** + * gridBaseCRS is an optional param, in case it is missing we can always grab it from the data source. + */ + val gridBaseCRS = params.validatedOptionalParam("gridbasecrs").andThen(_.traverse(CRSUtils.ogcToCRS)) + + /** + * If the CRS is not provided, than it is in the CRS of the dataset. + */ + val idAndBboxAndCrs: Validated[NEL[ParamError], (String, Vector[Double], CRS)] = + idAndBboxAndCrsOption + .andThen { case (id, bbox, crsOption) => + /** + * If the CRS wasn't in the boundingbox parameter, pull it out of the CRS field. + */ + crsOption match { + case Some(crsDesc) => CRSUtils.ogcToCRS(crsDesc).map(crs => (id, bbox, crs)) + case None => + gridBaseCRS match { + case Valid(Some(crs)) => gridBaseCRS.map(_ => (id, bbox, crs)) + case _ => Invalid(ParamError.MissingParam("BoundingBox CRS")).toValidatedNel + } + } } - } - val temporalSequenceOption = params.validatedOgcTimeSequence("timesequence") + val temporalSequenceOption = params.validatedOgcTimeSequence("timesequence") - val format = - params - .validatedParam("format") - .andThen { f => - OutputFormat.fromString(f) match { - case Some(format) => Valid(format).toValidatedNel - case None => Invalid(UnsupportedFormatError(f)).toValidatedNel + val format = + params + .validatedParam("format") + .andThen { f => + OutputFormat.fromString(f) match { + case Some(format) => Valid(format).toValidatedNel + case None => Invalid(UnsupportedFormatError(f)).toValidatedNel + } } - } - /** GridCS: default is "urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS" */ - val gridCS = params - .validatedOptionalParam[URI]("gridcs", s => Try(new URI(s)).toOption) - .map(_.getOrElse(new URI("urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS"))) - - /** - * GridType: default is "urn:ogc:def:method:WCS:1.1:2dSimpleGrid" (This GridType disallows rotation or skew relative to the GridBaseCRS – - * therefore GridOffsets has only two numbers.) - */ - val gridType = params - .validatedOptionalParam[URI]("gridtype", s => Try(new URI(s)).toOption) - .map(_.getOrElse(new URI("urn:ogc:def:method:WCS:1.1:2dSimpleGrid"))) - - /** GridOrigin: default is "0,0" (KVP) or "0 0" (XML); it is the boundingBox corner. */ - val gridOrigin = params - .validatedOptionalParam[(Double, Double)]( - "gridorigin", + /** + * GridCS: default is "urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS" + */ + val gridCS = params + .validatedOptionalParam[URI]("gridcs", s => Try(new URI(s)).toOption) + .map(_.getOrElse(new URI("urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS"))) + + /** + * GridType: default is "urn:ogc:def:method:WCS:1.1:2dSimpleGrid" (This GridType disallows rotation or skew relative to the GridBaseCRS – + * therefore GridOffsets has only two numbers.) + */ + val gridType = params + .validatedOptionalParam[URI]("gridtype", s => Try(new URI(s)).toOption) + .map(_.getOrElse(new URI("urn:ogc:def:method:WCS:1.1:2dSimpleGrid"))) + + /** + * GridOrigin: default is "0,0" (KVP) or "0 0" (XML); it is the boundingBox corner. + */ + val gridOrigin = params + .validatedOptionalParam[(Double, Double)]( + "gridorigin", + s => + Try { + val List(fst, snd) = s.split(",").map(_.toDouble).toList + (fst, snd) + }.toOption + ) + + /** + * If not passed, than the original source resolution would be used. + */ + val gridOffsets = params.validatedOptionalParam[(Double, Double)]( + "gridoffsets", s => Try { - val List(fst, snd) = s.split(",").map(_.toDouble).toList - (fst, snd) + + /** + * In case 4 parameters would be passed, we care only about the first and the last only. + */ + val list = s.split(",").map(_.toDouble).toList + (list.head, list.last) }.toOption ) - /** If not passed, than the original source resolution would be used. */ - val gridOffsets = params.validatedOptionalParam[(Double, Double)]( - "gridoffsets", - s => - Try { - - /** In case 4 parameters would be passed, we care only about the first and the last only. */ - val list = s.split(",").map(_.toDouble).toList - (list.head, list.last) - }.toOption - ) - - (idAndBboxAndCrs, format, gridBaseCRS, gridCS, gridType, gridOrigin, gridOffsets, temporalSequenceOption).mapN { - case ((id, bbox, crs), format, gridBaseCRS, gridCS, gridType, gridOrigin, gridOffsets, temporalSeqOpt) => - val extent = Extent(bbox(0), bbox(1), bbox(2), bbox(3)) - GetCoverageWcsParams(version, id, extent, temporalSeqOpt, format, gridBaseCRS, gridCS, gridType, gridOrigin, gridOffsets, crs, params) - } + (idAndBboxAndCrs, format, gridBaseCRS, gridCS, gridType, gridOrigin, gridOffsets, temporalSequenceOption).mapN { + case ((id, bbox, crs), format, gridBaseCRS, gridCS, gridType, gridOrigin, gridOffsets, temporalSeqOpt) => + val extent = Extent(bbox(0), bbox(1), bbox(2), bbox(3)) + GetCoverageWcsParams(version, id, extent, temporalSeqOpt, format, gridBaseCRS, gridCS, gridType, gridOrigin, gridOffsets, crs, params) + } } } } diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wcs/package.scala b/ogc/src/main/scala/geotrellis/server/ogc/wcs/package.scala index 364e2aed..a3c93747 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wcs/package.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wcs/package.scala @@ -20,11 +20,11 @@ import scala.xml.NamespaceBinding package object wcs { val wcsScope: NamespaceBinding = scalaxb.toScope( - None -> "http://www.opengis.net/wcs/1.1.1", - Some("gml") -> "http://www.opengis.net/gml", - Some("ows") -> "http://www.opengis.net/ows/1.1", - Some("ogc") -> "http://www.opengis.net/ogc", + None -> "http://www.opengis.net/wcs/1.1.1", + Some("gml") -> "http://www.opengis.net/gml", + Some("ows") -> "http://www.opengis.net/ows/1.1", + Some("ogc") -> "http://www.opengis.net/ogc", Some("xlink") -> "http://www.w3.org/1999/xlink", - Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" + Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" ) } diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wfs/WfsFeatureCollection.scala b/ogc/src/main/scala/geotrellis/server/ogc/wfs/WfsFeatureCollection.scala index 0e876246..98b7903c 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wfs/WfsFeatureCollection.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wfs/WfsFeatureCollection.scala @@ -80,7 +80,7 @@ object WfsFeatureCollection { case p: Polygon => p :: Nil case mp: MultiPolygon => mp.polygons.toList case po: Point => - val (x, y) = po.getX -> po.getY + val (x, y) = po.getX -> po.getY val CellSize(w, h) = cellSize Polygon( (x - w, y - h), @@ -125,8 +125,8 @@ object WfsFeatureCollection { .mkString(" "), attributes = Map( "@decimal" -> DataRecord("."), - "@cs" -> DataRecord(","), - "@ts" -> DataRecord(" ") + "@cs" -> DataRecord(","), + "@ts" -> DataRecord(" ") ) ) ) :: Nil diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wfs/package.scala b/ogc/src/main/scala/geotrellis/server/ogc/wfs/package.scala index 9c7e7265..e64d1ccc 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wfs/package.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wfs/package.scala @@ -22,12 +22,12 @@ import scala.xml.{Elem, NamespaceBinding, NodeSeq, XML} package object wfs { val wfsScope: NamespaceBinding = scalaxb.toScope( - None -> "http://www.opengis.net/wfs", - Some("gml") -> "http://www.opengis.net/gml", - Some("ows") -> "http://www.opengis.net/ows/1.1", - Some("ogc") -> "http://www.opengis.net/ogc", + None -> "http://www.opengis.net/wfs", + Some("gml") -> "http://www.opengis.net/gml", + Some("ows") -> "http://www.opengis.net/ows/1.1", + Some("ogc") -> "http://www.opengis.net/ogc", Some("xlink") -> "http://www.w3.org/1999/xlink", - Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" + Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" ) implicit class ElemOps(val elem: Elem) extends AnyVal { diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/CapabilitiesView.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/CapabilitiesView.scala index eb1b3068..f1785334 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/CapabilitiesView.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/CapabilitiesView.scala @@ -108,7 +108,7 @@ case class CapabilitiesView[F[_]: Functor: Apply: Monad](model: WmsModel[F], ser ) :: Nil ) - modelAsLayer(model.parentLayerMeta, model) map { layer => + modelAsLayer(model.parentLayerMeta, model).map { layer => val capability = Capability( Request = Request( GetCapabilities = getCapabilities, @@ -144,7 +144,7 @@ object CapabilitiesView { if (crs.isGeographic) BoundingBox( Map( - "@CRS" -> s"EPSG:${crs.epsgCode.get}", + "@CRS" -> s"EPSG:${crs.epsgCode.get}", "@minx" -> extent.ymin, "@miny" -> extent.xmin, "@maxx" -> extent.ymax, @@ -156,7 +156,7 @@ object CapabilitiesView { else BoundingBox( Map( - "@CRS" -> s"EPSG:${crs.epsgCode.get}", + "@CRS" -> s"EPSG:${crs.epsgCode.get}", "@minx" -> extent.xmin, "@miny" -> extent.ymin, "@maxx" -> extent.xmax, @@ -180,7 +180,7 @@ object CapabilitiesView { Layer( Name = source.name.some, Title = source.title, - Abstract = None, + AbstractValue = None, KeywordList = None, // extra CRS that is supported by this layer CRS = (parentProjections ++ source.nativeCrs).distinct.map { crs => @@ -201,7 +201,7 @@ object CapabilitiesView { ) } .reduce { (re1, re2) => - val e = re1.extent combine re2.extent + val e = re1.extent.combine(re2.extent) val cs = if (re1.cellSize.resolution < re2.cellSize.resolution) re1.cellSize @@ -230,8 +230,8 @@ object CapabilitiesView { Dimension( tp.toString, Map( - "@name" -> DataRecord("time"), - "@units" -> DataRecord("ISO8601"), + "@name" -> DataRecord("time"), + "@units" -> DataRecord("ISO8601"), "@default" -> DataRecord(source.timeDefault.selectTime(nel).toInstant.toString) ) ) :: Nil @@ -239,8 +239,8 @@ object CapabilitiesView { Dimension( ti.toString, Map( - "@name" -> DataRecord("time"), - "@units" -> DataRecord("ISO8601"), + "@name" -> DataRecord("time"), + "@units" -> DataRecord("ISO8601"), "@default" -> DataRecord(source.timeDefault.selectTime(NonEmptyList.of(start, end)).toInstant.toString) ) ) :: Nil @@ -261,14 +261,14 @@ object CapabilitiesView { } def modelAsLayer[F[_]: Monad](parentLayerMeta: WmsParentLayerMeta, model: WmsModel[F]): F[Layer] = { - val bboxAndLayers = model.sources.store map { sources => - val bboxes = sources map { source => + val bboxAndLayers = model.sources.store.map { sources => + val bboxes = sources.map { source => val llre = source match { case mas: MapAlgebraSource => mas.sourcesList .map(rs => ReprojectRasterExtent(rs.gridExtent, rs.crs, LatLng, Options.DEFAULT.copy(mas.resampleMethod))) .reduce { (re1, re2) => - val e = re1.extent combine re2.extent + val e = re1.extent.combine(re2.extent) val cs = if (re1.cellSize.resolution < re2.cellSize.resolution) re1.cellSize else re2.cellSize new GridExtent[Long](e, cs) } @@ -284,7 +284,7 @@ object CapabilitiesView { llre.extent } - val bbox = bboxes.tail.fold(bboxes.head)(_ combine _) + val bbox = bboxes.tail.fold(bboxes.head)(_ combine _) val ogcBbox = EX_GeographicBoundingBox(bbox.xmin, bbox.xmax, bbox.ymin, bbox.ymax).some (ogcBbox, sources.map(_.toLayer(parentLayerMeta.supportedProjections))) } @@ -292,7 +292,7 @@ object CapabilitiesView { Layer( Name = parentLayerMeta.name, Title = parentLayerMeta.title, - Abstract = parentLayerMeta.description, + AbstractValue = parentLayerMeta.description, KeywordList = None, // All layers are avail at least at this CRS // All sublayers would have metadata in this CRS + its own @@ -317,7 +317,7 @@ object CapabilitiesView { Dimension( tp.toString, Map( - "@name" -> DataRecord("time"), + "@name" -> DataRecord("time"), "@units" -> DataRecord("ISO8601") ) ) :: Nil @@ -325,7 +325,7 @@ object CapabilitiesView { Dimension( ti.toString, Map( - "@name" -> DataRecord("time"), + "@name" -> DataRecord("time"), "@units" -> DataRecord("ISO8601") ) ) :: Nil diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfo.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfo.scala index 27ad3093..233ca82e 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfo.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfo.scala @@ -20,9 +20,8 @@ import geotrellis.server.LayerExtent import geotrellis.server.ogc.{MapAlgebraOgcLayer, SimpleOgcLayer} import geotrellis.server.ogc.wms.WmsParams.{GetFeatureInfoParams, GetMapParams} import geotrellis.server.utils.throwableExtensions - import cats.data.Validated.{Invalid, Valid} -import cats.effect.Concurrent +import cats.effect.{Async, Concurrent} import cats.{ApplicativeThrow, Parallel} import cats.syntax.functor._ import cats.syntax.applicative._ @@ -30,7 +29,7 @@ import cats.syntax.flatMap._ import cats.syntax.option._ import cats.syntax.traverse._ import cats.syntax.applicativeError._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import io.circe.syntax._ import io.circe.Json import com.azavea.maml.eval.ConcurrentInterpreter @@ -41,7 +40,7 @@ import opengis.wms._ import opengis._ import scalaxb._ -case class GetFeatureInfo[F[_]: Logger: Parallel: Concurrent: ApplicativeThrow]( +case class GetFeatureInfo[F[_]: Logger: Parallel: Async: ApplicativeThrow]( model: WmsModel[F], rasterCache: Cache[GetMapParams, Raster[MultibandTile]] ) { @@ -58,19 +57,22 @@ case class GetFeatureInfo[F[_]: Logger: Parallel: Concurrent: ApplicativeThrow]( LayerExtent(ml.algebra.pure[F], ml.parameters.pure[F], ConcurrentInterpreter.DEFAULT[F], ml.targetCellType) } - evalExtent(re.extent, re.cellSize.some).map { - case Valid(mbtile) => Valid(mbtile) - case Invalid(errs) => Invalid(errs) - }.attempt flatMap { - case Right(Valid(mbtile)) => // success - val raster = Raster(mbtile, re.extent) - rasterCache.put(params.toGetMapParams, raster) - featureFromRaster(raster, params).pure[F].widen - case Right(Invalid(errs)) => // maml-specific errors - Logger[F].debug(errs.toList.toString).as(Left(LayerNotDefinedException(errs.toList.toString, params.version))).widen - case Left(err) => // exceptions - Logger[F].error(err.stackTraceString).as(Left(LayerNotDefinedException(err.stackTraceString, params.version))).widen - }: F[Either[GetFeatureInfoException, Feature[Geometry, Json]]] + evalExtent(re.extent, re.cellSize.some) + .map { + case Valid(mbtile) => Valid(mbtile) + case Invalid(errs) => Invalid(errs) + } + .attempt + .flatMap { + case Right(Valid(mbtile)) => // success + val raster = Raster(mbtile, re.extent) + rasterCache.put(params.toGetMapParams, raster) + featureFromRaster(raster, params).pure[F].widen + case Right(Invalid(errs)) => // maml-specific errors + Logger[F].debug(errs.toList.toString).as(Left(LayerNotDefinedException(errs.toList.toString, params.version))).widen + case Left(err) => // exceptions + Logger[F].error(err.stackTraceString).as(Left(LayerNotDefinedException(err.stackTraceString, params.version))).widen + }: F[Either[GetFeatureInfoException, Feature[Geometry, Json]]] } .headOption .sequence diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfoException.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfoException.scala index 4ac86089..1b25f429 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfoException.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfoException.scala @@ -38,7 +38,7 @@ sealed trait GetFeatureInfoException extends java.lang.Exception { ServiceException = ServiceExceptionType( msg, Map( - "@code" -> DataRecord(code), + "@code" -> DataRecord(code), "@locator" -> DataRecord("noLocator") ) ) :: Nil, @@ -58,9 +58,9 @@ object GetFeatureInfoException { Map( "version" -> e.version.asJson, "exceptions" -> List( - "code" -> e.code, + "code" -> e.code, "locator" -> "noLocator", - "text" -> e.msg + "text" -> e.msg ).asJson ).asJson } diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfoExtended.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfoExtended.scala index 09793b6f..cdcac7fa 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfoExtended.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetFeatureInfoExtended.scala @@ -22,7 +22,7 @@ import geotrellis.server.ogc.{FeatureCollection, MapAlgebraOgcLayer, SimpleOgcLa import geotrellis.server.ogc.wms.WmsParams.GetFeatureInfoExtendedParams import geotrellis.server.utils.throwableExtensions import cats.data.Validated.{Invalid, Valid} -import cats.effect.Concurrent +import cats.effect.{Async, Concurrent} import cats.{ApplicativeThrow, Parallel} import cats.syntax.nested._ import cats.syntax.functor._ @@ -32,7 +32,7 @@ import cats.syntax.flatMap._ import cats.syntax.traverse._ import cats.syntax.applicativeError._ import com.azavea.maml.error.MamlError -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import io.circe._ import io.circe.syntax._ import com.azavea.maml.eval.ConcurrentInterpreter @@ -43,7 +43,7 @@ import opengis.wms._ import opengis._ import scalaxb._ -case class GetFeatureInfoExtended[F[_]: Logger: Parallel: Concurrent: ApplicativeThrow]( +case class GetFeatureInfoExtended[F[_]: Logger: Parallel: Async: ApplicativeThrow]( model: WmsModel[F], // cache rasters by the asset name and point rasterCache: Cache[(String, Extent), MultibandTile] @@ -56,7 +56,7 @@ case class GetFeatureInfoExtended[F[_]: Logger: Parallel: Concurrent: Applicativ .flatMap { layers => layers.flatMap { layer => // TODO: move it into GeoTrellis - val mp = params.multiPoint + val mp = params.multiPoint val points = (0 until mp.getNumPoints).map(idx => mp.getGeometryN(idx).asInstanceOf[Point]) val (evalExtent, cs) = layer match { diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetMap.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetMap.scala index 2cd73e99..aa9f7473 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetMap.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetMap.scala @@ -18,7 +18,7 @@ package geotrellis.server.ogc.wms import io.circe.syntax._ import cats.data.Validated.{Invalid, Valid} -import cats.effect.{Concurrent, Sync} +import cats.effect.{Async, Concurrent, Sync} import cats.{ApplicativeThrow, Parallel} import cats.syntax.functor._ import cats.syntax.applicative._ @@ -27,7 +27,7 @@ import cats.syntax.flatMap._ import cats.syntax.option._ import cats.syntax.applicativeError._ import com.azavea.maml.error.Interpreted -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import com.azavea.maml.eval.ConcurrentInterpreter import geotrellis.raster._ import geotrellis.server.{LayerExtent, LayerHistogram} @@ -37,7 +37,7 @@ import geotrellis.server.utils.throwableExtensions import geotrellis.store.query.withName import com.github.blemale.scaffeine.Cache -case class GetMap[F[_]: Logger: Parallel: Concurrent: ApplicativeThrow]( +case class GetMap[F[_]: Logger: Parallel: Async: ApplicativeThrow]( model: WmsModel[F], tileCache: Cache[GetMapParams, Array[Byte]], histoCache: Cache[OgcLayer, Interpreted[List[Histogram[Double]]]] @@ -72,20 +72,23 @@ case class GetMap[F[_]: Logger: Parallel: Concurrent: ApplicativeThrow]( _ <- Sync[F].delay(histoCache.put(layer, hist)) } yield hist - val res: F[Either[GetMapException, Array[Byte]]] = (evalExtent(re.extent, re.cellSize.some), histIO).parMapN { - case (Valid(mbtile), Valid(hists)) => Valid((mbtile, hists)) - case (Invalid(errs), _) => Invalid(errs) - case (_, Invalid(errs)) => Invalid(errs) - }.attempt flatMap { - case Right(Valid((mbtile, hists))) => // success - val rendered = Raster(mbtile, re.extent).render(params.crs, layer.style, params.format, hists) - tileCache.put(params, rendered) - Right(rendered).pure[F].widen - case Right(Invalid(errs)) => // maml-specific errors - Logger[F].debug(errs.toList.toString).as(Left(GetMapBadRequest(errs.asJson.spaces2))).widen - case Left(err) => // exceptions - Logger[F].error(err.stackTraceString).as(Left(GetMapInternalServerError(err.stackTraceString))).widen - } + val res: F[Either[GetMapException, Array[Byte]]] = (evalExtent(re.extent, re.cellSize.some), histIO) + .parMapN { + case (Valid(mbtile), Valid(hists)) => Valid((mbtile, hists)) + case (Invalid(errs), _) => Invalid(errs) + case (_, Invalid(errs)) => Invalid(errs) + } + .attempt + .flatMap { + case Right(Valid((mbtile, hists))) => // success + val rendered = Raster(mbtile, re.extent).render(params.crs, layer.style, params.format, hists) + tileCache.put(params, rendered) + Right(rendered).pure[F].widen + case Right(Invalid(errs)) => // maml-specific errors + Logger[F].debug(errs.toList.toString).as(Left(GetMapBadRequest(errs.asJson.spaces2))).widen + case Left(err) => // exceptions + Logger[F].error(err.stackTraceString).as(Left(GetMapInternalServerError(err.stackTraceString))).widen + } res } @@ -101,7 +104,7 @@ case class GetMap[F[_]: Logger: Parallel: Concurrent: ApplicativeThrow]( .flatMap { _.headOption match { case Some(_) => - val tile = ArrayTile.empty(IntUserDefinedNoDataCellType(0), 1, 1) + val tile = ArrayTile.empty(IntUserDefinedNoDataCellType(0), 1, 1) val raster = Raster(MultibandTile(tile, tile, tile), params.boundingBox) Right(raster.render(params.crs, None, params.format, Nil)).pure[F].widen case _ => diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetMapException.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetMapException.scala index b77f3cdd..9958acd2 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/GetMapException.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/GetMapException.scala @@ -16,6 +16,6 @@ package geotrellis.server.ogc.wms -sealed trait GetMapException extends Exception -case class GetMapBadRequest(msg: String) extends GetMapException +sealed trait GetMapException extends Exception +case class GetMapBadRequest(msg: String) extends GetMapException case class GetMapInternalServerError(msg: String) extends GetMapException diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsModel.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsModel.scala index bd776099..f0fd91a2 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsModel.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsModel.scala @@ -30,7 +30,9 @@ import cats.syntax.applicative._ import cats.syntax.semigroup._ import cats.syntax.option._ -/** This class holds all the information necessary to construct a response to a WMS request */ +/** + * This class holds all the information necessary to construct a response to a WMS request + */ case class WmsModel[F[_]: Monad]( serviceMeta: opengis.wms.Service, parentLayerMeta: WmsParentLayerMeta, @@ -42,7 +44,7 @@ case class WmsModel[F[_]: Monad]( private def getLayer(query: Query, time: OgcTime, supportedCrs: CRS, styles: List[String], params: Option[ParamMap]): F[List[OgcLayer]] = sources.find(query).map { sources => - sources map { source => + sources.map { source => val styleName: Option[String] = styles.headOption.filterNot(_.isEmpty).orElse(source.defaultStyle) val style: Option[OgcStyle] = styleName.flatMap { name => source.styles.find(_.name == name) @@ -79,7 +81,9 @@ case class WmsModel[F[_]: Monad]( .find(_ == p.crs) .fold[F[List[OgcLayer]]](List.empty[OgcLayer].pure[F])(getLayer(p.toQuery, p.time, _, p.styles, p.params.some)) - /** GetFeatureInfoExtended specific only */ + /** + * GetFeatureInfoExtended specific only + */ def getLayer(p: GetFeatureInfoExtendedParams): F[List[OgcLayer]] = parentLayerMeta.supportedProjections .find(_ == p.crs) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParams.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParams.scala index 993d17be..9bae6008 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParams.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParams.scala @@ -67,10 +67,10 @@ object WmsParams { ) extends WmsParams { def toQuery: Query = { val layer = layers.headOption.map(withName).getOrElse(nothing) - val query = layer and intersects(ProjectedGeometry(boundingBox, crs)) + val query = layer.and(intersects(ProjectedGeometry(boundingBox, crs))) time match { - case timeInterval: OgcTimeInterval => query and between(timeInterval.start, timeInterval.end) - case OgcTimePositions(list) => query and list.toList.map(at(_)).reduce(_ or _) + case timeInterval: OgcTimeInterval => query.and(between(timeInterval.start, timeInterval.end)) + case OgcTimePositions(list) => query.and(list.toList.map(at(_)).reduce(_ or _)) case OgcTimeEmpty => query } } @@ -87,7 +87,7 @@ object WmsParams { .andThen { version => val layers = params.validatedParam[List[String]]("layers", s => s.split(",").toList.some) val styles = params.validatedParam[List[String]]("styles", s => s.split(",").toList.some) - val crs = params.validatedParam("crs", s => Try(CRS.fromName(s)).toOption) + val crs = params.validatedParam("crs", s => Try(CRS.fromName(s)).toOption) val bbox = crs.andThen { crs => params.validatedParam( @@ -103,9 +103,9 @@ object WmsParams { ) } - val width = params.validatedParam[Int]("width", s => Try(s.toInt).toOption) + val width = params.validatedParam[Int]("width", s => Try(s.toInt).toOption) val height = params.validatedParam[Int]("height", s => Try(s.toInt).toOption) - val time = params.validatedOgcTime("time") + val time = params.validatedOgcTime("time") val format = params @@ -156,7 +156,7 @@ object WmsParams { object GetFeatureInfoParams { def build(params: ParamMap): ValidatedNel[ParamError, WmsParams] = { - val getMap: ValidatedNel[ParamError, WmsParams] = GetMapParams.build(params) + val getMap: ValidatedNel[ParamError, WmsParams] = GetMapParams.build(params) val versionParam: ValidatedNel[ParamError, String] = params.validatedVersion(wmsVersion) (versionParam, getMap).tupled .andThen { @@ -211,7 +211,9 @@ object WmsParams { } } - /** An extended version, can be passed via POST request, does not perform mosaics, operates with bulk requests. */ + /** + * An extended version, can be passed via POST request, does not perform mosaics, operates with bulk requests. + */ case class GetFeatureInfoExtendedParams( version: String, layers: List[String], @@ -226,11 +228,11 @@ object WmsParams { def toQuery: Query = { val layer = if (layers.nonEmpty) withNames(layers.toSet) else nothing - val query = layer and intersects(projectedGeometry) + val query = layer.and(intersects(projectedGeometry)) time.foldLeft(query) { case (q, t) => t match { - case timeInterval: OgcTimeInterval => query and between(timeInterval.start, timeInterval.end) - case OgcTimePositions(list) => query and list.toList.map(at(_)).reduce(_ or _) + case timeInterval: OgcTimeInterval => query.and(between(timeInterval.start, timeInterval.end)) + case OgcTimePositions(list) => query.and(list.toList.map(at(_)).reduce(_ or _)) case OgcTimeEmpty => q } } @@ -245,8 +247,8 @@ object WmsParams { def apply(queryParams: Map[String, Seq[String]]): ValidatedNel[ParamError, WmsParams] = { val params = ParamMap(queryParams) - val serviceParam = params.validatedParam("service", validValues = Set("wms")) - val requestParam = params.validatedParam("request", validValues = Set("getcapabilities", "getmap", "getfeatureinfo")) + val serviceParam = params.validatedParam("service", validValues = Set("wms")) + val requestParam = params.validatedParam("request", validValues = Set("getcapabilities", "getmap", "getfeatureinfo")) val firstStageValidation = (serviceParam, requestParam).mapN { case (_, b) => b } firstStageValidation.andThen { diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParentLayerMeta.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParentLayerMeta.scala index 96dc1b08..ddb51700 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParentLayerMeta.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParentLayerMeta.scala @@ -18,7 +18,9 @@ package geotrellis.server.ogc.wms import geotrellis.proj4.CRS -/** Parent layer metadata class (used in configuration and reporting capabilities) */ +/** + * Parent layer metadata class (used in configuration and reporting capabilities) + */ case class WmsParentLayerMeta( name: Option[String], title: String, diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wms/package.scala b/ogc/src/main/scala/geotrellis/server/ogc/wms/package.scala index c9f851e1..af683800 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wms/package.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wms/package.scala @@ -42,11 +42,11 @@ package object wms { * "http://www.w3.org/1999/xlink", Some("xs") -> "http://www.w3.org/2001/XMLSchema", Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance") */ val wmsScope: NamespaceBinding = scalaxb.toScope( - None -> "http://www.opengis.net/wms", - Some("ogc") -> "http://www.opengis.net/ogc", + None -> "http://www.opengis.net/wms", + Some("ogc") -> "http://www.opengis.net/ogc", Some("xlink") -> "http://www.w3.org/1999/xlink", - Some("xs") -> "http://www.w3.org/2001/XMLSchema", - Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" + Some("xs") -> "http://www.w3.org/2001/XMLSchema", + Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" ) implicit class withLegendModelMethods(that: LegendModel) { @@ -65,8 +65,8 @@ package object wms { "@{http://www.w3.org/1999/xlink}type" -> Option( DataRecord(xlink.TypeType.fromString(that.`type`, scope = toScope(Some("xlink") -> "http://www.w3.org/1999/xlink"))) ), - "@{http://www.w3.org/1999/xlink}href" -> Option(DataRecord(new URI(that.href))), - "@{http://www.w3.org/1999/xlink}role" -> that.role.map(v => DataRecord(new URI(v))), + "@{http://www.w3.org/1999/xlink}href" -> Option(DataRecord(new URI(that.href))), + "@{http://www.w3.org/1999/xlink}role" -> that.role.map(v => DataRecord(new URI(v))), "@{http://www.w3.org/1999/xlink}title" -> that.title.map(v => DataRecord(v)), "@{http://www.w3.org/1999/xlink}show" -> that.show.map(v => DataRecord(xlink.ShowType.fromString(v, scope = scalaxb.toScope(Some("xlink") -> "http://www.w3.org/1999/xlink"))) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wmts/CapabilitiesView.scala b/ogc/src/main/scala/geotrellis/server/ogc/wmts/CapabilitiesView.scala index 0151ba19..0ee62e09 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wmts/CapabilitiesView.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wmts/CapabilitiesView.scala @@ -48,7 +48,7 @@ class CapabilitiesView[F[_]: Monad](wmtsModel: WmtsModel[F], serviceUrl: URL) { Title = LanguageStringType( wmtsModel.serviceMetadata.identification.title ) :: Nil, - Abstract = LanguageStringType( + AbstractValue = LanguageStringType( wmtsModel.serviceMetadata.identification.description ) :: Nil, Keywords = KeywordsType( @@ -146,7 +146,7 @@ class CapabilitiesView[F[_]: Monad](wmtsModel: WmtsModel[F], serviceUrl: URL) { OperationsMetadata(Operation = getCapabilities :: getTile :: getFeatureInfo :: Nil) } - val layers = modelAsLayers(wmtsModel) + val layers = modelAsLayers(wmtsModel) val tileMatrixSets = wmtsModel.matrices.map(_.toXml) // that's how layers metadata is generated @@ -203,7 +203,7 @@ object CapabilitiesView { LayerType( Title = LanguageStringType(self.title) :: Nil, - Abstract = Nil, + AbstractValue = Nil, Keywords = Nil, WGS84BoundingBox = boundingBox(wgs84extent) :: Nil, Identifier = CodeType(self.name), @@ -213,7 +213,7 @@ object CapabilitiesView { Style = self.styles.map { style => Style( Title = LanguageStringType(style.title) :: Nil, - Abstract = LanguageStringType(style.title) :: Nil, + AbstractValue = LanguageStringType(style.title) :: Nil, Identifier = CodeType(style.name), Keywords = Nil, LegendURL = Nil @@ -236,7 +236,7 @@ object CapabilitiesView { def modelAsLayers[F[_]: Monad](wmtsModel: WmtsModel[F]): F[List[DataRecord[LayerType]]] = wmtsModel.sources.store .map { sources => - sources map { src => + sources.map { src => DataRecord( "wms".some, "Layer".some, diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrix.scala b/ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrix.scala index 7c55a3f7..0db5050c 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrix.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrix.scala @@ -24,7 +24,9 @@ import geotrellis.vector.Extent import opengis.ows._ import opengis.wmts.TileMatrix -/** Relates Geotrellis Extent and TileLayout to a corresponding OGC Tile Matrix */ +/** + * Relates Geotrellis Extent and TileLayout to a corresponding OGC Tile Matrix + */ case class GeotrellisTileMatrix( identifier: String, extent: Extent, @@ -37,7 +39,7 @@ case class GeotrellisTileMatrix( val projectionMetersPerUnit: Map[CRS, Double] = Map( // meters per unit on equator - LatLng -> 6378137.0 * 2.0 * math.Pi / 360.0, + LatLng -> 6378137.0 * 2.0 * math.Pi / 360.0, WebMercator -> 1 ) @@ -47,7 +49,7 @@ case class GeotrellisTileMatrix( val scaleDenominator = layout.cellSize.width / 0.00028 * metersPerUnit TileMatrix( Title = title.map(LanguageStringType(_)).toList, - Abstract = `abstract`.map(LanguageStringType(_)).toList, + AbstractValue = `abstract`.map(LanguageStringType(_)).toList, Keywords = Nil, Identifier = CodeType(identifier), ScaleDenominator = scaleDenominator, diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrixSet.scala b/ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrixSet.scala index e5f4cc39..d04e208c 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrixSet.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrixSet.scala @@ -25,7 +25,9 @@ import opengis.wmts.TileMatrixSet import java.net.URI -/** A collection of tile matrices; most commonly forming a pyramid of different resolutions */ +/** + * A collection of tile matrices; most commonly forming a pyramid of different resolutions + */ case class GeotrellisTileMatrixSet( identifier: String, supportedCrs: CRS, @@ -38,7 +40,7 @@ case class GeotrellisTileMatrixSet( def toXml: TileMatrixSet = { val ret = TileMatrixSet( Title = title.map(LanguageStringType(_)).toList, - Abstract = `abstract`.map(LanguageStringType(_)).toList, + AbstractValue = `abstract`.map(LanguageStringType(_)).toList, Keywords = Nil, Identifier = CodeType(identifier), TileMatrix = tileMatrix.map(_.toXml(supportedCrs)), diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wmts/WmtsModel.scala b/ogc/src/main/scala/geotrellis/server/ogc/wmts/WmtsModel.scala index 25a7826f..8214f9b7 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wmts/WmtsModel.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wmts/WmtsModel.scala @@ -28,7 +28,9 @@ import cats.syntax.functor._ import cats.syntax.apply._ import cats.instances.option._ -/** This class holds all the information necessary to construct a response to a WMTS request */ +/** + * This class holds all the information necessary to construct a response to a WMTS request + */ case class WmtsModel[F[_]: Monad]( serviceMetadata: ows.ServiceMetadata, matrices: List[GeotrellisTileMatrixSet], @@ -45,9 +47,9 @@ case class WmtsModel[F[_]: Monad]( ( getMatrixCrs(p.tileMatrixSet), getMatrixLayoutDefinition(p.tileMatrixSet, p.tileMatrix) - ) traverseN { (crs, layout) => + ).traverseN { (crs, layout) => sources.find(p.toQuery).map { sources => - sources map { source => + sources.map { source => val style: Option[OgcStyle] = source.styles.find(_.name == p.style) source match { @@ -65,12 +67,12 @@ case class WmtsModel[F[_]: Monad]( } } } - } map { _ getOrElse Nil } + }.map { _.getOrElse(Nil) } def getMatrixLayoutDefinition(tileMatrixSetId: String, tileMatrixId: String): Option[LayoutDefinition] = for { matrixSet <- matrixSetLookup.get(tileMatrixSetId) - matrix <- matrixSet.tileMatrix.find(_.identifier == tileMatrixId) + matrix <- matrixSet.tileMatrix.find(_.identifier == tileMatrixId) } yield matrix.layout def getMatrixCrs(tileMatrixSetId: String): Option[CRS] = diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wmts/WmtsParams.scala b/ogc/src/main/scala/geotrellis/server/ogc/wmts/WmtsParams.scala index c01f6ca5..8dff88fd 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wmts/WmtsParams.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wmts/WmtsParams.scala @@ -66,35 +66,36 @@ object WmtsParams { val versionParam = params.validatedVersion(wmtsVersion) versionParam - .andThen { version: String => - val layer = params.validatedParam("layer") - val style = params.validatedParam("style") - val tileMatrixSet = params.validatedParam("tilematrixset") - val tileMatrix = params.validatedParam("tilematrix") - val tileRow = params.validatedParam[Int]("tilerow", s => Try(s.toInt).toOption) - val tileCol = params.validatedParam[Int]("tilecol", s => Try(s.toInt).toOption) - - val format = - params - .validatedParam("format") - .andThen { f => - OutputFormat.fromString(f) match { - case Some(format) => Valid(format).toValidatedNel - case None => - Invalid(ParamError.UnsupportedFormatError(f)).toValidatedNel + .andThen { + version: String => + val layer = params.validatedParam("layer") + val style = params.validatedParam("style") + val tileMatrixSet = params.validatedParam("tilematrixset") + val tileMatrix = params.validatedParam("tilematrix") + val tileRow = params.validatedParam[Int]("tilerow", s => Try(s.toInt).toOption) + val tileCol = params.validatedParam[Int]("tilecol", s => Try(s.toInt).toOption) + + val format = + params + .validatedParam("format") + .andThen { f => + OutputFormat.fromString(f) match { + case Some(format) => Valid(format).toValidatedNel + case None => + Invalid(ParamError.UnsupportedFormatError(f)).toValidatedNel + } } - } - (layer, style, tileMatrixSet, tileMatrix, format, tileRow, tileCol).mapN { - case (layer, style, tileMatrixSet, tileMatrix, format, tileRow, tileCol) => - GetTile(version, layer, style, format, tileMatrixSet, tileMatrix, tileRow, tileCol) - } + (layer, style, tileMatrixSet, tileMatrix, format, tileRow, tileCol).mapN { + case (layer, style, tileMatrixSet, tileMatrix, format, tileRow, tileCol) => + GetTile(version, layer, style, format, tileMatrixSet, tileMatrix, tileRow, tileCol) + } } } } def apply(queryParams: Map[String, Seq[String]]): ValidatedNel[ParamError, WmtsParams] = { - val params = ParamMap(queryParams) + val params = ParamMap(queryParams) val serviceParam = params.validatedParam("service", validValues = Set("wmts")) val requestParam = params.validatedParam("request", validValues = Set("getcapabilities", "gettile")) diff --git a/ogc/src/main/scala/geotrellis/server/ogc/wmts/package.scala b/ogc/src/main/scala/geotrellis/server/ogc/wmts/package.scala index 17d6f75f..92ce66f5 100644 --- a/ogc/src/main/scala/geotrellis/server/ogc/wmts/package.scala +++ b/ogc/src/main/scala/geotrellis/server/ogc/wmts/package.scala @@ -20,10 +20,10 @@ import scala.xml.NamespaceBinding package object wmts { val wmtsScope: NamespaceBinding = scalaxb.toScope( - None -> "http://www.opengis.net/wmts/1.0", - Some("gml") -> "http://www.opengis.net/gml", - Some("ows") -> "http://www.opengis.net/ows/1.1", + None -> "http://www.opengis.net/wmts/1.0", + Some("gml") -> "http://www.opengis.net/gml", + Some("ows") -> "http://www.opengis.net/ows/1.1", Some("xlink") -> "http://www.w3.org/1999/xlink", - Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" + Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" ) } diff --git a/ogc/src/test/scala/geotrellis/server/ogc/InterpolatedColorMapSpec.scala b/ogc/src/test/scala/geotrellis/server/ogc/InterpolatedColorMapSpec.scala index b0104206..b6157a3a 100644 --- a/ogc/src/test/scala/geotrellis/server/ogc/InterpolatedColorMapSpec.scala +++ b/ogc/src/test/scala/geotrellis/server/ogc/InterpolatedColorMapSpec.scala @@ -25,22 +25,22 @@ import org.scalatest.matchers.should.Matchers class InterpolatedColorMapSpec extends AnyFunSpec with Matchers { describe("InterpolatedColorMap") { - val minColor = RGBA.fromRGBA(255, 0, 0, 100).int - val medColor = RGBA.fromRGBA(0, 255, 0, 100).int - val maxColor = RGBA.fromRGBA(0, 0, 255, 100).int + val minColor = RGBA.fromRGBA(255, 0, 0, 100).int + val medColor = RGBA.fromRGBA(0, 255, 0, 100).int + val maxColor = RGBA.fromRGBA(0, 0, 255, 100).int val clippedColor = RGBA.fromRGBA(0, 0, 0, 0).int val poles = Map[Double, Int]( -100.0 -> minColor, - 0.0 -> medColor, - 100.0 -> maxColor + 0.0 -> medColor, + 100.0 -> maxColor ) it("should interpolate based based on the two nearest poles") { val clipDefinition = ClipNone - val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) - val interpolated = RGBA(interpolate(50.0)) - val expected = RGBA.fromRGBA(0, 127, 127, 100) + val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) + val interpolated = RGBA(interpolate(50.0)) + val expected = RGBA.fromRGBA(0, 127, 127, 100) interpolated shouldBe expected interpolated.red shouldBe expected.red @@ -50,29 +50,29 @@ class InterpolatedColorMapSpec extends AnyFunSpec with Matchers { it("should respect clip definition: none") { val clipDefinition = ClipNone - val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) - val i = interpolate(Double.MinValue) + val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) + val i = interpolate(Double.MinValue) interpolate(Double.MinValue) shouldBe minColor interpolate(Double.MaxValue) shouldBe maxColor } it("should respect clip definition: left") { val clipDefinition = ClipLeft - val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) + val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) interpolate(Double.MinValue) shouldBe clippedColor interpolate(Double.MaxValue) shouldBe maxColor } it("should respect clip definition: right") { val clipDefinition = ClipRight - val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) + val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) interpolate(Double.MinValue) shouldBe minColor interpolate(Double.MaxValue) shouldBe clippedColor } it("should respect clip definition: both") { val clipDefinition = ClipBoth - val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) + val interpolate = InterpolatedColorMap.interpolation(poles, clipDefinition) interpolate(Double.MinValue) shouldBe clippedColor interpolate(Double.MaxValue) shouldBe clippedColor } diff --git a/ogc/src/test/scala/geotrellis/server/ogc/OgcStyleSpec.scala b/ogc/src/test/scala/geotrellis/server/ogc/OgcStyleSpec.scala index 80ec19e6..4587de9d 100644 --- a/ogc/src/test/scala/geotrellis/server/ogc/OgcStyleSpec.scala +++ b/ogc/src/test/scala/geotrellis/server/ogc/OgcStyleSpec.scala @@ -28,12 +28,12 @@ class OgcStyleSpec extends AnyFunSpec with Matchers { describe("ColorRampStyle") { it("should interpolate breaks") { - val minimum = -10 - val maximum = 90 + val minimum = -10 + val maximum = 90 val desiredBreaks = 50 - val ramp = ColorRamp(Array(0xff0000, 0x0000ff)) - val style = ColorRampStyle("test", "title", ramp, Some(20), Some(minimum), Some(maximum)) - val breaks = style.breaks(List(DoubleHistogram()), desiredBreaks) + val ramp = ColorRamp(Array(0xff0000, 0x0000ff)) + val style = ColorRampStyle("test", "title", ramp, Some(20), Some(minimum), Some(maximum)) + val breaks = style.breaks(List(DoubleHistogram()), desiredBreaks) breaks.length shouldBe desiredBreaks breaks.head shouldBe -10 breaks.reverse.head shouldBe 90 diff --git a/project/Dependencies.scala b/project/Dependencies.scala index aa501135..7b8132a3 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,29 +3,45 @@ import sbt.Keys._ object Dependencies { - def catsVersion(module: String) = - Def.setting { - module match { - case "core" => "org.typelevel" %% s"cats-$module" % "2.7.0" - case "effect" => "org.typelevel" %% s"cats-$module" % "2.5.4" - } + def catsVersion(module: String) = Def.setting { + val version = module match { + case "core" => "2.10.0" + case "effect" => "3.5.4" + case "tagless" => "0.16.0" } - def circeVersion(module: String) = Def.setting("io.circe" %% s"circe-$module" % "0.14.1") - def http4sVer(module: String) = Def.setting("org.http4s" %% s"http4s-$module" % "0.21.31") + "org.typelevel" %% s"cats-$module" % version + } - val crossScalaVer = List("2.12.15") + def circeVersion(module: String) = Def.setting { + val version = module match { + case "generic-extras" => "0.14.3" + case _ => "0.14.7" + } + + "io.circe" %% s"circe-$module" % version + } + def http4sVer(module: String) = Def.setting { + val version = module match { + case "scala-xml" => "0.23.13" + case _ => "0.23.16" + } + "org.http4s" %% s"http4s-$module" % version + } + + val crossScalaVer = List("2.13.14", "2.12.19") val scalaVer = crossScalaVer.head val dispatchVer = "0.11.3" - val gtVer = "3.6.0" - val stac4sVer = "0.6.2" + val gtVer = "3.7.1" + val stac4sVer = "0.9.0" val jaxbApiVer = "2.3.1" - val refinedVer = "0.9.23" + val refinedVer = "0.11.1" val shapelessVer = "2.3.3" val cats = catsVersion("core") val catsEffect = catsVersion("effect") + val catsTagless = catsVersion("tagless") val circeCore = circeVersion("core") val circeShapes = circeVersion("shapes") val circeGeneric = circeVersion("generic") @@ -34,9 +50,9 @@ object Dependencies { val circeParser = circeVersion("parser") val circeRefined = circeVersion("refined") val circeJava8 = circeVersion("java8") - val commonsIO = "commons-io" % "commons-io" % "2.11.0" + val commonsIO = "commons-io" % "commons-io" % "2.16.1" val concHashMap = "com.googlecode.concurrentlinkedhashmap" % "concurrentlinkedhashmap-lru" % "1.4.2" - val decline = "com.monovore" %% "decline" % "1.4.0" + val decline = "com.monovore" %% "decline" % "2.4.1" val geotrellisRaster = "org.locationtech.geotrellis" %% "geotrellis-raster" % gtVer val geotrellisLayer = "org.locationtech.geotrellis" %% "geotrellis-layer" % gtVer val geotrellisVector = "org.locationtech.geotrellis" %% "geotrellis-vector" % gtVer @@ -52,29 +68,29 @@ object Dependencies { val http4sDsl = http4sVer("dsl") val http4sXml = http4sVer("scala-xml") val jaxbApi = "javax.xml.bind" % "jaxb-api" % jaxbApiVer - val kindProjector = "org.typelevel" %% "kind-projector" % "0.13.2" - val semanticdbScalac = "org.scalameta" % "semanticdb-scalac" % "4.4.31" - val log4cats = "io.chrisdavenport" %% "log4cats-slf4j" % "1.1.1" - val mamlJvm = "com.azavea.geotrellis" %% "maml-jvm" % "0.6.1" - val pureConfig = "com.github.pureconfig" %% "pureconfig" % "0.14.1" - val pureConfigCatsEffect = "com.github.pureconfig" %% "pureconfig-cats-effect" % "0.14.1" - val scaffeine = "com.github.blemale" %% "scaffeine" % "5.1.1" - val scalaXml = "org.scala-lang.modules" %% "scala-xml" % "1.3.0" - val scalatest = "org.scalatest" %% "scalatest" % "3.2.10" % Test + val kindProjector = "org.typelevel" %% "kind-projector" % "0.13.3" + val semanticdbScalac = "org.scalameta" % "semanticdb-scalac" % "4.9.3" + val log4cats = "org.typelevel" %% "log4cats-slf4j" % "2.7.0" + val mamlJvm = "com.azavea.geotrellis" %% "maml-jvm" % "0.7.0+1-a6158955-SNAPSHOT" + val pureConfig = "com.github.pureconfig" %% "pureconfig" % "0.17.6" + val pureConfigCatsEffect = "com.github.pureconfig" %% "pureconfig-cats-effect" % "0.17.6" + val scaffeine = "com.github.blemale" %% "scaffeine" % "5.2.1" + val scalaXml = "org.scala-lang.modules" %% "scala-xml" % "2.3.0" + val scalatest = "org.scalatest" %% "scalatest" % "3.2.18" % Test val scalacheck = "org.scalacheck" %% "scalacheck" % "1.14.0" % Test val scalacheckCats = "io.chrisdavenport" %% "cats-scalacheck" % "0.1.1" % Test - val sttpHttp4s = "com.softwaremill.sttp.client3" %% "http4s-ce2-backend" % "3.3.9" + val sttpHttp4s = "com.softwaremill.sttp.client3" %% "http4s-backend" % "3.9.6" val macrosParadise = "org.scalamacros" % "paradise" % "2.1.1" - val scalaParser = "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2" + val scalaParser = "org.scala-lang.modules" %% "scala-parser-combinators" % "2.4.0" val shapeless = "com.chuusai" %% "shapeless" % shapelessVer - val logback = "ch.qos.logback" % "logback-classic" % "1.2.7" % Runtime - val droste = "io.higherkindness" %% "droste-core" % "0.8.0" + val logback = "ch.qos.logback" % "logback-classic" % "1.5.6" % Runtime + val droste = "io.higherkindness" %% "droste-core" % "0.9.0" val stac4sCore = "com.azavea.stac4s" %% "core" % stac4sVer val stac4sClient = "com.azavea.stac4s" %% "client" % stac4sVer val refinedCats = "eu.timepit" %% "refined-cats" % refinedVer val refinedPureconfig = "eu.timepit" %% "refined-pureconfig" % refinedVer - val threetenExtra = "org.threeten" % "threeten-extra" % "1.7.0" + val threetenExtra = "org.threeten" % "threeten-extra" % "1.8.0" val ansiColors212 = "org.backuity" %% "ansi-interpolator" % "1.1.0" % Provided - val tofuCore = "tf.tofu" %% "tofu-core" % "0.10.3" - val azureStorage = "com.azure" % "azure-storage-blob" % "12.14.2" + val tofuCore = "tf.tofu" %% "tofu-core-ce3" % "0.13.0" + val azureStorage = "com.azure" % "azure-storage-blob" % "12.25.4" } diff --git a/project/build.properties b/project/build.properties index bb3a9b7d..081fdbbc 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.5.6 +sbt.version=1.10.0 diff --git a/project/plugins.sbt b/project/plugins.sbt index 03226f58..3495f5b4 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,13 +1,12 @@ addDependencyTreePlugin -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.0.0") -addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.3") + +addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0") +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.2") addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.3") -addSbtPlugin("org.scalaxb" % "sbt-scalaxb" % "1.8.2") -addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.5.0") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.5") -addSbtPlugin("io.crashbox" % "sbt-gpg" % "0.2.0") -addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.2") -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.6.0") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.5") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.33") +addSbtPlugin("org.scalaxb" % "sbt-scalaxb" % "1.12.0") +addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.11.0") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") diff --git a/sbt b/sbt index 4ee84afc..43a52e83 100755 --- a/sbt +++ b/sbt @@ -34,11 +34,11 @@ set -o pipefail -declare -r sbt_release_version="1.5.6" -declare -r sbt_unreleased_version="1.6.0-M1" +declare -r sbt_release_version="1.10.0" +declare -r sbt_unreleased_version="1.10.0" -declare -r latest_213="2.13.7" -declare -r latest_212="2.12.15" +declare -r latest_213="2.13.14" +declare -r latest_212="2.12.19" declare -r latest_211="2.11.12" declare -r latest_210="2.10.7" declare -r latest_29="2.9.3" diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/FocalParameters.scala b/stac-example/src/main/scala/geotrellis/server/ogc/FocalParameters.scala index 2b07e0eb..ea54656e 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/FocalParameters.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/FocalParameters.scala @@ -52,9 +52,9 @@ case class FocalParameters(azimuth: Option[Double], altitude: Option[Double], zF object FocalParameters { def fromParams(params: ParamMap): Validated[NonEmptyList[ParamError], Option[FocalParameters]] = { - val azimuth: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("azimuth") + val azimuth: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("azimuth") val altitude: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("altitude") - val zFactor: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("zfactor") + val zFactor: Validated[NonEmptyList[ParamError], Option[Double]] = params.validatedOptionalParamDouble("zfactor") val target: Validated[NonEmptyList[ParamError], Option[TargetCell]] = params .validatedOptionalParam("target") .andThen { f => diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/Main.scala b/stac-example/src/main/scala/geotrellis/server/ogc/Main.scala index 019acf5f..cb09c77a 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/Main.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/Main.scala @@ -20,7 +20,6 @@ import geotrellis.server.ogc.conf._ import geotrellis.server.ogc.wms._ import geotrellis.server.ogc.wcs._ import geotrellis.server.ogc.wmts._ -import geotrellis.store.util.BlockingThreadPool import cats.effect._ import cats.implicits._ import com.monovore.decline._ @@ -31,14 +30,15 @@ import org.http4s.server.middleware.CORS import org.http4s.syntax.kleisli._ import org.backuity.ansi.AnsiFormatter.FormattedHelper import com.google.common.util.concurrent.ThreadFactoryBuilder -import io.chrisdavenport.log4cats.Logger -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger +import org.typelevel.log4cats.Logger +import org.typelevel.log4cats.slf4j.Slf4jLogger import sttp.client3.http4s.Http4sBackend import scala.concurrent.duration._ import scala.concurrent.ExecutionContext import java.net.URL import java.util.concurrent.Executors +import cats.effect.unsafe.implicits.global object Main extends CommandApp( @@ -89,7 +89,9 @@ object Main case None => loggerSync.info(ansi"%red{Warning}: No configuration path provided. Loading defaults.") } - /** Server endpoints pool. */ + /** + * Server endpoints pool. + */ implicit val executionContext: ExecutionContext = ExecutionContext.fromExecutor( Executors.newCachedThreadPool( @@ -99,21 +101,21 @@ object Main ) ) - implicit val cs: ContextShift[IO] = IO.contextShift(executionContext) - implicit val timer: Timer[IO] = IO.timer(executionContext) - val commonMiddleware: HttpMiddleware[IO] = { (routes: HttpRoutes[IO]) => CORS(routes) } def logOptState[A](opt: Option[A], upLog: String, downLog: String): IO[Unit] = opt.fold(logger.info(downLog))(_ => logger.info(upLog)) - def createServer: Resource[IO, Server[IO]] = + def createServer: Resource[IO, Server] = for { conf <- Conf.loadResourceF[IO](configPath) - /** MosaicRasterSources pool init for the graceful shutdown. */ - // blockingPool <- Resource.make(IO.delay(BlockingThreadPool.pool))(p => IO.delay(p.shutdown())) - /** Uses server pool. */ - http4sClient <- Http4sBackend.usingDefaultBlazeClientBuilder[IO](Blocker.liftExecutionContext(executionContext), executionContext) + /** + * MosaicRasterSources pool init for the graceful shutdown. + */ + /** + * Uses server pool. + */ + http4sClient <- Http4sBackend.usingDefaultBlazeClientBuilder[IO]() simpleSources = conf.layers.values.collect { case rsc: RasterSourceConf => rsc.toLayer }.toList _ <- Resource.eval( logOptState( diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/OgcService.scala b/stac-example/src/main/scala/geotrellis/server/ogc/OgcService.scala index 8c4c9cc1..5e72b0ce 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/OgcService.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/OgcService.scala @@ -24,12 +24,12 @@ import org.http4s._ import org.http4s.dsl.Http4sDsl import cats.Parallel import cats.effect._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import org.log4s.getLogger import java.net.URL -class OgcService[F[_]: Concurrent: Parallel: Logger]( +class OgcService[F[_]: Async: Parallel: Logger]( wmsModel: Option[WmsModel[F]], wcsModel: Option[WcsModel[F]], wmtsModel: Option[WmtsModel[F]], @@ -37,13 +37,13 @@ class OgcService[F[_]: Concurrent: Parallel: Logger]( ) extends Http4sDsl[F] { val logger = getLogger - val wcsView = wcsModel.map(new WcsView(_, serviceUrl)) - val wmsView = wmsModel.map(new WmsView(_, serviceUrl)) + val wcsView = wcsModel.map(new WcsView(_, serviceUrl)) + val wmsView = wmsModel.map(new WmsView(_, serviceUrl)) val wmtsView = wmtsModel.map(new WmtsView(_, serviceUrl)) // Predicates for choosing a service - def isWcsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wcs" - def isWmsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wms" + def isWcsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wcs" + def isWmsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wms" def isWmtsReq(key: String, value: String) = key.toLowerCase == "service" && value.toLowerCase == "wmts" def isWmsReqGetFeatureInfoExtended(key: String, value: String) = key.toLowerCase == "request" && value.toLowerCase == "getfeatureinfoextended" diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/RGBParameters.scala b/stac-example/src/main/scala/geotrellis/server/ogc/RGBParameters.scala index d04ad829..56de4f82 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/RGBParameters.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/RGBParameters.scala @@ -44,19 +44,13 @@ case class RGBParameters( case r :: g :: b :: Nil => val id: Expression => Expression = identity val rmap = - clampRed.map(_.bind).getOrElse(id) andThen - normalizeRed.map(_.bind).getOrElse(id) andThen - rescaleRed.map(_.bind).getOrElse(id) + clampRed.map(_.bind).getOrElse(id).andThen(normalizeRed.map(_.bind).getOrElse(id)).andThen(rescaleRed.map(_.bind).getOrElse(id)) val gmap = - clampGreen.map(_.bind).getOrElse(id) andThen - normalizeGreen.map(_.bind).getOrElse(id) andThen - rescaleGreen.map(_.bind).getOrElse(id) + clampGreen.map(_.bind).getOrElse(id).andThen(normalizeGreen.map(_.bind).getOrElse(id)).andThen(rescaleGreen.map(_.bind).getOrElse(id)) val bmap = - clampBlue.map(_.bind).getOrElse(id) andThen - normalizeBlue.map(_.bind).getOrElse(id) andThen - rescaleBlue.map(_.bind).getOrElse(id) + clampBlue.map(_.bind).getOrElse(id).andThen(normalizeBlue.map(_.bind).getOrElse(id)).andThen(rescaleBlue.map(_.bind).getOrElse(id)) e.copy(children = rmap(r) :: gmap(g) :: bmap(b) :: Nil) case _ => e diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/StacOgcSource.scala b/stac-example/src/main/scala/geotrellis/server/ogc/StacOgcSource.scala index 564a8e55..94f6955e 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/StacOgcSource.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/StacOgcSource.scala @@ -61,7 +61,7 @@ case class StacOgcSource( override def extentIn(crs: CRS): Extent = projectedExtent.reproject(crs).extent - override def projectedExtent: ProjectedExtent = stacSource.projectedExtent + override def projectedExtent: ProjectedExtent = stacSource.projectedExtent override lazy val attributes: Map[String, String] = stacSource.attributes def toLayer(crs: CRS, style: Option[OgcStyle], temporalSequence: List[OgcTime]): SimpleOgcLayer = diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala b/stac-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala index f5d11df3..a15a60a2 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala @@ -26,7 +26,7 @@ import geotrellis.server.ogc.wms.WmsParentLayerMeta import geotrellis.server.ogc.wmts.GeotrellisTileMatrixSet import geotrellis.server.ogc.stac._ import geotrellis.store.query.{Repository, RepositoryM} -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import sttp.client3.SttpBackend /** @@ -36,7 +36,7 @@ import sttp.client3.SttpBackend sealed trait OgcServiceConf { def layerDefinitions: List[OgcSourceConf] def layerSources(rasterOgcSources: List[RasterOgcSource]): Repository[OgcSource] = { - val rasterLayers = layerDefinitions.collect { case rsc: RasterSourceConf => rsc.toLayer } + val rasterLayers = layerDefinitions.collect { case rsc: RasterSourceConf => rsc.toLayer } val mapAlgebraLayers = layerDefinitions.collect { case masc: MapAlgebraSourceConf => masc.modelOpt(rasterOgcSources) }.flatten OgcSourceRepository(rasterLayers ++ mapAlgebraLayers) @@ -46,8 +46,8 @@ sealed trait OgcServiceConf { rasterOgcSources: List[RasterOgcSource], client: SttpBackend[F, Any] ): RepositoryM[F, List, OgcSource] = { - val ogcLayers = layerDefinitions.collect { case osc: OgcSourceConf => osc } - val stacLayers = ogcLayers.collect { case ssc: StacSourceConf => ssc } + val ogcLayers = layerDefinitions.collect { case osc: OgcSourceConf => osc } + val stacLayers = ogcLayers.collect { case ssc: StacSourceConf => ssc } val mapAlgebraConfLayers = ogcLayers.collect { case masc: MapAlgebraSourceConf => masc } layerSources(rasterOgcSources).toF[F] |+| @@ -56,21 +56,27 @@ sealed trait OgcServiceConf { } } -/** WMS Service configuration */ +/** + * WMS Service configuration + */ case class WmsConf( parentLayerMeta: WmsParentLayerMeta, serviceMetadata: opengis.wms.Service, layerDefinitions: List[OgcSourceConf] ) extends OgcServiceConf -/** WMTS Service configuration */ +/** + * WMTS Service configuration + */ case class WmtsConf( serviceMetadata: ows.ServiceMetadata, layerDefinitions: List[OgcSourceConf], tileMatrixSets: List[GeotrellisTileMatrixSet] ) extends OgcServiceConf -/** WCS Service configuration */ +/** + * WCS Service configuration + */ case class WcsConf( serviceMetadata: ows.ServiceMetadata, layerDefinitions: List[OgcSourceConf], diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/conf/OgcSourceConf.scala b/stac-example/src/main/scala/geotrellis/server/ogc/conf/OgcSourceConf.scala index 5120123d..ded80645 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/conf/OgcSourceConf.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/conf/OgcSourceConf.scala @@ -64,10 +64,14 @@ case class StacSourceConf( parallelMosaic: Boolean = false ) extends OgcSourceConf { - /** flag used to convert https azure URI into wasbs:// URIs to smooth GDAL integration */ + /** + * flag used to convert https azure URI into wasbs:// URIs to smooth GDAL integration + */ val toWASBS: Boolean = withGDAL && withVSIAZ - /** By default the search would happen across collections. */ + /** + * By default the search would happen across collections. + */ def searchCriteria: StacSearchCriteria = (collection, layer) match { case (None, Some(_)) => ByLayer @@ -102,7 +106,7 @@ case class StacSourceConf( object StacSourceConf { implicit val crsReader: ConfigReader[CRS] = ConfigReader[String].map { str => - Try(CRS.fromName(str)).toOption orElse Try(CRS.fromString(str)).toOption match { + Try(CRS.fromName(str)).toOption.orElse(Try(CRS.fromString(str)).toOption) match { case Some(crs) => crs case None => throw new Exception(s"Invalid Proj4 String: $str") } diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala b/stac-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala index cc1dea1e..ddc03a9b 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala @@ -20,14 +20,18 @@ import geotrellis.server.ogc.style._ import geotrellis.raster.render.{ColorMap, ColorRamp} -/** The trait implemented by different style configuration options */ +/** + * The trait implemented by different style configuration options + */ sealed trait StyleConf { def name: String def title: String def toStyle: OgcStyle } -/** Styling in which a color scheme is known but not the values that these colors should map to */ +/** + * Styling in which a color scheme is known but not the values that these colors should map to + */ final case class ColorRampConf( name: String, title: String, @@ -42,7 +46,9 @@ final case class ColorRampConf( ColorRampStyle(name, title, colors, stops, minRender, maxRender, clampWithColor, legends) } -/** Styling in which both a color scheme and the data-to-color mapping is known */ +/** + * Styling in which both a color scheme and the data-to-color mapping is known + */ final case class ColorMapConf( name: String, title: String, diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/conf/package.scala b/stac-example/src/main/scala/geotrellis/server/ogc/conf/package.scala index 1243d4f0..61e797c5 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/conf/package.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/conf/package.scala @@ -38,7 +38,9 @@ import pureconfig.generic.auto._ import scala.util.{Success, Try} import scala.collection.JavaConverters._ -/** A grab bag of [[ConfigReader]] instances necessary to read the configuration */ +/** + * A grab bag of [[ConfigReader]] instances necessary to read the configuration + */ package object conf { /** @@ -53,7 +55,7 @@ package object conf { implicit val circeJsonReader: ConfigReader[Json] = ConfigReader[ConfigValue].emap { cv => val renderOptions = ConfigRenderOptions.concise().setJson(true) - val jsonString = cv.render(renderOptions) + val jsonString = cv.render(renderOptions) parse(jsonString) match { case Left(parsingFailure) => Left(CannotConvert(jsonString, "json", parsingFailure.getMessage)) case Right(json) => Right(json) @@ -89,7 +91,7 @@ package object conf { case ConfigValueType.OBJECT => val confmap = v.asInstanceOf[ConfigObject].asScala confmap.map { case (ck, cv) => - val key = k + "." + ck + val key = k + "." + ck val value = cv.unwrapped.asInstanceOf[String] key -> value } @@ -100,7 +102,7 @@ package object conf { } } .map { case (k, v) => - val key = k.toDouble + val key = k.toDouble val value = java.lang.Long.decode(v).toInt key -> value } @@ -176,7 +178,7 @@ package object conf { 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 parseAuto(str: String): OverviewStrategy = parse("auto", str) def parseLevel(str: String): OverviewStrategy = parse("level", str) ConfigReader[String].map { diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/stac/MapAlgebraStacOgcRepositories.scala b/stac-example/src/main/scala/geotrellis/server/ogc/stac/MapAlgebraStacOgcRepositories.scala index 634677f3..fe8675c2 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/stac/MapAlgebraStacOgcRepositories.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/stac/MapAlgebraStacOgcRepositories.scala @@ -21,13 +21,12 @@ import geotrellis.server.ogc.conf.{MapAlgebraSourceConf, OgcSourceConf, RasterSo import geotrellis.store.query import geotrellis.store.query._ import geotrellis.server.ogc._ - import cats.effect.Sync import cats.Functor import cats.syntax.functor._ import cats.syntax.semigroup._ import cats.instances.list._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import sttp.client3.SttpBackend case class MapAlgebraStacOgcRepository[F[_]: Functor]( @@ -40,7 +39,9 @@ case class MapAlgebraStacOgcRepository[F[_]: Functor]( def store: F[List[OgcSource]] = find(query.all) def find(query: Query): F[List[OgcSource]] = - /** Replace the OGC layer name with its STAC Layer name */ + /** + * Replace the OGC layer name with its STAC Layer name + */ repository .find(names.map(query.overrideName).fold(nothing)(_ or _)) .map(_.collect { case rs: RasterOgcSource => rs }) @@ -63,12 +64,16 @@ case class MapAlgebraStacOgcRepositories[F[_]: Sync: Logger]( StacOgcRepositories .eval(query)(mapAlgebraConfLayers) .map { conf => - /** Extract layerNames from the MAML expression */ + /** + * Extract layerNames from the MAML expression + */ val layerNames = conf.listParams(conf.algebra) - /** Get all ogc layers that are required for the MAML expression evaluation */ + /** + * Get all ogc layers that are required for the MAML expression evaluation + */ val ogcLayersFiltered = ogcLayers.filter(l => layerNames.contains(l.name)) - val stacLayers = ogcLayersFiltered.collect { case ssc: StacSourceConf => ssc } + val stacLayers = ogcLayersFiltered.collect { case ssc: StacSourceConf => ssc } val rasterLayers = if (stacLayers.nonEmpty) ogcLayersFiltered.collect { case ssc: RasterSourceConf => ssc.toLayer } else Nil diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/stac/SearchFiltersQuery.scala b/stac-example/src/main/scala/geotrellis/server/ogc/stac/SearchFiltersQuery.scala index 09c3d863..46176817 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/stac/SearchFiltersQuery.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/stac/SearchFiltersQuery.scala @@ -39,10 +39,12 @@ import eu.timepit.refined.cats._ import eu.timepit.refined.types.numeric.NonNegInt import eu.timepit.refined.types.string.NonEmptyString -/** SearchFilters Query evaluation */ +/** + * SearchFilters Query evaluation + */ object SearchFiltersQuery { // overcome diverging implicit expansion - implicit private val nonNegIntOrder: Order[NonNegInt] = refTypeOrder + implicit private val nonNegIntOrder: Order[NonNegInt] = refTypeOrder implicit private val nonNegIntOrdering: Ordering[NonNegInt] = nonNegIntOrder.toOrdering object IntersectionSemigroup { @@ -75,8 +77,8 @@ object SearchFiltersQuery { collections = (left.collections |+| right.collections).distinct, items = (left.items |+| right.items).distinct, limit = List(left.limit, right.limit).min, - next = right.next, - query = left.query.deepMerge(right.query) + query = left.query.deepMerge(right.query), + paginationBody = right.paginationBody ) } } @@ -111,8 +113,8 @@ object SearchFiltersQuery { collections = (left.collections |+| right.collections).distinct, items = (left.items |+| right.items).distinct, limit = List(left.limit, right.limit).min, - next = right.next, - query = left.query.deepMerge(right.query) + query = left.query.deepMerge(right.query), + paginationBody = right.paginationBody ) } } @@ -138,7 +140,7 @@ object SearchFiltersQuery { case Intersects(e) => SearchFilters(intersects = e.reproject(LatLng).geometry.some).some case Covers(e) => SearchFilters(bbox = e.reproject(LatLng).geometry.extent.toTwoDimBbox.some).some case And(l, r) => import IntersectionSemigroup._; l |+| r - case Or(l, r) => import UnionSemigroup._; l |+| r + case Or(l, r) => import UnionSemigroup._; l |+| r // unsupported nodes case _ => SearchFilters().some } diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacOgcRepositories.scala b/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacOgcRepositories.scala index ccc50a0a..63a040af 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacOgcRepositories.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacOgcRepositories.scala @@ -26,12 +26,12 @@ import geotrellis.raster.{EmptyName, MosaicRasterSource, RasterSource} import geotrellis.server.ogc.OgcSource import geotrellis.server.ogc.conf.{OgcSourceConf, StacSourceConf} import geotrellis.raster.effects.MosaicRasterSourceIO - import sttp.client3.SttpBackend import sttp.client3.UriContext import com.azavea.stac4s.api.client.{Query => _, _} import cats.data.NonEmptyList import cats.effect.Sync +import cats.effect.unsafe.IORuntime import cats.syntax.applicative._ import cats.syntax.apply._ import cats.syntax.option._ @@ -39,7 +39,9 @@ import cats.syntax.semigroup._ import cats.instances.list._ import higherkindness.droste.{scheme, Algebra} -/** Sync is required to compile [[fs2.Stream]] */ +/** + * Sync is required to compile [[fs2.Stream]] + */ case class StacOgcRepository[F[_]: Sync]( stacSourceConf: StacSourceConf, client: StreamingStacClientFS2[F] @@ -48,16 +50,20 @@ case class StacOgcRepository[F[_]: Sync]( def find(query: Query): F[List[OgcSource]] = { - /** Replace the actual conf name with the STAC Layer name. */ + /** + * Replace the actual conf name with the STAC Layer name. + */ val filters: Option[SearchFilters] = SearchFilters .eval(stacSourceConf.searchCriteria)(query.overrideName(stacSourceConf.searchName)) .map(_.copy(limit = stacSourceConf.pageLimit)) filters.fold(List.empty[OgcSource].pure[F]) { filter => - /** Query summary i.e. collection or layer summary and items and perform the matching items search. */ + /** + * Query summary i.e. collection or layer summary and items and perform the matching items search. + */ val summary = client.summary(stacSourceConf.searchName, stacSourceConf.searchCriteria) - val items = stacSourceConf.assetLimit.fold(client.search(filter))(limit => client.search(filter).take(limit.value)) + val items = stacSourceConf.assetLimit.fold(client.search(filter))(limit => client.search(filter).take(limit.value)) (summary, items.compile.toList) .mapN { case (summary, items) => val rasterSources = @@ -74,21 +80,27 @@ case class StacOgcRepository[F[_]: Sync]( val source: Option[StacCollectionSource] = rasterSources match { case head :: Nil => StacCollectionSource(csummary.asset, head).some case head :: _ => - /** Extra temporal layers filtering (slicing). If the layer is not temporal, no extra filtering (slicing) would be applied. */ + /** + * Extra temporal layers filtering (slicing). If the layer is not temporal, no extra filtering (slicing) would be applied. + */ val sources = rasterSources.timeSlice(query, stacSourceConf.timeDefault, stacSourceConf.ignoreTime, stacSourceConf.datetimeField.some) - val commonCrs = if (sources.flatMap(_.asset.crs).distinct.size == 1) head.crs else stacSourceConf.commonCrs + val commonCrs = if (sources.flatMap(_.asset.crs).distinct.size == 1) head.crs else stacSourceConf.commonCrs val reprojectedSources = sources.map(_.reproject(commonCrs)) - val attributes = reprojectedSources.attributesByName + val attributes = reprojectedSources.attributesByName // TODO: Fix the unsafe behavior, requires refactor of all repos and all RasterSources usages val mosaicRasterSource = if (stacSourceConf.parallelMosaic) - MosaicRasterSourceIO.instance(NonEmptyList.fromListUnsafe(reprojectedSources), commonCrs, csummary.sourceName, attributes) + MosaicRasterSourceIO.instance(NonEmptyList.fromListUnsafe(reprojectedSources), commonCrs, csummary.sourceName, attributes)( + IORuntime.global + ) else MosaicRasterSource.instance(NonEmptyList.fromListUnsafe(reprojectedSources), commonCrs, csummary.sourceName, attributes) - /** In case some of the RasterSources are not from the STAC collection, we'd need to expand the [[StacCollectionSource]] extent. */ + /** + * In case some of the RasterSources are not from the STAC collection, we'd need to expand the [[StacCollectionSource]] extent. + */ StacCollectionSource(csummary.asset.expandExtentToInclude(mosaicRasterSource.extent), mosaicRasterSource).some case _ => None } @@ -98,16 +110,20 @@ case class StacOgcRepository[F[_]: Sync]( val source: Option[RasterSource] = rasterSources match { case head :: Nil => head.some case head :: _ => - /** Extra temporal layers filtering (slicing). If the layer is not temporal, no extra filtering (slicing) would be applied. */ + /** + * Extra temporal layers filtering (slicing). If the layer is not temporal, no extra filtering (slicing) would be applied. + */ val sources = rasterSources.timeSlice(query, stacSourceConf.timeDefault, stacSourceConf.ignoreTime, stacSourceConf.datetimeField.some) - val commonCrs = if (sources.flatMap(_.asset.crs).distinct.size == 1) head.crs else stacSourceConf.commonCrs + val commonCrs = if (sources.flatMap(_.asset.crs).distinct.size == 1) head.crs else stacSourceConf.commonCrs val reprojectedSources = sources.map(_.reproject(commonCrs)) - val attributes = reprojectedSources.attributesByName + val attributes = reprojectedSources.attributesByName // TODO: Fix the unsafe behavior, requires refactor of all repos and all RasterSources usages if (stacSourceConf.parallelMosaic) - MosaicRasterSourceIO.instance(NonEmptyList.fromListUnsafe(reprojectedSources), commonCrs, EmptyName, attributes).some + MosaicRasterSourceIO + .instance(NonEmptyList.fromListUnsafe(reprojectedSources), commonCrs, EmptyName, attributes)(IORuntime.global) + .some else MosaicRasterSource.instance(NonEmptyList.fromListUnsafe(reprojectedSources), commonCrs, EmptyName, attributes).some case _ => None @@ -146,7 +162,7 @@ object StacOgcRepositories { case WithNames(names) => _.filter(c => names.contains(c.name)) case And(e1, e2) => list => - val left = e1(list); left intersect e2(left) + val left = e1(list); left.intersect(e2(left)) case Or(e1, e2) => list => e1(list) ++ e2(list) case _ => identity } diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSearchCriteria.scala b/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSearchCriteria.scala index 0fc122dc..5eb587b4 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSearchCriteria.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSearchCriteria.scala @@ -17,5 +17,5 @@ package geotrellis.server.ogc.stac sealed trait StacSearchCriteria -case object ByLayer extends StacSearchCriteria +case object ByLayer extends StacSearchCriteria case object ByCollection extends StacSearchCriteria diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSummary.scala b/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSummary.scala index a70eb1ff..1c2e88ea 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSummary.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSummary.scala @@ -19,5 +19,5 @@ package geotrellis.server.ogc.stac import com.azavea.stac4s.StacCollection sealed trait StacSummary -case object EmptySummary extends StacSummary +case object EmptySummary extends StacSummary case class CollectionSummary(asset: StacCollection) extends StacSummary diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/stac/package.scala b/stac-example/src/main/scala/geotrellis/server/ogc/stac/package.scala index 0d89922d..44d7816f 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/stac/package.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/stac/package.scala @@ -44,13 +44,17 @@ package object stac { implicit class AssetsMapOps(val assets: Map[String, StacAsset]) extends AnyVal { - /** Returns the first asset that matches selector Regex */ + /** + * Returns the first asset that matches selector Regex + */ def select(selector: Regex): Option[StacAsset] = assets.find { case (k, _) => selector.findFirstIn(k).nonEmpty }.map(_._2) } implicit class StacExtentionOps(val self: StacExtent) extends AnyVal { - /** [[StacExtent]]s with no temporal component are valid. */ + /** + * [[StacExtent]]s with no temporal component are valid. + */ def ogcTime: Option[OgcTime] = self.temporal.interval.headOption.map { case TemporalExtent(start, end) => List(start, end).flatten.map(_.atZone(ZoneOffset.UTC)) }.map { case fst :: Nil => OgcTimeInterval(fst) @@ -68,7 +72,7 @@ package object stac { } implicit class StacAssetOps(val self: StacAsset) extends AnyVal { - def hrefGDAL(withGDAL: Boolean): String = if (withGDAL) s"gdal+${self.href}" else s"${GeoTiffPath.PREFIX}${self.href}" + def hrefGDAL(withGDAL: Boolean): String = if (withGDAL) s"gdal+${self.href}" else s"${GeoTiffPath.PREFIX}${self.href}" def withGDAL(withGDAL: Boolean): StacAsset = self.copy(href = hrefGDAL(withGDAL)) def withAzureSupport(toWASBS: Boolean): StacAsset = if (toWASBS && AzureRangeReaderProvider.canProcess(self.href)) self.copy(href = AzureURI.fromString(self.href).toString) else self @@ -117,7 +121,9 @@ package object stac { } } else self - /** Collects all RasterSources attributes and prefixing each result Map key with the RasterSource name. */ + /** + * Collects all RasterSources attributes and prefixing each result Map key with the RasterSource name. + */ def attributesByName: Map[String, String] = self.foldMap { rs => rs.name match { diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/wcs/WcsView.scala b/stac-example/src/main/scala/geotrellis/server/ogc/wcs/WcsView.scala index e55b6009..83f28afb 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/wcs/WcsView.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/wcs/WcsView.scala @@ -20,20 +20,19 @@ import geotrellis.server.ogc.{OutputFormat, ToMediaType} import geotrellis.server.ogc.params.ParamError import geotrellis.server.ogc.ows.OwsDataRecord import geotrellis.server.utils._ - import org.backuity.ansi.AnsiFormatter.FormattedHelper import org.http4s.scalaxml._ import org.http4s._ import org.http4s.dsl.Http4sDsl import cats.effect._ -import cats.Parallel +import cats.{ApplicativeThrow, Parallel} import cats.data.Validated import cats.syntax.apply._ import cats.syntax.flatMap._ import cats.syntax.functor._ import cats.syntax.option._ import cats.syntax.applicativeError._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import opengis.ows.{AllowedValues, AnyValue, DomainType, ValueType} import opengis._ import org.http4s.headers.`Content-Type` @@ -41,7 +40,7 @@ import scalaxb._ import java.net._ -class WcsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( +class WcsView[F[_]: Async: Parallel: ApplicativeThrow: Logger]( wcsModel: WcsModel[F], serviceUrl: URL ) extends Http4sDsl[F] { @@ -158,12 +157,12 @@ class WcsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( case Validated.Valid(wcsParams) => wcsParams match { case _: GetCapabilitiesWcsParams => - logger.debug(ansi"%bold{GetCapabilities: $serviceUrl}") *> + (logger.debug(ansi"%bold{GetCapabilities: $serviceUrl}") *> new CapabilitiesView[F]( wcsModel, serviceUrl, extendedParameters ::: extendedRGBParameters - ).toXML flatMap { Ok(_) } + ).toXML).flatMap { Ok(_) } case p: DescribeCoverageWcsParams => logger.debug(ansi"%bold{DescribeCoverage: ${req.uri}}") *> @@ -171,10 +170,10 @@ class WcsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( case p: GetCoverageWcsParams => for { - _ <- logger.debug(ansi"%bold{GetCoverage: ${req.uri}}") + _ <- logger.debug(ansi"%bold{GetCoverage: ${req.uri}}") getCoverage <- getCoverage.build(p).attempt - result <- handleError(getCoverage, p.format) - _ <- logger.debug(s"getcoverage result: $result") + result <- handleError(getCoverage, p.format) + _ <- logger.debug(s"getcoverage result: $result") } yield result } } diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/wms/WmsView.scala b/stac-example/src/main/scala/geotrellis/server/ogc/wms/WmsView.scala index baa1b7d2..3b34053f 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/wms/WmsView.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/wms/WmsView.scala @@ -29,12 +29,12 @@ import org.http4s.circe._ import org.http4s.dsl.Http4sDsl import io.circe.syntax._ import cats.effect._ -import cats.Parallel +import cats.{ApplicativeThrow, Parallel} import cats.syntax.flatMap._ import cats.syntax.functor._ import cats.syntax.option._ import cats.data.Validated._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import com.github.blemale.scaffeine.{Cache, Scaffeine} import org.backuity.ansi.AnsiFormatter.FormattedHelper import opengis._ @@ -46,7 +46,7 @@ import java.net.URL import scala.concurrent.duration._ import scala.xml.Elem -class WmsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( +class WmsView[F[_]: Async: Parallel: ApplicativeThrow: Logger]( wmsModel: WmsModel[F], serviceUrl: URL ) extends Http4sDsl[F] { diff --git a/stac-example/src/main/scala/geotrellis/server/ogc/wmts/WmtsView.scala b/stac-example/src/main/scala/geotrellis/server/ogc/wmts/WmtsView.scala index b0ad484b..29c6aa66 100644 --- a/stac-example/src/main/scala/geotrellis/server/ogc/wmts/WmtsView.scala +++ b/stac-example/src/main/scala/geotrellis/server/ogc/wmts/WmtsView.scala @@ -21,7 +21,6 @@ import geotrellis.server.ogc._ import geotrellis.server.ogc.params.ParamError import geotrellis.server.ogc.wmts.WmtsParams.{GetCapabilities, GetTile} import geotrellis.server.utils._ - import geotrellis.layer.SpatialKey import geotrellis.raster.Raster import com.azavea.maml.eval._ @@ -31,7 +30,7 @@ import org.http4s.dsl.Http4sDsl import org.http4s.circe._ import _root_.io.circe.syntax._ import cats.effect._ -import cats.Parallel +import cats.{ApplicativeThrow, Parallel} import cats.data.Validated.{Invalid, Valid} import cats.syntax.apply._ import cats.syntax.applicative._ @@ -39,7 +38,7 @@ import cats.syntax.functor._ import cats.syntax.flatMap._ import cats.syntax.applicativeError._ import cats.syntax.parallel._ -import io.chrisdavenport.log4cats.Logger +import org.typelevel.log4cats.Logger import com.github.blemale.scaffeine.{Cache, Scaffeine} import org.backuity.ansi.AnsiFormatter.FormattedHelper import org.http4s.headers.`Content-Type` @@ -47,7 +46,7 @@ import org.http4s.headers.`Content-Type` import scala.concurrent.duration._ import java.net._ -class WmtsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( +class WmtsView[F[_]: Async: Parallel: ApplicativeThrow: Logger]( wmtsModel: WmtsModel[F], serviceUrl: URL ) extends Http4sDsl[F] { @@ -67,13 +66,13 @@ class WmtsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( logger.warn(msg) *> BadRequest(msg) case Valid(_: GetCapabilities) => - logger.debug(ansi"%bold{GetCapabilities: ${req.uri}}") *> - new CapabilitiesView(wmtsModel, serviceUrl).toXML flatMap (Ok(_)) + (logger.debug(ansi"%bold{GetCapabilities: ${req.uri}}") *> + new CapabilitiesView(wmtsModel, serviceUrl).toXML).flatMap(Ok(_)) case Valid(wmtsReq: GetTile) => logger.debug(ansi"%bold{GetTile: ${req.uri}}") - val tileCol = wmtsReq.tileCol - val tileRow = wmtsReq.tileRow + val tileCol = wmtsReq.tileCol + val tileRow = wmtsReq.tileRow val layerName = wmtsReq.layer val res = @@ -95,23 +94,26 @@ class WmtsView[F[_]: Concurrent: Parallel: ApplicativeThrow: Logger]( LayerHistogram(mas.algebra.pure[F], mas.parameters.pure[F], ConcurrentInterpreter.DEFAULT[F], 512) } - (evalWmts(0, tileCol, tileRow), evalHisto).parMapN { - case (Valid(mbtile), Valid(hists)) => Valid((mbtile, hists)) - case (Invalid(errs), _) => Invalid(errs) - case (_, Invalid(errs)) => Invalid(errs) - }.attempt flatMap { - case Right(Valid((mbtile, hists))) => // success - val extent = layer.layout.mapTransform(SpatialKey(tileCol, tileRow)) - val rendered = Raster(mbtile, extent).render(layer.crs, layer.style, wmtsReq.format, hists) - tileCache.put(wmtsReq, rendered) - Ok(rendered).map(_.putHeaders(`Content-Type`(ToMediaType(wmtsReq.format)))) - case Right(Invalid(errs)) => // maml-specific errors - logger.debug(errs.toList.toString) - BadRequest(errs.asJson) - case Left(err) => // exceptions - logger.error(err.stackTraceString) - InternalServerError(err.stackTraceString) - } + (evalWmts(0, tileCol, tileRow), evalHisto) + .parMapN { + case (Valid(mbtile), Valid(hists)) => Valid((mbtile, hists)) + case (Invalid(errs), _) => Invalid(errs) + case (_, Invalid(errs)) => Invalid(errs) + } + .attempt + .flatMap { + case Right(Valid((mbtile, hists))) => // success + val extent = layer.layout.mapTransform(SpatialKey(tileCol, tileRow)) + val rendered = Raster(mbtile, extent).render(layer.crs, layer.style, wmtsReq.format, hists) + tileCache.put(wmtsReq, rendered) + Ok(rendered).map(_.putHeaders(`Content-Type`(ToMediaType(wmtsReq.format)))) + case Right(Invalid(errs)) => // maml-specific errors + logger.debug(errs.toList.toString) + BadRequest(errs.asJson) + case Left(err) => // exceptions + logger.error(err.stackTraceString) + InternalServerError(err.stackTraceString) + } }.headOption.getOrElse(BadRequest(s"Layer ($layerName) not found")) } diff --git a/stac-example/src/test/scala/geotrellis/IOSpec.scala b/stac-example/src/test/scala/geotrellis/IOSpec.scala index e8bda05b..af74c21f 100644 --- a/stac-example/src/test/scala/geotrellis/IOSpec.scala +++ b/stac-example/src/test/scala/geotrellis/IOSpec.scala @@ -16,19 +16,17 @@ package geotrellis -import cats.effect.{ContextShift, IO, Timer} -import geotrellis.store.util.BlockingThreadPool -import io.chrisdavenport.log4cats.Logger -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger +import cats.effect.IO +import cats.effect.unsafe.IORuntime +import org.typelevel.log4cats.Logger +import org.typelevel.log4cats.slf4j.Slf4jLogger import org.scalatest.funspec.AsyncFunSpec import org.scalatest.matchers.should.Matchers import org.scalatest.{Assertion, Assertions} trait IOSpec extends AsyncFunSpec with Assertions with Matchers { - implicit override val executionContext = BlockingThreadPool.executionContext - implicit val contextShift: ContextShift[IO] = IO.contextShift(executionContext) - implicit val timer: Timer[IO] = IO.timer(executionContext) - implicit val logger: Logger[IO] = Slf4jLogger.getLogger[IO] + implicit val runtime: IORuntime = IORuntime.global + implicit val logger: Logger[IO] = Slf4jLogger.getLogger[IO] private val itWord = new ItWord diff --git a/stac-example/src/test/scala/geotrellis/server/ogc/stac/Http4sStacClientSpec.scala b/stac-example/src/test/scala/geotrellis/server/ogc/stac/Http4sStacClientSpec.scala index 8dce5356..064d40a2 100644 --- a/stac-example/src/test/scala/geotrellis/server/ogc/stac/Http4sStacClientSpec.scala +++ b/stac-example/src/test/scala/geotrellis/server/ogc/stac/Http4sStacClientSpec.scala @@ -18,7 +18,7 @@ package geotrellis.server.ogc.stac import cats.data.NonEmptyList import cats.data.Validated.Valid -import cats.effect.{Blocker, ConcurrentEffect, ContextShift, IO} +import cats.effect.{Async, IO} import com.azavea.stac4s.api.client.SttpStacClient import com.azavea.stac4s.extensions.layer.LayerItemExtension import com.azavea.stac4s.syntax._ @@ -28,18 +28,17 @@ import geotrellis.proj4.CRS import geotrellis.server.ogc.stac import geotrellis.store.query.vector.ProjectedGeometry import geotrellis.vector.Extent -import io.chrisdavenport.log4cats.{Logger => Logger4Cats} +import org.typelevel.log4cats.{Logger => Logger4Cats} import sttp.client3.UriContext import sttp.client3.http4s.Http4sBackend -import scala.concurrent.ExecutionContext import scala.language.reflectiveCalls class Http4sStacClientSpec extends IOSpec { - def withClient[F[_]: ContextShift: ConcurrentEffect: Logger4Cats](implicit ec: ExecutionContext) = + def withClient[F[_]: Async: Logger4Cats] = new { def apply[T](f: SttpStacClient[F] => F[T]): F[T] = - Http4sBackend.usingDefaultBlazeClientBuilder[F](Blocker.liftExecutionContext(executionContext), executionContext).use { client => + Http4sBackend.usingDefaultBlazeClientBuilder[F]().use { client => f(SttpStacClient(client, uri"http://localhost:9090/")) } } @@ -62,7 +61,9 @@ class Http4sStacClientSpec extends IOSpec { ignore("should handle the collections query") { withClient[IO].apply { client => - client.collections.take(30).compile.toList.map { list => println(list); true shouldBe true } + client.collections.take(30).compile.toList.map { list => + println(list); true shouldBe true + } } } diff --git a/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjItemExtension.scala b/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjItemExtension.scala index c764333d..2de8e1e2 100644 --- a/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjItemExtension.scala +++ b/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjItemExtension.scala @@ -35,6 +35,6 @@ case class ProjItemExtension( object ProjItemExtension { implicit val eq: Eq[ProjItemExtension] = Eq.fromUniversalEquals - implicit lazy val itemExtension: ItemExtension[ProjItemExtension] = ItemExtension.instance + implicit lazy val itemExtension: ItemExtension[ProjItemExtension] = ItemExtension.instance implicit lazy val stacAssetExtension: StacAssetExtension[ProjItemExtension] = StacAssetExtension.instance } diff --git a/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjShape.scala b/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjShape.scala index 605c5e9d..685c11c7 100644 --- a/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjShape.scala +++ b/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjShape.scala @@ -24,7 +24,7 @@ import scala.util.Try case class ProjShape(cols: Long, rows: Long) { def toDimensions: Dimensions[Long] = Dimensions(cols, rows) - def toList: List[Long] = List(cols, rows) + def toList: List[Long] = List(cols, rows) } object ProjShape { @@ -34,6 +34,6 @@ object ProjShape { ProjShape(cols, rows) }.toEither.leftMap(_.getMessage) - implicit val enProjShape: Encoder[ProjShape] = Encoder.encodeList[Long].contramap(_.toList) + implicit val enProjShape: Encoder[ProjShape] = Encoder.encodeList[Long].contramap(_.toList) implicit val decProjShape: Decoder[ProjShape] = Decoder.decodeList[Long].emap(ProjShape.apply) } diff --git a/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjTransform.scala b/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjTransform.scala index dc9a2443..8dcd33e1 100644 --- a/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjTransform.scala +++ b/stac/src/main/scala/geotrellis/stac/extensions/proj/ProjTransform.scala @@ -24,8 +24,8 @@ import scala.util.Try case class ProjTransform(upx: Double, xres: Double, xskew: Double, upy: Double, yskew: Double, yres: Double) { def toArray: Array[Double] = Array(upx, xres, xskew, upy, yskew, yres) - def toList: List[Double] = toArray.toList - def cellSize: CellSize = CellSize(math.abs(xres), math.abs(yres)) + def toList: List[Double] = toArray.toList + def cellSize: CellSize = CellSize(math.abs(xres), math.abs(yres)) } object ProjTransform { diff --git a/stac/src/main/scala/geotrellis/stac/package.scala b/stac/src/main/scala/geotrellis/stac/package.scala index 7baba9ea..6f0f1a87 100644 --- a/stac/src/main/scala/geotrellis/stac/package.scala +++ b/stac/src/main/scala/geotrellis/stac/package.scala @@ -40,7 +40,7 @@ package object stac { } implicit class SpatialExtentOps(val self: SpatialExtent) extends AnyVal { - def toExtent: Extent = self.bbox.reduce(_ |+| _).toExtent.valueOr(e => throw new Exception(e)) + def toExtent: Extent = self.bbox.reduce(_ |+| _).toExtent.valueOr(e => throw new Exception(e)) def expandToInclude(extent: Extent): SpatialExtent = SpatialExtent(toExtent.expandToInclude(extent).toTwoDimBbox :: Nil) } @@ -48,11 +48,11 @@ package object stac { def expandToInclude(extent: Extent): StacExtent = self.copy(spatial = self.spatial.expandToInclude(extent)) } - implicit def stacItemOps(stacItem: StacItem): StacItemOps = StacItemOps(stacItem) + implicit def stacItemOps(stacItem: StacItem): StacItemOps = StacItemOps(stacItem) implicit def stacAssetOps(stacAsset: StacAsset): StacAssetOps = StacAssetOps(stacAsset) implicit class StacCollectionOps(val self: StacCollection) extends AnyVal { - def sourceName: SourceName = StringName(self.id) + def sourceName: SourceName = StringName(self.id) def expandExtentToInclude(extent: Extent): StacCollection = self.copy(extent = self.extent.expandToInclude(extent)) } @@ -63,15 +63,15 @@ package object stac { sourceName: SourceName, stacAttributes: Map[String, String] ): MosaicRasterSource = { - val combinedExtent = sourcesList.map(_.extent).toList.reduce(_ combine _) - val minCellSize = sourcesList.map(_.cellSize).toList.maxBy(_.resolution) + val combinedExtent = sourcesList.map(_.extent).toList.reduce(_ combine _) + val minCellSize = sourcesList.map(_.cellSize).toList.maxBy(_.resolution) val combinedGridExtent = GridExtent[Long](combinedExtent, minCellSize) new MosaicRasterSource { val sources: NonEmptyList[RasterSource] = sourcesList - val crs: CRS = targetCRS - def gridExtent: GridExtent[Long] = combinedGridExtent - val name: SourceName = sourceName + val crs: CRS = targetCRS + def gridExtent: GridExtent[Long] = combinedGridExtent + val name: SourceName = sourceName override val attributes = stacAttributes } diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacAssetOps.scala b/stac/src/main/scala/geotrellis/stac/raster/StacAssetOps.scala index ab3381a1..a9e8b92b 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacAssetOps.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacAssetOps.scala @@ -38,7 +38,7 @@ case class StacAssetOps(self: StacAsset) { } def getGeometry: Option[Geometry] = projExtension.flatMap(_.geometry) - def getExtent: Option[Extent] = getGeometry.map(geom => Extent(geom.getEnvelopeInternal)) + def getExtent: Option[Extent] = getGeometry.map(geom => Extent(geom.getEnvelopeInternal)) def transform: Option[ProjTransform] = projExtension.flatMap(_.transform) @@ -55,6 +55,6 @@ case class StacAssetOps(self: StacAsset) { .orElse(gsd.map(d => CellSize(d, d))) def gridExtent: Option[GridExtent[Long]] = (getExtent, cellSize).mapN(GridExtent.apply[Long]) - def rasterExtent: Option[RasterExtent] = gridExtent.map(_.toRasterExtent) + def rasterExtent: Option[RasterExtent] = gridExtent.map(_.toRasterExtent) def dimensions: Option[Dimensions[Long]] = projExtension.flatMap(_.shape).map(_.toDimensions) } diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacAssetRasterSource.scala b/stac/src/main/scala/geotrellis/stac/raster/StacAssetRasterSource.scala index cfe6da93..8603a4d6 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacAssetRasterSource.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacAssetRasterSource.scala @@ -33,15 +33,15 @@ class StacAssetRasterSource( ) extends RasterSource { @transient private lazy val underlying = underlyingRS.getOrElse(RasterSource(asset.href)) - val name: SourceName = asset.href - def crs: CRS = asset.crs.getOrElse(underlying.crs) - def bandCount: Int = asset.bandCount.getOrElse(underlying.bandCount) - def cellType: CellType = underlying.cellType - def gridExtent: GridExtent[Long] = asset.gridExtent.getOrElse(underlying.gridExtent) - def resolutions: List[CellSize] = underlying.resolutions - def attributes: Map[String, String] = asset.item.properties.toMap + val name: SourceName = asset.href + def crs: CRS = asset.crs.getOrElse(underlying.crs) + def bandCount: Int = asset.bandCount.getOrElse(underlying.bandCount) + def cellType: CellType = underlying.cellType + def gridExtent: GridExtent[Long] = asset.gridExtent.getOrElse(underlying.gridExtent) + def resolutions: List[CellSize] = underlying.resolutions + def attributes: Map[String, String] = asset.item.properties.toMap def attributesForBand(band: Int): Map[String, String] = Map.empty - def metadata: StacItemAssetMetadata = StacItemAssetMetadata(name, crs, bandCount, cellType, gridExtent, resolutions, asset) + def metadata: StacItemAssetMetadata = StacItemAssetMetadata(name, crs, bandCount, cellType, gridExtent, resolutions, asset) def reprojection( targetCRS: CRS, diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacAssetReprojectRasterSource.scala b/stac/src/main/scala/geotrellis/stac/raster/StacAssetReprojectRasterSource.scala index 4a289b40..bc5b8bd4 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacAssetReprojectRasterSource.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacAssetReprojectRasterSource.scala @@ -38,10 +38,10 @@ class StacAssetReprojectRasterSource( private[geotrellis] val targetCellType: Option[TargetCellType] = None, @transient underlyingRS: => Option[RasterSource] = None ) extends RasterSource { - @transient private lazy val underlying = underlyingRS.getOrElse(RasterSource(asset.href)) + @transient private lazy val underlying = underlyingRS.getOrElse(RasterSource(asset.href)) @transient private lazy val underlyingReprojected = underlying.reproject(crs, resampleTarget, resampleMethod, strategy) - protected lazy val baseCRS: CRS = asset.crs.getOrElse(underlying.crs) + protected lazy val baseCRS: CRS = asset.crs.getOrElse(underlying.crs) protected lazy val baseGridExtent: GridExtent[Long] = asset.gridExtent.getOrElse(underlying.gridExtent) // TODO: remove transient notation with Proj4 1.1 release @@ -58,12 +58,12 @@ class StacAssetReprojectRasterSource( resampleTarget(reprojectedRasterExtent) } - def metadata: StacItemAssetMetadata = StacItemAssetMetadata(name, crs, bandCount, cellType, gridExtent, resolutions, asset) - val name: SourceName = asset.href - def bandCount: Int = asset.bandCount.getOrElse(underlyingReprojected.bandCount) - def cellType: CellType = underlyingReprojected.cellType - def resolutions: List[CellSize] = underlyingReprojected.resolutions - def attributes: Map[String, String] = asset.item.properties.toMap + def metadata: StacItemAssetMetadata = StacItemAssetMetadata(name, crs, bandCount, cellType, gridExtent, resolutions, asset) + val name: SourceName = asset.href + def bandCount: Int = asset.bandCount.getOrElse(underlyingReprojected.bandCount) + def cellType: CellType = underlyingReprojected.cellType + def resolutions: List[CellSize] = underlyingReprojected.resolutions + def attributes: Map[String, String] = asset.item.properties.toMap def attributesForBand(band: Int): Map[String, String] = Map.empty def reprojection( diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacAssetResampleRasterSource.scala b/stac/src/main/scala/geotrellis/stac/raster/StacAssetResampleRasterSource.scala index e6a6755a..b7e1ac26 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacAssetResampleRasterSource.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacAssetResampleRasterSource.scala @@ -36,18 +36,18 @@ class StacAssetResampleRasterSource( private[geotrellis] val targetCellType: Option[TargetCellType], @transient underlyingRS: => Option[RasterSource] ) extends RasterSource { - @transient private lazy val underlying = underlyingRS.getOrElse(RasterSource(asset.href)) + @transient private lazy val underlying = underlyingRS.getOrElse(RasterSource(asset.href)) @transient private lazy val underlyingResampled = underlying.resample(resampleTarget, resampleMethod, strategy) lazy val gridExtent: GridExtent[Long] = resampleTarget(asset.gridExtent.getOrElse(underlying.gridExtent)) - def metadata: StacItemAssetMetadata = StacItemAssetMetadata(name, crs, bandCount, cellType, gridExtent, resolutions, asset) - val name: SourceName = asset.href - def crs: CRS = asset.crs.getOrElse(underlying.crs) - def bandCount: Int = asset.bandCount.getOrElse(underlyingResampled.bandCount) - def cellType: CellType = underlyingResampled.cellType - def resolutions: List[CellSize] = underlyingResampled.resolutions - def attributes: Map[String, String] = asset.item.properties.toMap + def metadata: StacItemAssetMetadata = StacItemAssetMetadata(name, crs, bandCount, cellType, gridExtent, resolutions, asset) + val name: SourceName = asset.href + def crs: CRS = asset.crs.getOrElse(underlying.crs) + def bandCount: Int = asset.bandCount.getOrElse(underlyingResampled.bandCount) + def cellType: CellType = underlyingResampled.cellType + def resolutions: List[CellSize] = underlyingResampled.resolutions + def attributes: Map[String, String] = asset.item.properties.toMap def attributesForBand(band: Int): Map[String, String] = Map.empty def reprojection( diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacCollectionSource.scala b/stac/src/main/scala/geotrellis/stac/raster/StacCollectionSource.scala index 3e24349b..d0580aba 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacCollectionSource.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacCollectionSource.scala @@ -22,7 +22,7 @@ import com.azavea.stac4s.{StacCollection, StacExtent} case class StacCollectionSource(asset: StacCollection, source: RasterSource) extends StacSource[StacCollection] { val stacExtent: StacExtent = asset.extent - val name: SourceName = StringName(asset.id) + val name: SourceName = StringName(asset.id) lazy val attributes: Map[String, String] = asset.asJson.asObject diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacItemAsset.scala b/stac/src/main/scala/geotrellis/stac/raster/StacItemAsset.scala index 2c3ce5ed..dd66fa26 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacItemAsset.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacItemAsset.scala @@ -24,23 +24,23 @@ import geotrellis.stac.extensions.proj.ProjTransform import geotrellis.vector.{Extent, Geometry} case class StacItemAsset(itemAsset: StacAsset, item: StacItem) { - def href: String = itemAsset.href + def href: String = itemAsset.href def bandCount: Option[Int] = item.bandCount - def crs: Option[CRS] = itemAsset.crs orElse item.crs + def crs: Option[CRS] = itemAsset.crs.orElse(item.crs) // geometry can be taken from the proj extension or projected from the LatLng geometry - def getGeometry: Option[Geometry] = itemAsset.getGeometry orElse item.getGeometry - def getExtent: Option[Extent] = itemAsset.getExtent orElse item.getExtent + def getGeometry: Option[Geometry] = itemAsset.getGeometry.orElse(item.getGeometry) + def getExtent: Option[Extent] = itemAsset.getExtent.orElse(item.getExtent) - def transform: Option[ProjTransform] = itemAsset.transform orElse item.transform + def transform: Option[ProjTransform] = itemAsset.transform.orElse(item.transform) // https://github.com/radiantearth/stac-spec/blob/v1.0.0-rc.1/item-spec/common-metadata.md#gsd - def gsd: Option[Double] = itemAsset.gsd orElse item.gsd + def gsd: Option[Double] = itemAsset.gsd.orElse(item.gsd) // the cellSize can be extracted from the transform object or derived from the given extent and shape - def cellSize: Option[CellSize] = itemAsset.cellSize orElse item.cellSize + def cellSize: Option[CellSize] = itemAsset.cellSize.orElse(item.cellSize) - def gridExtent: Option[GridExtent[Long]] = itemAsset.gridExtent orElse item.gridExtent - def rasterExtent: Option[RasterExtent] = itemAsset.rasterExtent orElse item.rasterExtent - def dimensions: Option[Dimensions[Long]] = itemAsset.dimensions orElse item.dimensions + def gridExtent: Option[GridExtent[Long]] = itemAsset.gridExtent.orElse(item.gridExtent) + def rasterExtent: Option[RasterExtent] = itemAsset.rasterExtent.orElse(item.rasterExtent) + def dimensions: Option[Dimensions[Long]] = itemAsset.dimensions.orElse(item.dimensions) } diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacItemAssetMetadata.scala b/stac/src/main/scala/geotrellis/stac/raster/StacItemAssetMetadata.scala index cddf2f59..3f5eb8f2 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacItemAssetMetadata.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacItemAssetMetadata.scala @@ -29,6 +29,6 @@ case class StacItemAssetMetadata( resolutions: List[CellSize], stacItemAsset: StacItemAsset ) extends RasterMetadata { - def attributes: Map[String, String] = stacItemAsset.item.properties.toMap + def attributes: Map[String, String] = stacItemAsset.item.properties.toMap def attributesForBand(band: Int): Map[String, String] = Map.empty } diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacItemOps.scala b/stac/src/main/scala/geotrellis/stac/raster/StacItemOps.scala index daee140a..2e1c1acf 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacItemOps.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacItemOps.scala @@ -28,10 +28,10 @@ import com.azavea.stac4s.extensions.ExtensionResult import cats.syntax.apply._ case class StacItemOps(self: StacItem) { - def eoExtension: ExtensionResult[EOItemExtension] = self.getExtensionFields[EOItemExtension] + def eoExtension: ExtensionResult[EOItemExtension] = self.getExtensionFields[EOItemExtension] def projExtension: ExtensionResult[ProjItemExtension] = self.getExtensionFields[ProjItemExtension] - private def eoExtensionOption: Option[EOItemExtension] = eoExtension.toOption + private def eoExtensionOption: Option[EOItemExtension] = eoExtension.toOption private def projExtensionOption: Option[ProjItemExtension] = projExtension.toOption def bandCount: Option[Int] = eoExtensionOption.map(_.bands.length) @@ -47,7 +47,7 @@ case class StacItemOps(self: StacItem) { // geometry can be taken from the proj extension or projected from the LatLng geometry def getGeometry: Option[Geometry] = projExtensionOption.flatMap(_.geometry).orElse(crs.map(self.geometry.reproject(LatLng, _))) - def getExtent: Option[Extent] = getGeometry.map(geom => Extent(geom.getEnvelopeInternal)) + def getExtent: Option[Extent] = getGeometry.map(geom => Extent(geom.getEnvelopeInternal)) def transform: Option[ProjTransform] = projExtensionOption.flatMap(_.transform) @@ -64,6 +64,6 @@ case class StacItemOps(self: StacItem) { .orElse(gsd.map(d => CellSize(d, d))) def gridExtent: Option[GridExtent[Long]] = (getExtent, cellSize).mapN(GridExtent.apply[Long]) - def rasterExtent: Option[RasterExtent] = gridExtent.map(_.toRasterExtent) + def rasterExtent: Option[RasterExtent] = gridExtent.map(_.toRasterExtent) def dimensions: Option[Dimensions[Long]] = projExtensionOption.flatMap(_.shape).map(_.toDimensions) } diff --git a/stac/src/main/scala/geotrellis/stac/raster/StacSource.scala b/stac/src/main/scala/geotrellis/stac/raster/StacSource.scala index 41b67117..0539f543 100644 --- a/stac/src/main/scala/geotrellis/stac/raster/StacSource.scala +++ b/stac/src/main/scala/geotrellis/stac/raster/StacSource.scala @@ -24,9 +24,9 @@ import geotrellis.raster.{RasterSource, SourceName} import geotrellis.vector.{Extent, ProjectedExtent} trait StacSource[T] { - val crs: CRS = LatLng + val crs: CRS = LatLng def projectedExtent: ProjectedExtent = ProjectedExtent(extent, crs) - def extent: Extent = stacExtent.spatial.toExtent + def extent: Extent = stacExtent.spatial.toExtent def asset: T def name: SourceName diff --git a/stac/src/main/scala/geotrellis/stac/util/logging/StacClientLoggingMid.scala b/stac/src/main/scala/geotrellis/stac/util/logging/StacClientLoggingMid.scala index 7715e2fa..c2eca846 100644 --- a/stac/src/main/scala/geotrellis/stac/util/logging/StacClientLoggingMid.scala +++ b/stac/src/main/scala/geotrellis/stac/util/logging/StacClientLoggingMid.scala @@ -22,7 +22,7 @@ import com.azavea.stac4s.api.client.{ETag, SearchFilters, StreamingStacClient, S import com.azavea.stac4s.{StacCollection, StacItem} import eu.timepit.refined.types.string.NonEmptyString import fs2.Stream -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger +import org.typelevel.log4cats.slf4j.Slf4jLogger import io.circe.Json import io.circe.syntax._ import tofu.higherKind.Mid diff --git a/stac/src/main/scala/geotrellis/stac/util/logging/StreamingStacClientLoggingMid.scala b/stac/src/main/scala/geotrellis/stac/util/logging/StreamingStacClientLoggingMid.scala index 52c0bf65..7b78a6fa 100644 --- a/stac/src/main/scala/geotrellis/stac/util/logging/StreamingStacClientLoggingMid.scala +++ b/stac/src/main/scala/geotrellis/stac/util/logging/StreamingStacClientLoggingMid.scala @@ -21,7 +21,7 @@ import com.azavea.stac4s.api.client.{ETag, SearchFilters, StreamingStacClient, S import com.azavea.stac4s.{StacCollection, StacItem} import eu.timepit.refined.types.string.NonEmptyString import fs2.Stream -import io.chrisdavenport.log4cats.slf4j.Slf4jLogger +import org.typelevel.log4cats.slf4j.Slf4jLogger import io.circe.Json import io.circe.syntax._ import tofu.higherKind.Mid diff --git a/stac/src/main/scala/geotrellis/stac/util/logging/syntax.scala b/stac/src/main/scala/geotrellis/stac/util/logging/syntax.scala index 609d9325..b0f4d037 100644 --- a/stac/src/main/scala/geotrellis/stac/util/logging/syntax.scala +++ b/stac/src/main/scala/geotrellis/stac/util/logging/syntax.scala @@ -24,7 +24,9 @@ import tofu.higherKind.Mid object syntax { - /** Syntax to attach two [[Mid]] instances (each for a separate param) to a service with two type params. */ + /** + * Syntax to attach two [[Mid]] instances (each for a separate param) to a service with two type params. + */ implicit class MidOps3Tuple[U[_[_], _[_], _], F[_], G[_], A](val mfg: (U[Mid[F, *], G, A], U[F, Mid[G, *], A])) extends AnyVal { def attach(u: U[F, G, A])(implicit af: ApplyK[U[F, *[_], A]], ag: ApplyK[U[*[_], G, A]]): U[F, G, A] = Mid.attach[U[*[_], G, A], F](mfg._1)(Mid.attach[U[F, *[_], A], G](mfg._2)(u)) @@ -32,13 +34,13 @@ object syntax { implicit class MidOps3TupleReverse[U[_[_], _[_], _], F[_], G[_], A](val mfg: (U[F, Mid[G, *], A], U[Mid[F, *], G, A])) extends AnyVal { def attach(u: U[F, G, A])(implicit af: ApplyK[U[F, *[_], A]], ag: ApplyK[U[*[_], G, A]]): U[F, G, A] = - mfg.swap attach u + mfg.swap.attach(u) } implicit def stacClientApplyKF[F[_]]: ApplyK[StreamingStacClient[*[_], Stream[F, *]]] = Derive.applyK[StreamingStacClient[*[_], Stream[F, *]]] - implicit def stacClientApplyKG[F[_]]: ApplyK[StreamingStacClient[F, *[_]]] = Derive.applyK[StreamingStacClient[F, *[_]]] + implicit def stacClientApplyKG[F[_]]: ApplyK[StreamingStacClient[F, *[_]]] = Derive.applyK[StreamingStacClient[F, *[_]]] implicit class StreamingStacClientOps[F[_]](val self: StreamingStacClientFS2[F]) extends AnyVal { - def withLogging(implicit sync: Sync[F]): StreamingStacClientFS2[F] = (StacClientLoggingMid[F], StreamingStacClientLoggingMid[F]) attach self + def withLogging(implicit sync: Sync[F]): StreamingStacClientFS2[F] = (StacClientLoggingMid[F], StreamingStacClientLoggingMid[F]).attach(self) } }