]> git.street.me.uk Git - andy/viking.git/blob - tools/viking-cache-mbtile.py
Merge branch 'MBTilesRead'
[andy/viking.git] / tools / viking-cache-mbtile.py
1 #!/usr/bin/env python
2 #
3 # Inspired by MBUtils:
4 #  http://github.com/mapbox/mbutil
5 #
6 # Licensed under BSD
7 #
8 import sqlite3, sys, logging, time, os, re
9
10 from optparse import OptionParser
11
12 logger = logging.getLogger(__name__)
13
14 #
15 # Functions from mbutil for sqlite DB format and connections
16 #  utils.py:
17 #
18 def flip_y(zoom, y):
19     return (2**zoom-1) - y
20
21 def mbtiles_setup(cur):
22     cur.execute("""
23         create table tiles (
24             zoom_level integer,
25             tile_column integer,
26             tile_row integer,
27             tile_data blob);
28             """)
29     cur.execute("""create table metadata
30         (name text, value text);""")
31     cur.execute("""create unique index name on metadata (name);""")
32     cur.execute("""create unique index tile_index on tiles
33         (zoom_level, tile_column, tile_row);""")
34
35 def mbtiles_connect(mbtiles_file):
36     try:
37         con = sqlite3.connect(mbtiles_file)
38         return con
39     except Exception, e:
40         logger.error("Could not connect to database")
41         logger.exception(e)
42         sys.exit(1)
43
44 def optimize_connection(cur):
45     cur.execute("""PRAGMA synchronous=0""")
46     cur.execute("""PRAGMA locking_mode=EXCLUSIVE""")
47     cur.execute("""PRAGMA journal_mode=DELETE""")
48
49 def write_database(cur):
50     logger.debug('analyzing db')
51     cur.execute("""ANALYZE;""")
52
53 def optimize_database(cur):
54     logger.debug('cleaning db')
55     cur.execute("""VACUUM;""")
56
57 #
58 # End functions from mbutils
59 #
60
61 # Based on disk_to_mbtiles in mbutil
62 def vikcache_to_mbtiles(directory_path, mbtiles_file, **kwargs):
63     logger.debug("%s --> %s" % (directory_path, mbtiles_file))
64     con = mbtiles_connect(mbtiles_file)
65     cur = con.cursor()
66     optimize_connection(cur)
67     mbtiles_setup(cur)
68     image_format = 'png'
69     count = 0
70     start_time = time.time()
71     msg = ""
72
73     #print ('tileid ' + kwargs.get('tileid'))
74     # Need to split tDddsDdzD
75     #  note zoom level can be negative hence the '-?' term
76     p = re.compile ('^t'+kwargs.get('tileid')+'s(-?\d+)z\d+$')
77     for ff in os.listdir(directory_path):
78         # Find only dirs related to this tileset
79         m = p.match(ff);
80         if m:
81             s = p.split(ff)
82             if len(s) > 2:
83                 #print s[1]
84                 # For some reason Viking does '17-zoom level' - so need to reverse that
85                 z = 17 - int(s[1])
86                 #print z
87                 for r2, xs, ignore in os.walk(os.path.join(directory_path, ff)):
88                     for x in xs:
89                         #print('x:'+directory_path+'/'+ff+'/'+x)
90                         for r3, ignore, ys in os.walk(os.path.join(directory_path, ff, x)):
91                             for y in ys:
92                                 #print('tile:'+directory_path+'/'+ff+'/'+x+'/'+y)
93                                 # Sometimes have random tmp files left around so skip over these
94                                 if "tmp" in y.lower():
95                                     continue
96                                 f = open(os.path.join(directory_path, ff, x, y), 'rb')
97                                 # Viking in xyz so always flip
98                                 y = flip_y(int(z), int(y))
99                                 cur.execute("""insert into tiles (zoom_level,
100                                              tile_column, tile_row, tile_data) values
101                                              (?, ?, ?, ?);""",
102                                             (z, x, y, sqlite3.Binary(f.read())))
103                                 f.close()
104                                 count = count + 1
105                                 if (count % 100) == 0:
106                                     for c in msg: sys.stdout.write(chr(8))
107                                     msg = "%s tiles inserted (%d tiles/sec)" % (count, count / (time.time() - start_time))
108                                     sys.stdout.write(msg)
109
110     msg = "\nTotal tiles inserted %s \n" %(count)
111     sys.stdout.write(msg)
112     write_database(cur)
113     if not kwargs.get('nooptimize'):
114         sys.stdout.write("Optimizing...\n")
115         optimize_database(con)
116     return
117
118 ##
119 ## Start of code here
120 ##
121 parser = OptionParser(usage="""usage: %prog [options] in-map-cache-directory-root  out-file.mbtile
122     
123 Example:
124     
125 Export Viking's cache files of a map type to an mbtiles file:
126 $ viking-cache-mbtile.py -t 17 ~/.viking-maps OSM_Cycle.mbtiles
127     
128 Import from an MB Tiles file into Viking's cache file layout is not available [yet]
129
130 Note you can use the http://github.com/mapbox/mbutil mbutil script to further handle .mbtiles
131 such as converting it into an OSM tile layout and then pointing a new Viking Map at that location with the map type of 'On Disk OSM Layout'""")
132    
133 parser.add_option('-t', '--tileid', dest='tileid',
134     action="store",
135     help='''Tile id of Viking map cache to use (19 if not specified as this is Viking's default (MaqQuest))''',
136     type='string',
137     default='19')
138
139 parser.add_option('-n', '--nooptimize', dest='nooptimize',
140     action="store_true",
141     help='''Do not attempt to optimize the mbtiles output file''',
142     default=False)
143
144 (options, args) = parser.parse_args()
145
146 if len(args) != 2:
147     parser.print_help()
148     sys.exit(1)
149
150 if not os.path.isdir(args[0]):
151     sys.stderr.write('Viking Map Cache directory not specified\n')
152     sys.exit(1)
153
154 if os.path.isfile(args[1]):
155     sys.stderr.write('Output file already exists!\n')
156     sys.exit(1)
157
158 # to mbtiles
159 if os.path.isdir(args[0]) and not os.path.isfile(args[0]):
160     directory_path, mbtiles_file = args
161     vikcache_to_mbtiles(directory_path, mbtiles_file, **options.__dict__)