def tilejson(map_path, layer): try: mbtiles = os.path.join( maps_root, map_path, '{}.mbtiles'.format(layer if layer else 'index')) reader = MBTilesReader(mbtiles) metadata = reader.metadata() tilejson = {} tilejson['tilejson'] = '2.2.0' if 'id' in metadata: tilejson['id'] = metadata['id'] tilejson['bounds'] = [float(x) for x in metadata['bounds'].split(',')] tilejson['center'] = [float(x) for x in metadata['center'].split(',') ] ## Ignored ?? tilejson['maxzoom'] = int(metadata['maxzoom']) tilejson['minzoom'] = int(metadata['minzoom']) tilejson['format'] = 'pbf' tilejson['scheme'] = 'xyz' tilejson['tiles'] = [ server_url('{}{}/mvtiles/{{z}}/{{x}}/{{y}}'.format( map_path, '/{}'.format(layer) if layer else '')) ] tilejson['vector_layers'] = json.loads( metadata['json'])['vector_layers'] return response.json(tilejson) except ExtractionError: pass except (InvalidFormatError, sqlite3.OperationalError): exceptions.abort(404, 'Cannot read tile database') return response.empty(status=204)
def maps(): flatmap_list = [] for tile_dir in pathlib.Path(root_paths['flatmaps']).iterdir(): mbtiles = os.path.join(root_paths['flatmaps'], tile_dir, 'index.mbtiles') if os.path.isdir(tile_dir) and os.path.exists(mbtiles): reader = MBTilesReader(mbtiles) try: source_row = reader._query( "SELECT value FROM metadata WHERE name='source';" ).fetchone() except (InvalidFormatError, sqlite3.OperationalError): flask.abort(404, 'Cannot read tile database: {}'.format(mbtiles)) if source_row is not None: flatmap = {'id': tile_dir.name, 'source': source_row[0]} created = reader._query( "SELECT value FROM metadata WHERE name='created';" ).fetchone() if created is not None: flatmap['created'] = created[0] describes = reader._query( "SELECT value FROM metadata WHERE name='describes';" ).fetchone() if describes is not None and describes[0]: flatmap['describes'] = normalise_identifier(describes[0]) flatmap_list.append(flatmap) return flask.jsonify(flatmap_list)
class TileDatabase(object): def __init__(self, db_file): self._db = MBTilesReader(db_file) def execute(self, sql): return self._db._query(sql) def metadata(self): return self._db.metadata()
def get_metadata(map_path, name): mbtiles = os.path.join(root_paths['flatmaps'], map_path, 'index.mbtiles') reader = MBTilesReader(mbtiles) try: row = reader._query( "SELECT value FROM metadata WHERE name='{}';".format( name)).fetchone() except (InvalidFormatError, sqlite3.OperationalError): flask.abort(404, 'Cannot read tile database') return {} if row is None else json.loads(row[0])
def image_tiles(request, map_path, layer, z, y, x): try: mbtiles = os.path.join(maps_root, map_path, '{}.mbtiles'.format(layer)) reader = MBTilesReader(mbtiles) return response.raw(reader.tile(int(z), int(y), int(x)), headers={'Content-Type': 'image/png'}) except ExtractionError: pass except (InvalidFormatError, sqlite3.OperationalError): exceptions.abort(404, 'Cannot read tile database') return response.raw(blank_tile(), headers={'Content-Type': 'image/png'})
def vector_tiles(map_path, z, y, x): try: mbtiles = os.path.join(root_paths['flatmaps'], map_path, 'index.mbtiles') reader = MBTilesReader(mbtiles) return flask.send_file(io.BytesIO(reader.tile(z, x, y)), mimetype='application/octet-stream') except ExtractionError: pass except (InvalidFormatError, sqlite3.OperationalError): flask.abort(404, 'Cannot read tile database') return flask.make_response('', 204)
def image_tiles(map_path, layer, z, y, x): try: mbtiles = os.path.join(root_paths['flatmaps'], map_path, '{}.mbtiles'.format(layer)) reader = MBTilesReader(mbtiles) return flask.send_file(io.BytesIO(reader.tile(z, x, y)), mimetype='image/png') except ExtractionError: pass except (InvalidFormatError, sqlite3.OperationalError): flask.abort(404, 'Cannot read tile database') return flask.send_file(blank_tile(), mimetype='image/png')
def image_tiles(map_id, layer, z, y, x): try: mbtiles = os.path.join(settings['FLATMAP_ROOT'], map_id, '{}.mbtiles'.format(layer)) reader = MBTilesReader(mbtiles) image_bytes = io.BytesIO(reader.tile(z, x, y)) return send_bytes(image_bytes, 'image/png') except ExtractionError: pass except (InvalidFormatError, sqlite3.OperationalError): flask.abort(404, 'Cannot read tile database') return send_bytes(blank_tile(), 'image/png')
def vector_tiles(map_id, z, y, x): try: mbtiles = os.path.join(settings['FLATMAP_ROOT'], map_id, 'index.mbtiles') tile_reader = MBTilesReader(mbtiles) tile_bytes = tile_reader.tile(z, x, y) if metadata(tile_reader, 'compressed'): tile_bytes = gzip.decompress(tile_bytes) return send_bytes(io.BytesIO(tile_bytes), 'application/octet-stream') except ExtractionError: pass except (InvalidFormatError, sqlite3.OperationalError): flask.abort(404, 'Cannot read tile database') return flask.make_response('', 204)
def vector_tiles(map_path, layer, z, y, x): try: mbtiles = os.path.join(maps_root, map_path, 'index.mbtiles') reader = MBTilesReader(mbtiles) tile = reader.tile(z, x, y) headers = {'Content-Type': 'application/x-protobuf'} if tile[0:2] == b'\x1f\x8b': headers['Content-Encoding'] = 'gzip' return response.raw(tile, headers=headers) except ExtractionError: pass except (InvalidFormatError, sqlite3.OperationalError): exceptions.abort(404, 'Cannot read tile database') return response.empty(status=204)
def __init__(self, name): """ Load a MBTile file. If `name` is a valid filepath, it will load it. Else, it will attempt to load it within `settings.MBTILES_ROOT` folder. """ mbtiles_file = name if not os.path.exists(mbtiles_file): if not os.path.exists(app_settings.MBTILES_ROOT): raise MBTilesFolderError() mbtiles_file = os.path.join(app_settings.MBTILES_ROOT, name) if not os.path.exists(mbtiles_file): mbtiles_file = "%s.%s" % (mbtiles_file, app_settings.MBTILES_EXT) if not os.path.exists(mbtiles_file): raise MBTilesNotFoundError(_("'%s' not found") % mbtiles_file) self.fullpath = mbtiles_file self.basename = os.path.basename(self.fullpath) self._reader = MBTilesReader(self.fullpath, tilesize=app_settings.TILE_SIZE)
def maps(): """ Get a list of available flatmaps. :>jsonarr string id: the flatmap's unique identifier on the server :>jsonarr string source: the map's source URL :>jsonarr string created: when the map was generated :>jsonarr string describes: the map's description """ flatmap_list = [] root_path = pathlib.Path(settings['FLATMAP_ROOT']) if root_path.is_dir(): for tile_dir in root_path.iterdir(): mbtiles = os.path.join(settings['FLATMAP_ROOT'], tile_dir, 'index.mbtiles') if os.path.isdir(tile_dir) and os.path.exists(mbtiles): reader = MBTilesReader(mbtiles) try: source_row = reader._query( "SELECT value FROM metadata WHERE name='source';" ).fetchone() except (InvalidFormatError, sqlite3.OperationalError): flask.abort( 404, 'Cannot read tile database: {}'.format(mbtiles)) if source_row is not None: flatmap = {'id': tile_dir.name, 'source': source_row[0]} created = reader._query( "SELECT value FROM metadata WHERE name='created';" ).fetchone() if created is not None: flatmap['created'] = created[0] describes = reader._query( "SELECT value FROM metadata WHERE name='describes';" ).fetchone() if describes is not None and describes[0]: flatmap['describes'] = normalise_identifier( describes[0]) flatmap_list.append(flatmap) return flask.jsonify(flatmap_list)
def load_mbtiles(self, mbtiles): """ Loads all the downloaded *.mbtiles, but centers on the requested one. """ mbt_merge_manager = MbtMergeManager() not_merged_mbtiles = mbt_merge_manager.not_merged() main_mbtiles_path = App.get_running_app().main_mbtiles_path # not yet merged *.mbtiles plus main one mbtiles_paths = not_merged_mbtiles + [main_mbtiles_path] # requested mbtiles path mbtiles_path = os.path.join(App.get_running_app().mbtiles_directory, mbtiles) map_source = MBTilesCompositeMapSource(mbtiles_paths) self.map_source = map_source mbreader = MBTilesReader(mbtiles_path) # centers on requested loaded mbtiles metadata = mbreader.metadata() if "center" in metadata: center = metadata["center"] longitude, latitude, default_zoom = map(float, center.split(",")) # defaults to the minimum available zoom zoom = int(default_zoom) self.animated_center_on(latitude, longitude, zoom)
def load_mbtiles(self, mbtiles): """ Loads all the downloaded *.mbtiles, but centers on the requested one. """ mbt_merge_manager = MbtMergeManager() not_merged_mbtiles = mbt_merge_manager.not_merged() main_mbtiles_path = App.get_running_app().main_mbtiles_path # not yet merged *.mbtiles plus main one mbtiles_paths = not_merged_mbtiles + [main_mbtiles_path] # requested mbtiles path mbtiles_path = os.path.join( App.get_running_app().mbtiles_directory, mbtiles) map_source = MBTilesCompositeMapSource(mbtiles_paths) self.map_source = map_source mbreader = MBTilesReader(mbtiles_path) # centers on requested loaded mbtiles metadata = mbreader.metadata() if "center" in metadata: center = metadata["center"] longitude, latitude, default_zoom = map(float, center.split(",")) # defaults to the minimum available zoom zoom = int(default_zoom) self.animated_center_on(latitude, longitude, zoom)
def __init__(self, name, catalog=None): self.catalog = catalog self.fullpath = self.objects.fullpath(name, catalog) self.basename = os.path.basename(self.fullpath) self._reader = MBTilesReader(self.fullpath, tilesize=app_settings.TILE_SIZE)
class MBTiles(object): """ Represent a MBTiles file """ objects = MBTilesManager() def __init__(self, name): """ Load a MBTile file. If `name` is a valid filepath, it will load it. Else, it will attempt to load it within `settings.MBTILES_ROOT` folder. """ mbtiles_file = name if not os.path.exists(mbtiles_file): if not os.path.exists(app_settings.MBTILES_ROOT): raise MBTilesFolderError() mbtiles_file = os.path.join(app_settings.MBTILES_ROOT, name) if not os.path.exists(mbtiles_file): mbtiles_file = "%s.%s" % (mbtiles_file, app_settings.MBTILES_EXT) if not os.path.exists(mbtiles_file): raise MBTilesNotFoundError(_("'%s' not found") % mbtiles_file) self.fullpath = mbtiles_file self.basename = os.path.basename(self.fullpath) self._reader = MBTilesReader(self.fullpath, tilesize=app_settings.TILE_SIZE) @property def id(self): iD, ext = os.path.splitext(self.basename) return iD @property def name(self): return self.metadata.get('name', self.id) @property def filesize(self): return os.path.getsize(self.fullpath) @reify def metadata(self): return self._reader.metadata() @reify def bounds(self): bounds = self.metadata.get('bounds', '').split(',') if len(bounds) != 4: logger.warning(_("Invalid bounds metadata in '%s', fallback to whole world.") % self.name) bounds = [-180,-90,180,90] return tuple(map(float, bounds)) @reify def center(self): """ Return the center (x,y) of the map at this zoom level. """ center = self.metadata.get('center', '').split(',') if len(center) == 3: lon, lat, zoom = map(float, center) zoom = int(zoom) if zoom not in self.zoomlevels: logger.warning(_("Invalid zoom level (%s), fallback to middle zoom (%s)") % (zoom, self.middlezoom)) zoom = self.middlezoom return (lon, lat, zoom) # Invalid center from metadata, guess center from bounds lat = self.bounds[1] + (self.bounds[3] - self.bounds[1])/2 lon = self.bounds[0] + (self.bounds[2] - self.bounds[0])/2 return (lon, lat, self.middlezoom) @property def minzoom(self): z = self.metadata.get('minzoom', self.zoomlevels[0]) return int(z) @property def maxzoom(self): z = self.metadata.get('maxzoom', self.zoomlevels[-1]) return int(z) @property def middlezoom(self): return self.zoomlevels[len(self.zoomlevels)/2] @reify def zoomlevels(self): return self._reader.zoomlevels() def tile(self, z, x, y): try: return self._reader.tile(z, x, y) except ExtractionError: raise MissingTileError def center_tile(self): lon, lat, zoom = self.center proj = GoogleProjection(app_settings.TILE_SIZE, [zoom]) return proj.tile_at(zoom, (lon, lat)) def grid(self, z, x, y, callback=None): try: return self._reader.grid(z, x, y, callback) except ExtractionError: raise MissingTileError def jsonp(self, request, callback): # Raw metadata jsonp = dict(self.metadata) # Post-processed metadata jsonp.update(**{ "bounds": self.bounds, "center": self.center, "minzoom": self.minzoom, "maxzoom": self.maxzoom, }) # Additionnal info tilepattern = reverse("mbtilesmap:tile", kwargs=dict(name=self.id, x='{x}',y='{y}',z='{z}')) tilepattern = request.build_absolute_uri(tilepattern) tilepattern = tilepattern.replace('%7B', '{').replace('%7D', '}') jsonp.update(**{ "id": self.id, "name": self.name, "scheme": "xyz", "basename": self.basename, "filesize": self.filesize, "tiles": [tilepattern], }) return '%s(%s);' % (callback, json.dumps(jsonp))
class MBTiles(object): """ Represent a MBTiles file """ objects = MBTilesManager() def __init__(self, name, catalog=None): self.catalog = catalog self.fullpath = self.objects.fullpath(name, catalog) self.basename = os.path.basename(self.fullpath) self._reader = MBTilesReader(self.fullpath, tilesize=app_settings.TILE_SIZE) @property def id(self): iD, ext = os.path.splitext(self.basename) return iD @property def name(self): return self.metadata.get('name', self.id) @property def filesize(self): return os.path.getsize(self.fullpath) @reify def metadata(self): return self._reader.metadata() @reify def bounds(self): bounds = self.metadata.get('bounds', '').split(',') if len(bounds) != 4: logger.warning(_("Invalid bounds metadata in '%s', fallback to whole world.") % self.name) bounds = [-180,-90,180,90] return tuple(map(float, bounds)) @reify def center(self): """ Return the center (x,y) of the map at this zoom level. """ center = self.metadata.get('center', '').split(',') if len(center) == 3: lon, lat, zoom = map(float, center) zoom = int(zoom) if zoom not in self.zoomlevels: logger.warning(_("Invalid zoom level (%s), fallback to middle zoom (%s)") % (zoom, self.middlezoom)) zoom = self.middlezoom return (lon, lat, zoom) # Invalid center from metadata, guess center from bounds lat = self.bounds[1] + (self.bounds[3] - self.bounds[1])/2 lon = self.bounds[0] + (self.bounds[2] - self.bounds[0])/2 return (lon, lat, self.middlezoom) @property def minzoom(self): z = self.metadata.get('minzoom', self.zoomlevels[0]) return int(z) @property def maxzoom(self): z = self.metadata.get('maxzoom', self.zoomlevels[-1]) return int(z) @property def middlezoom(self): return self.zoomlevels[len(self.zoomlevels)/2] @reify def zoomlevels(self): return self._reader.zoomlevels() def tile(self, z, x, y): try: return self._reader.tile(z, x, y) except ExtractionError: raise MissingTileError def center_tile(self): lon, lat, zoom = self.center proj = GoogleProjection(app_settings.TILE_SIZE, [zoom]) return proj.tile_at(zoom, (lon, lat)) def grid(self, z, x, y, callback=None): try: return self._reader.grid(z, x, y, callback) except ExtractionError: raise MissingTileError def tilejson(self, request): # Raw metadata jsonp = dict(self.metadata) # Post-processed metadata jsonp.update(**{ "bounds": self.bounds, "center": self.center, "minzoom": self.minzoom, "maxzoom": self.maxzoom, }) # Additionnal info try: kwargs = dict(name=self.id, x='{x}',y='{y}',z='{z}') if self.catalog: kwargs['catalog'] = self.catalog tilepattern = reverse("mbtilesmap:tile", kwargs=kwargs) gridpattern = reverse("mbtilesmap:grid", kwargs=kwargs) except NoReverseMatch: # In case django-mbtiles was not registered in namespace mbtilesmap tilepattern = reverse("tile", kwargs=dict(name=self.id, x='{x}',y='{y}',z='{z}')) gridpattern = reverse("grid", kwargs=dict(name=self.id, x='{x}',y='{y}',z='{z}')) tilepattern = request.build_absolute_uri(tilepattern) gridpattern = request.build_absolute_uri(gridpattern) tilepattern = tilepattern.replace('%7B', '{').replace('%7D', '}') gridpattern = gridpattern.replace('%7B', '{').replace('%7D', '}') jsonp.update(**{ "tilejson": "2.0.1", "id": self.id, "name": self.name, "scheme": "xyz", "basename": self.basename, "filesize": self.filesize, "tiles": [tilepattern], "grids": [gridpattern] }) return json.dumps(jsonp)
def __init__(self, db_file): self._db = MBTilesReader(db_file)
def get_metadata(map_id, name): mbtiles = os.path.join(settings['FLATMAP_ROOT'], map_id, 'index.mbtiles') return metadata(MBTilesReader(mbtiles), name)