11package geoscript.layer
22
33import geoscript.geom.Bounds
4+ import geoscript.style.Fill
5+ import geoscript.style.Stroke
6+ import geoscript.workspace.Directory
7+ import geoscript.workspace.Workspace
8+
9+ import javax.imageio.ImageIO
10+ import java.awt.image.BufferedImage
411
512/**
613 * A TileGenerator
@@ -27,19 +34,95 @@ class TileGenerator {
2734 */
2835 void generate (Map options = [:], TileLayer tileLayer , TileRenderer renderer , int startZoom , int endZoom ) {
2936 boolean missingOnly = options. get(" missingOnly" , false )
37+ Map metatile = options. get(" metatile" , [:])
38+ boolean doMetatiling = metatile && renderer instanceof ImageTileRenderer
3039 (startZoom.. endZoom). each {zoom ->
3140 if (verbose) println " Zoom Level ${ zoom} "
3241 long startTime = System . nanoTime()
3342 TileCursor tileCursor = options. bounds ? tileLayer. tiles(options. bounds, zoom) : tileLayer. tiles(zoom)
34- tileCursor. eachWithIndex { Tile t , int i ->
35- if (verbose) println " ${ i} ). ${ t} "
36- Bounds b = tileLayer. pyramid. bounds(t)
37- if (verbose) println " Bounds${ b} "
38- if (! missingOnly || (missingOnly && ! t. data)) {
39- t. data = renderer. render(b)
40- tileLayer. put(t)
41- } else {
42- if (verbose) println " Already generated!"
43+ if (! doMetatiling) {
44+ tileCursor. eachWithIndex { Tile t , int i ->
45+ if (verbose) println " ${ i} ). ${ t} "
46+ Bounds b = tileLayer. pyramid. bounds(t)
47+ if (verbose) println " Bounds${ b} "
48+ if (! missingOnly || (missingOnly && ! t. data)) {
49+ t. data = renderer. render(b)
50+ tileLayer. put(t)
51+ } else {
52+ if (verbose) println " Already generated!"
53+ }
54+ }
55+ } else {
56+
57+ ImageTileRenderer imageTileRenderer = renderer as ImageTileRenderer
58+
59+ int metaTileWidth = metatile. width
60+ int metaTileHeight = metatile. height
61+ String imageType = metatile. imageType ?: " png"
62+
63+ int metaTileColumns = (int ) (Math . ceil(((float ) tileCursor. width) / ((float ) metaTileWidth)))
64+ int metaTileRows = (int ) (Math . ceil(((float ) tileCursor. height) / ((float ) metaTileHeight)))
65+
66+ int tileWidth = tileLayer. pyramid. tileWidth
67+ int tileHeight = tileLayer. pyramid. tileHeight
68+
69+ Pyramid.Origin origin = tileLayer. pyramid. origin
70+
71+ int tileCounter = 0
72+
73+ (0 .. < metaTileColumns). each { int c ->
74+
75+ int startX = Math . min(tileCursor. maxX, tileCursor. minX + (c * metaTileWidth))
76+ int endX = Math . min(tileCursor. maxX, startX + metaTileWidth)
77+
78+ (0 .. < metaTileRows). each { int r ->
79+
80+ int startY = Math . min(tileCursor. maxY, tileCursor. minY + (r * metaTileHeight))
81+ int endY = Math . min(tileCursor. maxY, startY + metaTileHeight)
82+
83+ TileCursor metaTileCursor = new TileCursor (tileLayer, zoom, startX, startY, endX, endY)
84+ Bounds bounds = metaTileCursor. bounds
85+ if (verbose) println " Metatile ${ c} ${ r} = ${ startX} ,${ startY} - ${ endX} ,${ endY} @ ${ bounds} "
86+
87+ byte [] imageData = imageTileRenderer. render(size : [
88+ width : tileLayer. pyramid. tileWidth * metaTileCursor. width,
89+ height : tileLayer. pyramid. tileHeight * metaTileCursor. height],
90+ bounds)
91+ BufferedImage image = ImageIO . read(new ByteArrayInputStream (imageData))
92+ int imageWidth = image. width
93+ int imageHeight = image. height
94+
95+ (0 .. < (imageWidth / tileLayer. pyramid. tileWidth)). each { int imgX ->
96+ (0 .. < (imageHeight / tileLayer. pyramid. tileHeight)). each { int imgY ->
97+ int imgMinX = (tileWidth * imgX)
98+ // Right
99+ if (origin == Pyramid.Origin . BOTTOM_RIGHT || origin == Pyramid.Origin . TOP_RIGHT ) {
100+ imgMinX = (imageWidth - imgMinX) - tileWidth
101+ }
102+ int imgMinY = (tileHeight * imgY)
103+ // Bottom
104+ if (origin == Pyramid.Origin . BOTTOM_LEFT || origin == Pyramid.Origin . BOTTOM_RIGHT ) {
105+ imgMinY = (imageHeight - imgMinY) - tileHeight
106+ }
107+
108+ long tileX = metaTileCursor. minX + imgX
109+ long tileY = metaTileCursor. minY + imgY
110+ ImageTile t = new ImageTile (zoom, tileX, tileY)
111+ if (verbose) println " ${ tileCounter} ). ${ t} "
112+
113+ if (! missingOnly || (missingOnly && ! t. data)) {
114+ BufferedImage subImage = image. getSubimage(imgMinX, imgMinY, tileWidth - 1 , tileHeight - 1 )
115+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream ()
116+ ImageIO . write(subImage, imageType, byteArrayOutputStream)
117+ t. data = byteArrayOutputStream. toByteArray()
118+ tileLayer. put(new ImageTile (zoom, tileX, tileY, byteArrayOutputStream. toByteArray()))
119+ } else {
120+ if (verbose) println " Already generated!"
121+ }
122+ tileCounter++
123+ }
124+ }
125+ }
43126 }
44127 }
45128 if (verbose) {
@@ -50,4 +133,22 @@ class TileGenerator {
50133 }
51134 }
52135
136+ static void main (String [] args ) {
137+
138+ Workspace workspace = new Directory (" examples/naturalearth" )
139+ Layer countries = workspace. get(" ne_110m_admin_0_countries" )
140+ countries. style = new Fill (" white" ) + new Stroke (" black" )
141+ Layer ocean = workspace. get(" ne_110m_ocean" )
142+ ocean. style = new Fill (" blue" )
143+
144+ File dir = new File (" target/tiles" )
145+ Pyramid pyramid = Pyramid . createGlobalMercatorPyramid(origin : Pyramid.Origin . BOTTOM_LEFT )
146+
147+ TMS tms = new TMS(" world" , " png" , dir, pyramid)
148+ TileRenderer renderer = new ImageTileRenderer (tms, [ocean, countries])
149+ TileGenerator generator = new TileGenerator (verbose : true )
150+ generator. generate(tms, renderer, 0 , 4 , metatile : [width :4 , height : 4 ])
151+
152+ }
153+
53154}
0 commit comments