Skip to content

Commit 000222b

Browse files
committed
Initial generation of slippy map tiles in WebMercator.
Signed-off-by: Simeon H.K. Fitch <[email protected]>
1 parent 79509df commit 000222b

File tree

8 files changed

+121
-37
lines changed

8 files changed

+121
-37
lines changed

core/src/main/scala/astraea/spark/rasterframes/util/debug/package.scala

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -39,37 +39,6 @@ import org.apache.spark.annotation.Experimental
3939
*/
4040
package object debug {
4141
implicit class RasterFrameWithDebug(val self: RasterFrame) {
42-
/** Create a slippy map directory structure export of raster frame, for debugging purposes only. */
43-
@Experimental
44-
def debugTileExport(dest: URI, zoomLevel: Int = 8): Unit = {
45-
val spark = self.sparkSession
46-
implicit val sc = spark.sparkContext
47-
48-
val tgtCrs = self.crs //WebMercator
49-
50-
val scheme = ZoomedLayoutScheme(tgtCrs)
51-
val mapTransform = scheme
52-
.levelForZoom(zoomLevel)
53-
.layout
54-
.mapTransform
55-
56-
val writer = new HadoopSlippyTileWriter[MultibandTile](dest.toASCIIString, "tiff")({ (key, tile) =>
57-
val extent = mapTransform(key)
58-
val opts = GeoTiffOptions.DEFAULT.mapWhen(_ tile.bands.lengthCompare(3) == 0, _.copy(colorSpace = ColorSpace.RGB))
59-
MultibandGeoTiff(tile, extent, tgtCrs, opts).toByteArray
60-
})
61-
62-
/** If there's a temporal component to the key, we drop it blindly. */
63-
val tlrdd: MultibandTileLayerRDD[SpatialKey] = self.toMultibandTileLayerRDD(self.tileColumns: _*) match {
64-
case Left(spatial) spatial
65-
case Right(origRDD)
66-
val newMD = origRDD.metadata.map(_.spatialKey)
67-
val rdd = origRDD.map { case (k, v) (k.spatialKey, v)}
68-
ContextRDD(rdd, newMD)
69-
}
70-
71-
writer.write(zoomLevel, tlrdd)
72-
}
7342

7443
/** Renders the whole schema with metadata as a JSON string. */
7544
def describeFullSchema: String = {
-951 KB
Binary file not shown.
-945 KB
Binary file not shown.
-957 KB
Binary file not shown.
-955 KB
Binary file not shown.

datasource/src/test/scala/astraea/spark/rasterframes/datasource/geotrellis/GeoTrellisDataSourceSpec.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,6 @@ class GeoTrellisDataSourceSpec
242242

243243
assertEqual(raster.tile, sampleGeoTiff.tile)
244244

245-
rf.debugTileExport(new File("target/slippy2").toURI)
246-
247245
//GeoTiff(raster).write("target/from-split.tiff")
248246
// 774 x 500
249247
}
Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,123 @@
1+
/*
2+
* This software is licensed under the Apache 2 license, quoted below.
3+
*
4+
* Copyright 2018 Astraea. Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
7+
* use this file except in compliance with the License. You may obtain a copy of
8+
* the License at
9+
*
10+
* [http://www.apache.org/licenses/LICENSE-2.0]
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15+
* License for the specific language governing permissions and limitations under
16+
* the License.
17+
*
18+
*
19+
*/
20+
121
package astraea.spark.rasterframes.experimental
222

23+
import geotrellis.util.MethodExtensions
24+
import java.net.URI
25+
26+
import astraea.spark.rasterframes._
27+
import astraea.spark.rasterframes.util._
28+
import geotrellis.proj4.WebMercator
29+
import geotrellis.raster.io.geotiff.tags.codes.ColorSpace
30+
import geotrellis.raster.io.geotiff._
31+
import geotrellis.raster.resample.Bilinear
32+
import geotrellis.raster._
33+
import geotrellis.raster.render.{ColorMap, ColorMaps, ColorRamps}
34+
import geotrellis.spark._
35+
import geotrellis.spark.io.slippy.HadoopSlippyTileWriter
36+
import geotrellis.spark.tiling.ZoomedLayoutScheme
37+
import org.apache.spark.annotation.Experimental
38+
39+
import scala.util.Try
40+
341
/**
4-
*
42+
* Experimental support for exporting a RasterFrame into Slippy map format.
543
*
644
* @since 4/10/18
745
*/
8-
class SlippyExport {
46+
@Experimental
47+
trait SlippyExport extends MethodExtensions[RasterFrame]{
48+
/**
49+
* Export GeoTiff tiles in a slippy map directory structure; for debugging purposes only.
50+
* NB: Temporal components are ignored blindly.
51+
*/
52+
@Experimental
53+
def exportGeoTiffTiles(dest: URI): Unit = {
54+
val spark = self.sparkSession
55+
implicit val sc = spark.sparkContext
956

57+
val tlm = self.tileLayerMetadata.widen
58+
val crs = tlm.crs
59+
val mapTransform = tlm.mapTransform
60+
61+
val writer = new HadoopSlippyTileWriter[MultibandTile](dest.toASCIIString, "tiff")({ (key, tile) =>
62+
val extent = mapTransform(key)
63+
// If we have exactly 3 columns, we assume RGB color space.
64+
val opts = GeoTiffOptions.DEFAULT
65+
.mapWhen(_ tile.bands.lengthCompare(3) == 0,
66+
_.copy(colorSpace = ColorSpace.RGB)
67+
)
68+
MultibandGeoTiff(tile, extent, crs, opts).toByteArray
69+
})
70+
71+
val tlrdd: MultibandTileLayerRDD[SpatialKey] = self.toMultibandTileLayerRDD(self.tileColumns: _*) match {
72+
case Left(spatial) spatial
73+
case Right(origRDD)
74+
val newMD = origRDD.metadata.map(_.spatialKey)
75+
val rdd = origRDD.map { case (k, v) (k.spatialKey, v)}
76+
ContextRDD(rdd, newMD)
77+
}
78+
79+
writer.write(0, tlrdd)
80+
}
81+
82+
/**
83+
* Export tiles as a slippy map. For debugging purposes only.
84+
* NB: Temporal components are ignored blindly.
85+
*/
86+
@Experimental
87+
def exportSlippyMap(dest: URI, colorMap: Option[ColorMap] = None): Unit = {
88+
val spark = self.sparkSession
89+
implicit val sc = spark.sparkContext
90+
91+
val inputRDD: MultibandTileLayerRDD[SpatialKey] = self.toMultibandTileLayerRDD(self.tileColumns: _*) match {
92+
case Left(spatial) spatial
93+
case Right(origRDD)
94+
val newMD = origRDD.metadata.map(_.spatialKey)
95+
val rdd = origRDD.map { case (k, v) (k.spatialKey, v)}
96+
ContextRDD(rdd, newMD)
97+
}
98+
99+
val layoutScheme = ZoomedLayoutScheme(WebMercator, tileSize = 256)
100+
101+
val (zoom, reprojected) = inputRDD.reproject(WebMercator, layoutScheme, Bilinear)
102+
103+
val writer = new HadoopSlippyTileWriter[MultibandTile](dest.toASCIIString, "png")({ (_, tile) =>
104+
val png = if(colorMap.isEmpty && tile.bands.lengthCompare(3) == 0) {
105+
// `Try` below is due to https://github.com/locationtech/geotrellis/issues/2621
106+
tile.mapBands((_, t) Try(t.rescale(0, 255)).getOrElse(t)).renderPng()
107+
}
108+
else {
109+
// Are there other ways to use the other bands?
110+
val selected = tile.bands.head
111+
colorMap.map(m selected.renderPng(m)).getOrElse(selected.renderPng(ColorRamps.greyscale(256)))
112+
}
113+
png.bytes
114+
})
115+
116+
writer.write(zoom, reprojected)
117+
}
10118
}
119+
120+
object SlippyExport {
121+
implicit class RasterFrameHasSlippy(val self: RasterFrame) extends SlippyExport
122+
}
123+

experimental/src/test/scala/astraea/spark/rasterframes/experimental/SlippyExportTest.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ import geotrellis.raster._
2828
import geotrellis.raster.io.geotiff.SinglebandGeoTiff
2929
import org.apache.spark.sql.SparkSession
3030

31-
object Slippy {
31+
object SlippyExportTest {
3232
def main(args: Array[String]): Unit = {
33+
3334
implicit val spark = SparkSession
3435
.builder()
3536
.master("local[*]")
@@ -39,6 +40,7 @@ object Slippy {
3940

4041
val bands: Seq[SinglebandGeoTiff] = for (i 1 to 3) yield {
4142
TestData.readSingleband(s"NAIP-VA-b$i.tiff")
43+
//s"L8-B$i-Elkton-VA.tiff")
4244
}
4345

4446
val mtile = MultibandTile(bands.map(_.tile))
@@ -49,6 +51,8 @@ object Slippy {
4951

5052
val rf = pr.toRF(64, 64)
5153

52-
rf.exportGeoTIFFTiles(new File("target/slippy-tiff").toURI)
54+
rf.exportGeoTiffTiles(new File("target/slippy-tiff").toURI)
55+
56+
rf.exportSlippyMap(new File("target/slippy-png").toURI)
5357
}
5458
}

0 commit comments

Comments
 (0)