def getTile(layer, extension, x, y, z, cfg): contenttype, content = TileStache.getTile(cfg.layers[layer], ModestMaps.Core.Coordinate( int(x), int(y), int(z)), extension, ignore_cached=True) return content
def getTile(layer,extension,x,y,z,config): cfg = TileStache.Config.buildConfiguration(config) contenttype, content = TileStache.getTile(cfg.layers[layer], ModestMaps.Core.Coordinate(int(x), int(y), int(z)),extension,ignore_cached=True) handle, filename = mkstemp(prefix='tile-', suffix='.'+extension) os.write(handle, content) os.close(handle) return filename
def tiles(request, service_slug, z, x, y, extension): """ Proxy to tilestache {X} - coordinate column. {Y} - coordinate row. {B} - bounding box. {Z} - zoom level. {S} - host. """ service = TileService.objects.get(slug=service_slug) config = { "cache": {"name": "Test"}, "layers": {} } config["layers"][service_slug]={ "provider": { 'name': 'mapnik', "mapfile": service.mapfile } } config = TileStache.Config.buildConfiguration(config) path_info = "%s/%s/%s/%s.%s" % (service_slug, z, x, y, extension) coord, extension = TileStache.splitPathInfo(path_info)[1:] mimetype, content = TileStache.getTile(config.layers[service_slug], coord, extension) return HttpResponse(content, mimetype=mimetype)
def tiles(layer, x, y, z): if not tileconfig.layers.has_key(layer): return abort(404) coord = ModestMaps.Core.Coordinate(y, x, z) type, bytes = TileStache.getTile(tileconfig.layers[layer], coord, 'png') buf = BytesIO(bytes) return send_file(buf, mimetype=type)
def get_url_tile(config, url, row, col, zoom, ext): ''' ''' layer = config.layers[url] coord = ModestMaps.Core.Coordinate(row, col, zoom) # (1582, 656, 12) mime, body = TileStache.getTile(layer, coord, ext) return (mime, body)
def getTileUrls(self, coord): """ Return tile URLs that start with file://, by first retrieving them. """ if self.threadsafe or self.lock.acquire(): mime_type, tile_data = TileStache.getTile(self.layer, coord, 'png', self.ignore_cached) handle, filename = mkstemp(prefix='tilestache-compose-', suffix='.png') write(handle, tile_data) close(handle) self.files.append(filename) if not self.threadsafe: # must be locked, right? self.lock.release() if self.verbose: size = len(tile_data) / 1024. printlocked( self.lock, self.layer.name() + '/%(zoom)d/%(column)d/%(row)d.png' % coord.__dict__, '(%dKB)' % size) return ('file://' + abspath(filename), )
def wms(layer,x,y,z): if not tileconfig.layers.has_key(layer): return abort(404) coord = ModestMaps.Core.Coordinate(y,x,z) type, bytes = TileStache.getTile(tileconfig.layers[layer], coord, 'png') buf = BytesIO(bytes) return send_file(buf, mimetype=type)
def seed_resource_cache(): zooms = range(settings.CACHE_SEED_MAX_ZOOM + 1) extension = 'pbf' lat1, lon1, lat2, lon2 = settings.CACHE_SEED_BOUNDS south, west = min(lat1, lat2), min(lon1, lon2) north, east = max(lat1, lat2), max(lon1, lon2) northwest = Location(north, west) southeast = Location(south, east) padding = 0 datatypes = [ d.pk for d in models.DDataType.objects.filter(isgeometric=True) ] nodes = models.Node.objects.filter(graph__isresource=True, datatype__in=datatypes) for node in nodes: datatype = datatype_factory.get_instance(node.datatype) count = models.TileModel.objects.filter( data__has_key=str(node.nodeid)).count() if datatype.should_cache(node) and count > 0: config = TileStache.parseConfig(get_tileserver_config(node.nodeid)) layer = config.layers[str(node.nodeid)] ul = layer.projection.locationCoordinate(northwest) lr = layer.projection.locationCoordinate(southeast) coordinates = generateCoordinates(ul, lr, zooms, padding) for (offset, count, coord) in coordinates: path = '%s/%d/%d/%d.%s' % (layer.name(), coord.zoom, coord.column, coord.row, extension) progress = {"tile": path, "offset": offset + 1, "total": count} attempts = 3 rendered = False while not rendered: print '%(offset)d of %(total)d...' % progress, try: mimetype, content = TileStache.getTile( layer, coord, extension, True) except: attempts -= 1 print 'Failed %s, will try %s more.' % ( progress['tile'], ['no', 'once', 'twice' ][attempts]) if attempts == 0: print 'Failed %(zoom)d/%(column)d/%(row)d, trying next tile.\n' % coord.__dict__ break else: rendered = True progress['size'] = '%dKB' % (len(content) / 1024) print '%(tile)s (%(size)s)' % progress
def seed_resource_cache(): datatype_factory = DataTypeFactory() zooms = range(settings.CACHE_SEED_MAX_ZOOM + 1) extension = 'pbf' lat1, lon1, lat2, lon2 = GeoUtils().get_bounds_from_geojson(settings.CACHE_SEED_BOUNDS) south, west = min(lat1, lat2), min(lon1, lon2) north, east = max(lat1, lat2), max(lon1, lon2) northwest = Location(north, west) southeast = Location(south, east) padding = 0 datatypes = [ d.pk for d in models.DDataType.objects.filter(isgeometric=True)] nodes = models.Node.objects.filter( graph__isresource=True, datatype__in=datatypes) for node in nodes: datatype = datatype_factory.get_instance(node.datatype) count = models.TileModel.objects.filter( data__has_key=str(node.nodeid)).count() if datatype.should_cache(node) and count > 0: config = TileStache.parseConfig(get_tileserver_config(node.nodeid)) layer = config.layers[str(node.nodeid)] ul = layer.projection.locationCoordinate(northwest) lr = layer.projection.locationCoordinate(southeast) coordinates = generateCoordinates(ul, lr, zooms, padding) for (offset, count, coord) in coordinates: path = '%s/%d/%d/%d.%s' % (layer.name(), coord.zoom, coord.column, coord.row, extension) progress = {"tile": path, "offset": offset + 1, "total": count} attempts = 3 rendered = False while not rendered: print '%(offset)d of %(total)d...' % progress, try: mimetype, content = TileStache.getTile( layer, coord, extension, True) except: attempts -= 1 print 'Failed %s, will try %s more.' % (progress['tile'], ['no', 'once', 'twice'][attempts]) if attempts == 0: print 'Failed %(zoom)d/%(column)d/%(row)d, trying next tile.\n' % coord.__dict__ break else: rendered = True progress['size'] = '%dKB' % (len(content) / 1024) print '%(tile)s (%(size)s)' % progress
def addLayer(self, layerDef, coord): layer = TileStache.getTile(self.layer.config.layers[layerDef['src']], coord, 'JSON')[1] # raise KnownUnknown(layer) if layerDef['wrapper'] == None: layer = json.loads(layer) else: layer = json.loads(layer[(len(layerDef['wrapper']) + 1):-1]) #Strip "Wrapper(...)" gridSize = len(layer['grid']) #init resultGrid based on given layers (if required) if len(self.resultGrid) == 0: for i in xrange(gridSize): self.resultGrid.append([]) for j in xrange(gridSize): self.resultGrid[i].append(-1) keys = layer['keys'] keyRemap = {} for k in keys: if k in self.gridKeys: for ext in xrange(ord('a'), ord('z') + 1): if not k + chr(ext) in self.gridKeys: keyRemap[k] = (k + chr(ext)) break if not k in keyRemap: raise Error("Couldn't remap") addedKeys = [] #FIXME: HashSet<string>? for y in xrange(gridSize): line = layer['grid'][y] for x in xrange(gridSize): idNo = self.decodeId(line[x]) if keys[idNo] == "": continue key = keys[idNo] if keys[idNo] in keyRemap: key = keyRemap[keys[idNo]] if not key in addedKeys: self.gridKeys.append(key) addedKeys.append(key) if layerDef[ 'layer_id'] != None and self.layer_id != None: #Add layer name attribute layer['data'][keys[idNo]][ self.layer_id] = layerDef['layer_id'] self.gridData[key] = layer['data'][keys[idNo]] newId = self.gridKeys.index(key) self.resultGrid[x][y] = newId
def addLayer( self, layerDef, coord ): _, _, layer = TileStache.getTile(self.layer.config.layers[layerDef['src']], coord, 'JSON')[1] # raise KnownUnknown(layer) if layerDef['wrapper'] == None: layer = json.loads(layer) else: layer = json.loads(layer[(len(layerDef['wrapper'])+1):-1]) #Strip "Wrapper(...)" gridSize = len(layer['grid']) #init resultGrid based on given layers (if required) if len(self.resultGrid) == 0: for i in xrange(gridSize): self.resultGrid.append([]) for j in xrange(gridSize): self.resultGrid[i].append(-1) keys = layer['keys'] keyRemap = {} for k in keys: if k in self.gridKeys: for ext in xrange(ord('a'), ord('z')+1): if not k+chr(ext) in self.gridKeys: keyRemap[k] = (k+chr(ext)) break if not k in keyRemap: raise Error("Couldn't remap") addedKeys = [] #FIXME: HashSet<string>? for y in xrange(gridSize): line = layer['grid'][y] for x in xrange(gridSize): idNo = self.decodeId(line[x]) if keys[idNo] == "": continue key = keys[idNo] if keys[idNo] in keyRemap: key = keyRemap[keys[idNo]] if not key in addedKeys: self.gridKeys.append(key) addedKeys.append(key) if layerDef['layer_id'] != None and self.layer_id != None: #Add layer name attribute layer['data'][keys[idNo]][self.layer_id] = layerDef['layer_id'] self.gridData[key] = layer['data'][keys[idNo]] newId = self.gridKeys.index(key) self.resultGrid[x][y] = newId
def addLayer(self, layerDef, coord): layer = TileStache.getTile(self.layer.config.layers[layerDef['src']], coord, 'JSON')[1] if layerDef['wrapper'] == None: layer = json.loads(layer) else: # Strip "Wrapper(...)" layer = json.loads(layer[(len(layerDef['wrapper']) + 1):-1]) grid_size = len(layer['grid']) # Init resultGrid based on given layers (if required) if len(self.resultGrid) == 0: for i in range(grid_size): self.resultGrid.append([]) for j in range(grid_size): self.resultGrid[i].append(-1) layer_keys = layer['keys'] for y in range(grid_size): line = layer['grid'][y] for x in range(grid_size): src_id = self.decodeId(line[x]) if layer_keys[src_id] == "": continue src_key = layer_keys[src_id] # Add layer name attribute if layerDef['layer_id'] != None and self.layer_id != None: layer['data'][src_key][ self.layer_id] = layerDef['layer_id'] if self.resultGrid[x][y] == -1: cur_id = self.curId self.curId += 1 cur_key = json.dumps(cur_id) # Set key for current point. self.resultGrid[x][y] = self.encodeId(cur_id) self.gridKeys.insert(cur_id + 1, cur_key) # Initialize data bucket. self.gridData[cur_key] = [] else: cur_id = self.decodeId(self.resultGrid[x][y]) cur_key = json.dumps(cur_id) self.gridData[cur_key].append(layer['data'][src_key])
def addLayer( self, layerDef, coord ): layer = TileStache.getTile(self.layer.config.layers[layerDef['src']], coord, 'JSON')[1] if layerDef['wrapper'] == None: layer = json.loads(layer) else: # Strip "Wrapper(...)" layer = json.loads(layer[(len(layerDef['wrapper'])+1):-1]) grid_size = len(layer['grid']) # Init resultGrid based on given layers (if required) if len(self.resultGrid) == 0: for i in xrange(grid_size): self.resultGrid.append([]) for j in xrange(grid_size): self.resultGrid[i].append(-1) layer_keys = layer['keys'] for y in xrange(grid_size): line = layer['grid'][y] for x in xrange(grid_size): src_id = self.decodeId(line[x]) if layer_keys[src_id] == "": continue src_key = layer_keys[src_id] # Add layer name attribute if layerDef['layer_id'] != None and self.layer_id != None: layer['data'][src_key][self.layer_id] = layerDef['layer_id'] if self.resultGrid[x][y] == -1: cur_id = self.curId self.curId += 1 cur_key = json.dumps(cur_id) # Set key for current point. self.resultGrid[x][y] = self.encodeId(cur_id) self.gridKeys.insert(cur_id + 1, cur_key) # Initialize data bucket. self.gridData[cur_key] = [] else: cur_id = self.decodeId(self.resultGrid[x][y]) cur_key = json.dumps(cur_id) self.gridData[cur_key].append(layer['data'][src_key])
def tilestache(request, layer_name, z, x, y, extension): """ Proxy to tilestache {X} - coordinate column. {Y} - coordinate row. {B} - bounding box. {Z} - zoom level. {S} - host. """ config = get_config() path_info = "%s/%s/%s/%s.%s" % (layer_name, z, x, y, extension) coord, extension = TileStache.splitPathInfo(path_info)[1:] mimetype, content = TileStache.getTile(config.layers[layer_name], coord, extension) return HttpResponse(content, mimetype=mimetype)
def test_tilestache_lib(self): config = eval(open(get_tilestache_file('tilestache.cfg')).read()) config["layers"]["example"]["provider"]["mapfile"] = "style_sheet.xml" # like http://tile.openstreetmap.org/1/0/0.png coord = ModestMaps.Core.Coordinate(0, 0, 0) config = TileStache.Config.buildConfiguration(config) mime_type, image_bytes = TileStache.getTile(config.layers['example'], coord, 'png') self.assertEquals(mime_type, 'image/png') open(actual_image, 'w').write(image_bytes) with open(actual_image) as actual, open(expected_image) as expected: actual_read = actual.read() self.assertEquals(actual_read, expected.read()) img = Image.open(StringIO(actual_read)) self.assertEquals(img.size, (256, 256))
def getTile(layer,extension,x,y,z): coord=ModestMaps.Core.Coordinate(int(x), int(y), int(z)) configFile=open('static/config/tile.cfg') config=json.load(configFile) cfg = TileStache.Config.buildConfiguration(config) contenttype, content = TileStache.getTile(cfg.layers[layer], coord,extension) if not os.path.exists('static/map/'+layer): os.mkdir(r'static/map/'+layer) if not os.path.exists('static/map/'+layer+'/'+z): os.mkdir(r'static/map/'+layer+'/'+z) if not os.path.exists('static/map/'+layer+'/'+z+'/'+y): os.mkdir(r'static/map/'+layer+'/'+z+'/'+y) #if not os.path.exists('static/map/'+layer+'/'+z+'/'+y+'/'+x): # os.mkdir(r'static/map/'+layer+'/'+z+'/'+y+'/'+x) tilepath='static/map/'+layer+'/'+z+'/'+y+'/'+x+'.png' open(tilepath, 'w').write(content) return contenttype, content
def GET(layer, zoom, x, y): ini = time.clock() path_configfile = os.path.join(request.folder, 'static/tilestache.cfg') config2 = ts.parseConfigfile(path_configfile) formato = request.extension token_version = "x234dffx" response.headers["Cache-Control"] = "public, max-age=100" if request.env.http_if_none_match == token_version: raise HTTP(304, "", **response.headers) else: try: layer2 = config2.layers[layer] coord = ts.Coordinate(int(y), int(x), int(zoom)) mime_type, tile_content = ts.getTile(layer2, coord, formato) except: raise HTTP(404) response.headers["Content-Type"] = mime_type response.headers["Etag"] = token_version raise HTTP(200, tile_content, **response.headers) return locals()
def renderTile(self, width, height, srs, coord): logging.info("[dithering] render tile %s..." % coord) source = self.layer.config.layers[ self.source_layer ] mime, body = TileStache.getTile(source, coord, 'png') if self.skip_on_checksum: hash = md5.new(body) if hash.hexdigest() == self.checksum: logging.info('[dithering] skip check sum matches %s : %s' % (coord, self.checksum)) return Image.new('RGBA', (256, 256)) img = Image.open(StringIO.StringIO(body)) if self.plotter == 'atk': return self.dither_atk(img) return self.dither_python(img)
def getTileUrls(self, coord): """ Return tile URLs that start with file://, by first retrieving them. """ if self.threadsafe or self.lock.acquire(): mime_type, tile_data = TileStache.getTile(self.layer, coord, 'png', self.ignore_cached) handle, filename = mkstemp(prefix='tilestache-compose-', suffix='.png') write(handle, tile_data) close(handle) self.files.append(filename) if not self.threadsafe: # must be locked, right? self.lock.release() if self.verbose: size = len(tile_data) / 1024. printlocked(self.lock, self.layer.name() + '/%(zoom)d/%(column)d/%(row)d.png' % coord.__dict__, '(%dKB)' % size) return ('file://' + abspath(filename), )
def renderTile(self, width, height, srs, coord): logging.info("[dithering] render tile %s..." % coord) source = self.layer.config.layers[self.source_layer] mime, body = TileStache.getTile(source, coord, 'png') if self.skip_on_checksum: hash = md5.new(body) if hash.hexdigest() == self.checksum: logging.info('[dithering] skip check sum matches %s : %s' % (coord, self.checksum)) return Image.new('RGBA', (256, 256)) img = Image.open(StringIO.StringIO(body)) if self.plotter == 'atk': return self.dither_atk(img) return self.dither_python(img)
def tilestache_tiles(request, layer_name, z, x, y, extension): """ :param request: :param layer_name: :param z: :param x: :param y: :param extension: :return: Proxy to tilestache {X} - coordinate column. {Y} - coordinate row. {B} - bounding box. {Z} - zoom level. {S} - host. """ config = TileStacheConfig.objects.filter(name='default')[0].config path_info = "%s/%s/%s/%s.%s" % (layer_name, z, x, y, extension) coord, extension = TileStache.splitPathInfo(path_info)[1:] mimetype, content = TileStache.getTile(config.layers[layer_name], coord, extension) return HttpResponse(content, mimetype=mimetype)
def tiles(request, service_slug, z, x, y, extension): """ Proxy to tilestache {X} - coordinate column. {Y} - coordinate row. {B} - bounding box. {Z} - zoom level. {S} - host. """ service = TileService.objects.get(slug=service_slug) config = {"cache": {"name": "Test"}, "layers": {}} config["layers"][service_slug] = { "provider": { 'name': 'mapnik', "mapfile": service.mapfile } } config = TileStache.Config.buildConfiguration(config) path_info = "%s/%s/%s/%s.%s" % (service_slug, z, x, y, extension) coord, extension = TileStache.splitPathInfo(path_info)[1:] mimetype, content = TileStache.getTile(config.layers[service_slug], coord, extension) return HttpResponse(content, mimetype=mimetype)
def render(self, config, input_rgba, coord): """ Render this image layer. Given a configuration object, starting image, and coordinate, return an output image with the contents of this image layer. """ has_layer, has_color, has_mask = False, False, False output_rgba = [chan.copy() for chan in input_rgba] if self.layername: layer = config.layers[self.layername] _, _, body = TileStache.getTile(layer, coord, 'png') layer_img = Image.open(StringIO(body)).convert('RGBA') layer_rgba = _img2rgba(layer_img) has_layer = True if self.maskname: layer = config.layers[self.maskname] _, _, body = TileStache.getTile(layer, coord, 'png') mask_img = Image.open(StringIO(body)).convert('L') mask_chan = _img2arr(mask_img).astype(numpy.float32) / 255. has_mask = True if self.colorname: color = make_color(self.colorname) color_rgba = [numpy.zeros(output_rgba[0].shape, numpy.float32) + band/255.0 for band in color] has_color = True if has_layer: layer_rgba = apply_adjustments(layer_rgba, self.adjustments) if has_layer and has_color and has_mask: raise KnownUnknown("You can't specify src, color and mask together in a Composite Layer: %s, %s, %s" % (repr(self.layername), repr(self.colorname), repr(self.maskname))) elif has_layer and has_color: # color first, then layer output_rgba = blend_images(output_rgba, color_rgba[:3], color_rgba[3], self.opacity, self.blendmode) output_rgba = blend_images(output_rgba, layer_rgba[:3], layer_rgba[3], self.opacity, self.blendmode) elif has_layer and has_mask: # need to combine the masks here layermask_chan = layer_rgba[3] * mask_chan output_rgba = blend_images(output_rgba, layer_rgba[:3], layermask_chan, self.opacity, self.blendmode) elif has_color and has_mask: output_rgba = blend_images(output_rgba, color_rgba[:3], mask_chan, self.opacity, self.blendmode) elif has_layer: output_rgba = blend_images(output_rgba, layer_rgba[:3], layer_rgba[3], self.opacity, self.blendmode) elif has_color: output_rgba = blend_images(output_rgba, color_rgba[:3], color_rgba[3], self.opacity, self.blendmode) elif has_mask: raise KnownUnknown("You have to provide more than just a mask to Composite Layer: %s" % repr(self.maskname)) else: raise KnownUnknown("You have to provide at least some combination of src, color and mask to Composite Layer") return output_rgba
def getTile(layer,extension,x,y,z,cfg): contenttype, content = TileStache.getTile(cfg.layers[layer], ModestMaps.Core.Coordinate(int(x), int(y), int(z)),extension,ignore_cached=True) return content
def render(self, config, input_rgba, coord): """ Render this image layer. Given a configuration object, starting image, and coordinate, return an output image with the contents of this image layer. """ has_layer, has_color, has_mask = False, False, False output_rgba = [chan.copy() for chan in input_rgba] if self.layername: layer = config.layers[self.layername] mime, body = TileStache.getTile(layer, coord, 'png') layer_img = Image.open(StringIO(body)).convert('RGBA') layer_rgba = _img2rgba(layer_img) has_layer = True if self.maskname: layer = config.layers[self.maskname] mime, body = TileStache.getTile(layer, coord, 'png') mask_img = Image.open(StringIO(body)).convert('L') mask_chan = _img2arr(mask_img).astype(numpy.float32) / 255. has_mask = True if self.colorname: color = make_color(self.colorname) color_rgba = [ numpy.zeros(output_rgba[0].shape, numpy.float32) + band / 255.0 for band in color ] has_color = True if has_layer: layer_rgba = apply_adjustments(layer_rgba, self.adjustments) if has_layer and has_color and has_mask: raise KnownUnknown( "You can't specify src, color and mask together in a Composite Layer: %s, %s, %s" % (repr(self.layername), repr( self.colorname), repr(self.maskname))) elif has_layer and has_color: # color first, then layer output_rgba = blend_images(output_rgba, color_rgba[:3], color_rgba[3], self.opacity, self.blendmode) output_rgba = blend_images(output_rgba, layer_rgba[:3], layer_rgba[3], self.opacity, self.blendmode) elif has_layer and has_mask: # need to combine the masks here layermask_chan = layer_rgba[3] * mask_chan output_rgba = blend_images(output_rgba, layer_rgba[:3], layermask_chan, self.opacity, self.blendmode) elif has_color and has_mask: output_rgba = blend_images(output_rgba, color_rgba[:3], mask_chan, self.opacity, self.blendmode) elif has_layer: output_rgba = blend_images(output_rgba, layer_rgba[:3], layer_rgba[3], self.opacity, self.blendmode) elif has_color: output_rgba = blend_images(output_rgba, color_rgba[:3], color_rgba[3], self.opacity, self.blendmode) elif has_mask: raise KnownUnknown( "You have to provide more than just a mask to Composite Layer: %s" % repr(self.maskname)) else: raise KnownUnknown( "You have to provide at least some combination of src, color and mask to Composite Layer" ) return output_rgba
if len(args) >= 1: r = int(args[0]) if len(args) >= 2: c = int(args[1]) if len(args) == 3: z = int(args[2]) config = { "cache": { "name": "Test" }, "layers": { "comuni": { "provider": { "name": "mapnik", "mapfile": "comuni_italia.xml" }, "projection": "spherical mercator" } } } # like http://tile.openstreetmap.org/1/0/0.png coord = ModestMaps.Core.Coordinate(r, c, z) config = TileStache.Config.buildConfiguration(config) type, bytes = TileStache.getTile(config.layers['comuni'], coord, 'png') filename = 'tile_%s_%s_%s.png' % (r, c, z) open(filename, 'w').write(bytes) print "tile %s created\n" % filename
"name": "proxy", "url": "http://tile.openstreetmap.org/{Z}/{X}/{Y}.png" } }, "mbtiles_layer": { "provider": { "name": "mbtiles", "tileset": "data/mbtiles/open-streets-dc.mbtiles" } }, "composite": { "provider": { "class": "TileStache.Goodies.Providers.Composite:Provider", "kwargs": { "stack": [ {"src": "osm_layer", "zoom": "0-9"}, {"src": "mbtiles_layer", "zoom": "10-18"} ] } } } } } # http://localhost:8080/mbtiles_layer/14/4688/6267.png coord = ModestMaps.Core.Coordinate(6267,4688,14) config = TileStache.Config.buildConfiguration(config) type, bytes = TileStache.getTile(config.layers['mbtiles_layer'], coord, 'png', ignore_cached=True) open('tile.png', 'w').write(bytes)