Skip to content

Instantly share code, notes, and snippets.

@lossyrob
Created July 22, 2015 19:47
Show Gist options
  • Save lossyrob/f95984cc81d810556184 to your computer and use it in GitHub Desktop.
Save lossyrob/f95984cc81d810556184 to your computer and use it in GitHub Desktop.
OAM tile server
import geotrellis.raster._
import geotrellis.raster.render._
import geotrellis.raster.resample._
import geotrellis.raster.io.geotiff._
import geotrellis.vector._
import geotrellis.vector.io.json._
import geotrellis.vector.reproject._
import geotrellis.spark._
import geotrellis.spark.tiling._
import geotrellis.proj4._
import spire.syntax.cfor._
import akka.actor.ActorSystem
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import spray.http.{MediaTypes }
import spray.httpx.SprayJsonSupport._
import spray.json._
import spray.routing._
import spray.util.LoggingContext
import spray.http.StatusCodes._
object TMSService extends App with SimpleRoutingApp {
implicit val system = ActorSystem("foundry-server-system")
val rootPath = "/Users/rob/proj/oam/data/oam-tiled-zoomedThrough"
val layers = List(
("20140903_154221_0906_visual-reprojected-correctbands", 15),
("20140903_154222_0906_visual-reprojected-correctbands", 15),
("20140903_154223_0906_visual-reprojected-correctbands", 15),
("20140903_154224_0906_visual-reprojected-correctbands", 15),
("20140903_154225_0906_visual-reprojected-correctbands", 15),
("20140903_154226_0906_visual-reprojected-correctbands", 15),
("20140903_154227_0906_visual-reprojected-correctbands", 15),
("20140903_154228_0906_visual-reprojected-correctbands", 15),
("20140903_154229_0906_visual-reprojected-correctbands", 15),
("20140903_154230_0906_visual-reprojected-correctbands", 15),
("20140903_154231_0906_visual-reprojected-correctbands", 15),
("20140903_154232_0906_visual-reprojected-correctbands", 15),
("20140903_154233_0906_visual-reprojected-correctbands", 15),
("20140903_154239_0906_visual-reprojected-correctbands", 15),
("20150424_182320_081f_visual-reprojected-correctbands", 15),
("20150506_134129_0822_visual-reprojected-correctbands", 15),
("20150514_151301_0822_visual-reprojected-correctbands", 15),
("20150514_151311_0822_visual-reprojected-correctbands", 15),
("20150608_160416_0906_visual-reprojected-correctbands", 15),
("20150609_155942_0905_visual-reprojected-correctbands", 15),
("20150609_155943_0905_visual-reprojected-correctbands", 15),
("20150609_155945_0905_visual-reprojected-correctbands", 15),
("20150609_155946_0905_visual-reprojected-correctbands", 15),
("20150609_155947_0905_visual-reprojected-correctbands", 15),
("20150609_155949_0905_visual-reprojected-correctbands", 15),
("20150624_160324_0905_visual-reprojected-correctbands", 15),
("LC80200282014245LGN00_bands_432-reprojected-correctbands", 12),
("LC80210282014156LGN00_RGB-reprojected-correctbands", 12),
("LC80220282015102LGN01_bands_432-reprojected-correctbands", 12)
)
val mapTransforms: Function1[Int, MapKeyTransform] = {
val worldExtent = Extent(-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244)
println(f"WORLD EXTENT: ${worldExtent.xmin}%f ${worldExtent.ymin}%f ${worldExtent.xmax}%f ${worldExtent.ymax}%f")
(1 to 24)
.map { z =>
val layoutCols = math.pow(2, z).toInt
val layoutRows = layoutCols
(z, MapKeyTransform(worldExtent, layoutCols, layoutRows))
}
.toMap
}
def tmsRoute =
pathPrefix(IntNumber / IntNumber / IntNumber) { (zoom, x, y) =>
respondWithMediaType(MediaTypes.`image/png`) {
val paths =
layers
.flatMap { case (layer, maxZoom) =>
val (lz, lx, ly) = {
if(zoom <= maxZoom)
(zoom, x, y)
else {
val d = math.pow(2, zoom - maxZoom)
(maxZoom, (x / d).toInt, (y / d).toInt)
}
}
val path = s"$rootPath/$layer/$lz/$lx/$ly.tiff"
if(!new java.io.File(path).exists) {
None
}
else {
Some((path, lz, lx, ly))
}
}
if(paths.isEmpty) {
reject
} else {
complete {
Future {
paths.foreach(println)
val ordered =
paths.map { case (path, lz, lx, ly) =>
val converted: MultiBandTile = {
val gt = MultiBandGeoTiff(path)
if(zoom == lz)
gt.tile.convert(TypeInt)
else {
val t = gt.tile
val re = RasterExtent(mapTransforms(zoom)(x, y), 256, 256)
val arr = Array.ofDim[Tile](t.bandCount)
cfor(0)(_ < 3, _ + 1) { b =>
arr(b) = t.band(b).convert(TypeInt).map { z => if(isNoData(z)) 128 else z.toByte & 0xFF }.resample(gt.extent, re, Bilinear)
}
ArrayMultiBandTile(arr)
}
}
if(converted.bandCount == 3) {
converted.combine(0, 1, 2) { (r, g, b) =>
if(r == 0 && g == 0 && b == 0) 0
else {
val cr = if(isNoData(r)) 128 else r.toByte & 0xFF
val cg = if(isNoData(g)) 128 else g.toByte & 0xFF
val cb = if(isNoData(b)) 128 else b.toByte & 0xFF
(cr << 24) | (cg << 16) | (cb << 8) | 0xFF
}
}
} else {
converted.combine(0, 1, 2, 3) { (r, g, b, a) =>
if(a != 255.toByte || (r == 0 && g == 0 && b == 0)) {
0
} else {
val cr = if(isNoData(r)) 128 else r.toByte & 0xFF
val cg = if(isNoData(g)) 128 else g.toByte & 0xFF
val cb = if(isNoData(b)) 128 else b.toByte & 0xFF
(cr << 24) | (cg << 16) | (cb << 8) | 0xFF
}
}
}
}
.toArray
val m = ordered(0).mutable
cfor(1)(_ < ordered.size, _ + 1) { i =>
val s = ordered(i)
cfor(0)(_ < 256, _ + 1) { row =>
cfor(0)(_ < 256, _ + 1) { col =>
val v1 = m.get(col, row)
val v2 = s.get(col, row)
if(v1 == 0) {
m.set(col, row, v2)
}
}
}
}
val rgb = m
rgb.renderPng.bytes
}
}
}
}
}
def root = {
pathPrefix("tms") { tmsRoute }
}
startServer(interface = "0.0.0.0", port = 8088) {
root
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment