-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
stac-simple-example/src/main/scala/geotrellis/example/MainAkka.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package geotrellis.example | ||
|
||
import cats.data.NonEmptyList | ||
import cats.effect.unsafe.IORuntime | ||
import cats.syntax.functor._ | ||
import cats.syntax.nested._ | ||
import cats.syntax.option._ | ||
import com.azavea.stac4s.api.client.{SearchFilters, SttpStacClient} | ||
import geotrellis.proj4.WebMercator | ||
import geotrellis.raster.effects.MosaicRasterSourceIO | ||
import geotrellis.raster.{MosaicRasterSource, StringName} | ||
import geotrellis.stac.raster.{StacAssetRasterSource, StacItemAsset} | ||
import geotrellis.vector.Extent | ||
import sttp.client3.UriContext | ||
import sttp.client3.akkahttp._ | ||
|
||
import scala.concurrent.duration.DurationInt | ||
import scala.concurrent.Await | ||
|
||
object MainAkka { | ||
// async context is good for client | ||
import scala.concurrent.ExecutionContext.Implicits.global | ||
|
||
def main(args: Array[String]): Unit = { | ||
val searchFilters = SearchFilters() | ||
val limit = 10000 // max items length if filter result is too wide | ||
val assetName = "b0".r | ||
val withGDAL: Boolean = false | ||
val defaultCRS = WebMercator | ||
val parallelMosaicEnabled = false | ||
val collectionName = StringName("aviris-classic") | ||
val extent = Extent(0, 0, 180, 180) | ||
val stacCatalogURI = uri"http://localhost:9090/" | ||
|
||
val backend = AkkaHttpBackend() | ||
val client = SttpStacClient(backend, stacCatalogURI) | ||
|
||
val source = client | ||
.search(searchFilters) | ||
.take(limit) | ||
.compileToFutureList | ||
.map(MosaicRasterSource.fromStacItems(collectionName, _, assetName, defaultCRS, withGDAL, parallelMosaicEnabled)) | ||
|
||
val result = source.nested | ||
.map(_.read(extent)) | ||
.value | ||
.map(_.flatten) | ||
.map { | ||
case Some(raster) => println(s"raster.extent: ${raster.extent}") | ||
case None => println(s"no rasters found for $extent") | ||
} | ||
|
||
Await.ready(result, 10.seconds) | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
stac-simple-example/src/main/scala/geotrellis/example/MainCats.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package geotrellis.example | ||
|
||
import cats.data.NonEmptyList | ||
import cats.effect.{ExitCode, IO, IOApp} | ||
import com.azavea.stac4s.api.client.{SearchFilters, SttpStacClient} | ||
import geotrellis.proj4.WebMercator | ||
import geotrellis.raster.{MosaicRasterSource, StringName} | ||
import geotrellis.raster.effects.MosaicRasterSourceIO | ||
import geotrellis.stac.raster.{StacAssetRasterSource, StacItemAsset} | ||
import geotrellis.vector.Extent | ||
import sttp.client3.asynchttpclient.cats.AsyncHttpClientCatsBackend | ||
import sttp.client3.UriContext | ||
import cats.syntax.option._ | ||
import cats.syntax.nested._ | ||
import cats.syntax.functor._ | ||
|
||
object MainCats extends IOApp { | ||
|
||
def run(args: List[String]): IO[ExitCode] = { | ||
val searchFilters = SearchFilters() | ||
val limit = 10000 // max items length if filter result is too wide | ||
val assetName = "b0".r | ||
val withGDAL: Boolean = false | ||
val defaultCRS = WebMercator | ||
val parallelMosaicEnabled = false | ||
val collectionName = StringName("aviris-classic") | ||
val extent = Extent(0, 0, 180, 180) | ||
val stacCatalogURI = uri"http://localhost:9090/" | ||
|
||
AsyncHttpClientCatsBackend | ||
.resource[IO]() | ||
.use { backend => | ||
val client = SttpStacClient(backend, stacCatalogURI) | ||
client | ||
.search(searchFilters) | ||
.take(limit) | ||
.compile | ||
.toList | ||
.map(MosaicRasterSource.fromStacItems(collectionName, _, assetName, defaultCRS, withGDAL, parallelMosaicEnabled)) | ||
} | ||
.nested | ||
.map(_.read(extent)) | ||
.value | ||
.map(_.flatten) | ||
.map { | ||
case Some(raster) => println(s"raster.extent: ${raster.extent}") | ||
case None => println(s"no rasters found for $extent") | ||
} | ||
.as(ExitCode.Success) | ||
} | ||
} |
111 changes: 111 additions & 0 deletions
111
stac-simple-example/src/main/scala/geotrellis/example/package.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package geotrellis | ||
|
||
import cats.{~>, Foldable, FunctorFilter} | ||
import cats.data.NonEmptyList | ||
import cats.effect.IO | ||
import cats.effect.unsafe.IORuntime | ||
import cats.syntax.foldable._ | ||
import cats.syntax.option._ | ||
import com.azavea.stac4s.{StacAsset, StacItem} | ||
import geotrellis.proj4.CRS | ||
import geotrellis.raster.effects.MosaicRasterSourceIO | ||
import geotrellis.raster.{EmptyName, GridExtent, MosaicRasterSource, RasterSource, SourceName, StringName} | ||
import geotrellis.raster.geotiff.GeoTiffPath | ||
import geotrellis.stac.raster.{StacAssetRasterSource, StacItemAsset} | ||
|
||
import scala.concurrent.{ExecutionContext, Future} | ||
import scala.util.matching.Regex | ||
|
||
package object example { | ||
implicit class AssetsMapOps(private val assets: Map[String, StacAsset]) extends AnyVal { | ||
def select(selector: Regex): Option[StacAsset] = assets.find { case (k, _) => selector.findFirstIn(k).nonEmpty }.map(_._2) | ||
} | ||
|
||
implicit class StacAssetOps(private val self: StacAsset) extends AnyVal { | ||
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)) | ||
} | ||
|
||
implicit class RasterSourcesQueryOps[G[_]: Foldable: FunctorFilter, T <: RasterSource](private val self: G[T]) { | ||
def attributesByName: Map[String, String] = | ||
self.foldMap { rs => | ||
rs.name match { | ||
case StringName(sn) => rs.attributes.map { case (k, v) => s"$sn-$k" -> v } | ||
case EmptyName => rs.attributes | ||
} | ||
} | ||
} | ||
|
||
implicit class MosaicRasterSourceOps(private val self: MosaicRasterSource.type) extends AnyVal { | ||
def instance( | ||
sourcesList: NonEmptyList[RasterSource], | ||
targetCRS: CRS, | ||
sourceName: SourceName, | ||
stacAttributes: Map[String, String] | ||
): MosaicRasterSource = { | ||
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 | ||
|
||
override val attributes = stacAttributes | ||
} | ||
} | ||
|
||
def instance(sourcesList: NonEmptyList[RasterSource], targetCRS: CRS, stacAttributes: Map[String, String]): MosaicRasterSource = | ||
instance(sourcesList, targetCRS, EmptyName, stacAttributes) | ||
|
||
def fromStacItems(collectionName: SourceName, | ||
items: List[StacItem], | ||
assetName: Regex, | ||
defaultCRS: CRS, | ||
withGDAL: Boolean, | ||
parallelMosaicEnabled: Boolean | ||
): Option[RasterSource] = { | ||
val sources = StacAssetRasterSource(items, assetName, withGDAL) | ||
sources match { | ||
case head :: Nil => head.some | ||
case head :: tail => | ||
val commonCrs = if (sources.flatMap(_.asset.crs).distinct.size == 1) head.crs else defaultCRS | ||
val reprojectedSources = NonEmptyList.of(head, tail: _*).map(_.reproject(commonCrs)) | ||
val attributes = reprojectedSources.toList.attributesByName | ||
|
||
val mosaicRasterSource = | ||
if (parallelMosaicEnabled) | ||
MosaicRasterSourceIO.instance(reprojectedSources, commonCrs, collectionName, attributes)(IORuntime.global) | ||
else | ||
MosaicRasterSource.instance(reprojectedSources, commonCrs, collectionName, attributes) | ||
|
||
mosaicRasterSource.some | ||
case _ => None | ||
} | ||
} | ||
} | ||
|
||
// format: off | ||
/** | ||
* Ugly shims: | ||
* 1. search via Futures backend and produce futures | ||
* 2. map into IO to compile fs2.Stream | ||
* 3. convert it back to Future[List[T]] | ||
*/ | ||
// format: on | ||
implicit class FS2StreamFutureOps[A](private val self: fs2.Stream[Future, A]) extends AnyVal { | ||
def toStreamIO: fs2.Stream[IO, A] = self.translate(λ[Future ~> IO](future => IO.fromFuture(IO(future)))) | ||
def compileToFutureList(implicit ec: ExecutionContext): Future[List[A]] = | ||
Future(toStreamIO.compile.toList.unsafeRunSync()(IORuntime.global)) | ||
} | ||
|
||
implicit class StacAssetRasterSourceOps(private val self: StacAssetRasterSource.type) extends AnyVal { | ||
def apply(items: List[StacItem], assetName: Regex, withGDAL: Boolean): Seq[StacAssetRasterSource] = items.flatMap { item => | ||
item.assets | ||
.select(assetName) | ||
.map(itemAsset => StacAssetRasterSource(StacItemAsset(itemAsset.withGDAL(withGDAL), item))) | ||
} | ||
} | ||
} |