]> git.street.me.uk Git - andy/viking.git/blob - tools/viking-cache-mbtile.py
Add link to HERE Maps.
[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                                 # Skip etag files...
97                                 if "etag" in y.lower():
98                                     continue
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
104                                              (?, ?, ?, ?);""",
105                                             (z, x, y, sqlite3.Binary(f.read())))
106                                 f.close()
107                                 count = count + 1
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)
112
113     msg = "\nTotal tiles inserted %s \n" %(count)
114     sys.stdout.write(msg)
115     write_database(cur)
116     if not kwargs.get('nooptimize'):
117         sys.stdout.write("Optimizing...\n")
118         optimize_database(con)
119     return
120
121 ##
122 ## Start of code here
123 ##
124 parser = OptionParser(usage="""usage: %prog [options] in-map-cache-directory-root  out-file.mbtile
125     
126 Example:
127     
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
130     
131 Import from an MB Tiles file into Viking's cache file layout is not available [yet]
132
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'""")
135    
136 parser.add_option('-t', '--tileid', dest='tileid',
137     action="store",
138     help='''Tile id of Viking map cache to use (19 if not specified as this is Viking's default (MaqQuest))''',
139     type='string',
140     default='19')
141
142 parser.add_option('-n', '--nooptimize', dest='nooptimize',
143     action="store_true",
144     help='''Do not attempt to optimize the mbtiles output file''',
145     default=False)
146
147 (options, args) = parser.parse_args()
148
149 if len(args) != 2:
150     parser.print_help()
151     sys.exit(1)
152
153 if not os.path.isdir(args[0]):
154     sys.stderr.write('Viking Map Cache directory not specified\n')
155     sys.exit(1)
156
157 if os.path.isfile(args[1]):
158     sys.stderr.write('Output file already exists!\n')
159     sys.exit(1)
160
161 # to mbtiles
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__)