-
Notifications
You must be signed in to change notification settings - Fork 12
/
gdal2tiles.py
245 lines (212 loc) · 9.06 KB
/
gdal2tiles.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#!/usr/bin/env python
#
# MapBox tile generator written by
# Tom MacWright <macwright [-at-] gmail.com>, based on
# gdal2tiles.py, whose license and author are noted below
#
###############################################################################
# Project: Google Summer of Code 2007
# Purpose: Convert a raster into TMS tiles, create KML SuperOverlay EPSG:4326,
# generate a simple HTML viewers based on Google Maps and OpenLayers
# Author: Klokan Petr Pridal, klokan at klokan dot cz
# Web: http://www.klokan.cz/projects/gdal2tiles/
###############################################################################
# Copyright (c) 2007, Klokan Petr Pridal
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
###############################################################################
from osgeo import gdal
import sys, os
from osgeo.gdalconst import GA_ReadOnly
from osgeo.osr import SpatialReference
from math import ceil, log10
from optparse import OptionParser
import operator
import sqlite3
tilesize = 256
tileformat = 'jpeg'
tempdriver = gdal.GetDriverByName('MEM')
tiledriver = gdal.GetDriverByName(tileformat)
def writemb(index, data, dxsize, dysize, bands, mb_db):
"""
Write raster 'data' (of the size 'dataxsize' x 'dataysize') read from
'dataset' into the mbtiles document 'mb_db' with size 'tilesize' pixels.
Later this should be replaced by new <TMS Tile Raster Driver> from GDAL.
"""
if bands == 3 and tileformat == 'png':
tmp = tempdriver.Create('', tilesize, tilesize, bands=4)
alpha = tmp.GetRasterBand(4)
#from Numeric import zeros
alphaarray = (zeros((dysize, dxsize)) + 255).astype('b')
alpha.WriteArray( alphaarray, 0, tilesize-dysize )
else:
tmp = tempdriver.Create('', tilesize, tilesize, bands=bands)
tmp.WriteRaster( 0, tilesize-dysize, dxsize, dysize, data, band_list=range(1, bands+1))
tiledriver.CreateCopy('tmp.png', tmp, strict=0)
query = """insert into tiles
(zoom_level, tile_column, tile_row, tile_data)
values (%d, %d, %d, ?)""" % (index[0], index[1], index[2])
cur = mb_db.cursor()
d = open('tmp.png', 'rb').read()
cur.execute(query, (sqlite3.Binary(d),))
mb_db.commit()
cur.close()
return 0
def init_db(db_filename, metadata):
if os.path.isfile(db_filename):
raise Exception('Output file already exists')
mb_db = sqlite3.connect(db_filename)
mb_db.execute("""
CREATE TABLE tiles (
zoom_level integer,
tile_column integer,
tile_row integer,
tile_data blob);
""")
mb_db.execute("""
CREATE UNIQUE INDEX tile_index on tiles
(zoom_level, tile_column, tile_row);
""")
mb_db.execute("""
CREATE TABLE "metadata" (
"name" TEXT ,
"value" TEXT );
""")
mb_db.execute("""
CREATE UNIQUE INDEX "name" ON "metadata"
("name");
""")
mb_db.executemany("""
INSERT INTO metadata (name, value) values
(?, ?)""", metadata)
mb_db.commit()
return mb_db
if __name__ == '__main__':
parser = OptionParser("%prog usage: %prog [input_file] [output_file]")
parser.add_option('-n', '--name', dest='name', help='Name')
parser.add_option('-d', '--description', dest='description', help='Description')
parser.add_option('-v', '--verbose', dest='verbose', help='Verbose', action='store_true')
parser.add_option('-r', '--version', dest='version', help='Version', default='1.0')
parser.add_option('-o', '--overlay', action='store_true',
dest='overlay', default=False, help='Overlay')
(options, args) = parser.parse_args()
try:
input_file = args[0]
db_filename = args[1]
except IndexError, e:
raise Exception('Input and Output file arguments are required')
profile = 'local' # later there should be support for TMS global profiles
isepsg4326 = False
gdal.AllRegister()
# Set correct default values.
if not options.name:
options.name = os.path.basename(input_file)
dataset = gdal.Open(input_file, GA_ReadOnly)
if dataset is None:
parser.usage()
bands = dataset.RasterCount
if bands == 3 and tileformat == 'png':
from Numeric import zeros
xsize = dataset.RasterXSize
ysize = dataset.RasterYSize
geotransform = dataset.GetGeoTransform()
projection = dataset.GetProjection()
north = geotransform[3]
south = geotransform[3] + geotransform[5] * ysize
east = geotransform[0] + geotransform[1] * xsize
west = geotransform[0]
if options.verbose:
print "Input (%s):" % input_file
print "="*80
print " Driver:", dataset.GetDriver().ShortName,'/', dataset.GetDriver().LongName
print " Size:", xsize, 'x', ysize, 'x', bands
print " Projection:", projection
print " NSEW: ", (north, south, east, west)
if projection and projection.endswith('AUTHORITY["EPSG","4326"]]'):
isepsg4326 = True
if options.verbose:
print "Projection detected as EPSG:4326"
# Python 2.2 compatibility.
log2 = lambda x: log10(x) / log10(2) # log2 (base 2 logarithm)
sum = lambda seq, start=0: reduce(operator.add, seq, start)
# Zoom levels of the pyramid.
maxzoom = int(max(ceil(log2(xsize/float(tilesize))), ceil(log2(ysize/float(tilesize)))))
zoompixels = [geotransform[1] * 2.0**(maxzoom-zoom) for zoom in range(0, maxzoom+1)]
tilecount = sum([
int(ceil(xsize / (2.0**(maxzoom-zoom)*tilesize))) * \
int(ceil(ysize / (2.0**(maxzoom-zoom)*tilesize))) \
for zoom in range(maxzoom+1)
])
if options.verbose:
print "Output (%s):" % db_filename
print "="*80
print " Format of tiles:", tiledriver.ShortName, '/', tiledriver.LongName
print " Size of a tile:", tilesize, 'x', tilesize, 'pixels'
print " Count of tiles:", tilecount
print " Zoom levels of the pyramid:", maxzoom
print " Pixel resolution by zoomlevels:", zoompixels
tileno = 0
progress = 0
layer_type = "overlay" if options.overlay else "baselayer"
print "Connecting to database %s" % db_filename
mb_db = init_db(db_filename,
[('name', options.name),
('version', options.version),
('description', options.description),
('type', layer_type)])
for zoom in range(maxzoom, -1, -1):
# Maximal size of read window in pixels.
rmaxsize = 2.0**(maxzoom-zoom)*tilesize
if options.verbose:
print "-"*80
print "Zoom %s - pixel %.20f" % (zoom, zoompixels[zoom]), int(2.0**zoom*tilesize)
print "-"*80
for ix in range(0, int( ceil( xsize / rmaxsize))):
# Read window xsize in pixels.
if ix+1 == int( ceil( xsize / rmaxsize)) and xsize % rmaxsize != 0:
rxsize = int(xsize % rmaxsize)
else:
rxsize = int(rmaxsize)
# Read window left coordinate in pixels.
rx = int(ix * rmaxsize)
for iy in range(0, int(ceil( ysize / rmaxsize))):
# Read window ysize in pixels.
if iy+1 == int(ceil( ysize / rmaxsize)) and ysize % rmaxsize != 0:
rysize = int(ysize % rmaxsize)
else:
rysize = int(rmaxsize)
# Read window top coordinate in pixels.
ry = int(ysize - (iy * rmaxsize)) - rysize
dxsize = int(rxsize/rmaxsize * tilesize)
dysize = int(rysize/rmaxsize * tilesize)
# Show the progress bar.
percent = int(ceil((tileno) / float(tilecount-1) * 100))
while progress <= percent:
if progress % 10 == 0:
sys.stdout.write( "%d" % progress )
sys.stdout.flush()
else:
sys.stdout.write( '.' )
sys.stdout.flush()
progress += 2.5
# Load raster from read window.
data = dataset.ReadRaster(rx, ry, rxsize, rysize, dxsize, dysize)
writemb((zoom, ix, iy), data, dxsize, dysize, bands, mb_db)
tileno += 1
mb_db.close()
# Last \n for the progress bar
print "\nDone creating .mbtiles file. You can now drop this file into a"
print "Maps on a Stick 'Maps' folder or MapBox on iPad folder"