4 # http://github.com/mapbox/mbutil
8 import sqlite3, sys, logging, time, os, re
10 from optparse import OptionParser
12 logger = logging.getLogger(__name__)
15 # Functions from mbutil for sqlite DB format and connections
19 return (2**zoom-1) - y
21 def mbtiles_setup(cur):
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);""")
35 def mbtiles_connect(mbtiles_file):
37 con = sqlite3.connect(mbtiles_file)
40 logger.error("Could not connect to database")
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""")
49 def write_database(cur):
50 logger.debug('analyzing db')
51 cur.execute("""ANALYZE;""")
53 def optimize_database(cur):
54 logger.debug('cleaning db')
55 cur.execute("""VACUUM;""")
58 # End functions from mbutils
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)
66 optimize_connection(cur)
70 start_time = time.time()
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
84 # For some reason Viking does '17-zoom level' - so need to reverse that
87 for r2, xs, ignore in os.walk(os.path.join(directory_path, ff)):
89 #print('x:'+directory_path+'/'+ff+'/'+x)
90 for r3, ignore, ys in os.walk(os.path.join(directory_path, ff, x)):
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():
97 if "etag" in y.lower():
99 f = open(os.path.join(directory_path, ff, x, y), 'rb')
100 # Viking in xyz so always flip
101 y = flip_y(int(z), int(y))
102 cur.execute("""insert into tiles (zoom_level,
103 tile_column, tile_row, tile_data) values
105 (z, x, y, sqlite3.Binary(f.read())))
108 if (count % 100) == 0:
109 for c in msg: sys.stdout.write(chr(8))
110 msg = "%s tiles inserted (%d tiles/sec)" % (count, count / (time.time() - start_time))
111 sys.stdout.write(msg)
113 msg = "\nTotal tiles inserted %s \n" %(count)
114 sys.stdout.write(msg)
116 if not kwargs.get('nooptimize'):
117 sys.stdout.write("Optimizing...\n")
118 optimize_database(con)
122 ## Start of code here
124 parser = OptionParser(usage="""usage: %prog [options] in-map-cache-directory-root out-file.mbtile
128 Export Viking's cache files of a map type to an mbtiles file:
129 $ viking-cache-mbtile.py -t 17 ~/.viking-maps OSM_Cycle.mbtiles
131 Import from an MB Tiles file into Viking's cache file layout is not available [yet]
133 Note you can use the http://github.com/mapbox/mbutil mbutil script to further handle .mbtiles
134 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'""")
136 parser.add_option('-t', '--tileid', dest='tileid',
138 help='''Tile id of Viking map cache to use (19 if not specified as this is Viking's default (MaqQuest))''',
142 parser.add_option('-n', '--nooptimize', dest='nooptimize',
144 help='''Do not attempt to optimize the mbtiles output file''',
147 (options, args) = parser.parse_args()
153 if not os.path.isdir(args[0]):
154 sys.stderr.write('Viking Map Cache directory not specified\n')
157 if os.path.isfile(args[1]):
158 sys.stderr.write('Output file already exists!\n')
162 if os.path.isdir(args[0]) and not os.path.isfile(args[0]):
163 directory_path, mbtiles_file = args
164 vikcache_to_mbtiles(directory_path, mbtiles_file, **options.__dict__)