def make_transform(tilerange, zoom): ulx, uly = mercantile.xy(*mercantile.ul(tilerange['x']['min'], tilerange['y']['min'], zoom)) lrx, lry = mercantile.xy(*mercantile.ul(tilerange['x']['max'], tilerange['y']['max'], zoom)) xcell = (lrx - ulx) / float(tilerange['x']['max'] - tilerange['x']['min']) ycell = (uly - lry) / float(tilerange['y']['max'] - tilerange['y']['min']) return Affine(xcell, 0, ulx, 0, -ycell, uly)
def union(inputtiles, parsenames): tiles = sutils.tile_parser(inputtiles, parsenames) xmin, xmax, ymin, ymax = sutils.get_range(tiles) zoom = sutils.get_zoom(tiles) # make an array of shape (xrange + 3, yrange + 3) burn = sutils.burnXYZs(tiles, xmin, xmax, ymin, ymax, 0) nw = mercantile.xy(*mercantile.ul(xmin, ymin, zoom)) se = mercantile.xy(*mercantile.ul(xmax + 1, ymax + 1, zoom)) aff = Affine(((se[0] - nw[0]) / float(xmax - xmin + 1)), 0.0, nw[0], 0.0, -((nw[1] - se[1]) / float(ymax - ymin + 1)), nw[1]) unprojecter = sutils.Unprojecter() unionedTiles = [ { 'geometry': unprojecter.unproject(feature), 'properties': {}, 'type': 'Feature' } for feature, shapes in features.shapes(np.asarray(np.flipud(np.rot90(burn)).astype(np.uint8), order='C'), transform=aff) if shapes == 1 ] return unionedTiles
def tilebbox(dist, lon, lat, bdim=BASE_DIM, buffer=4, format=None): """ dist @float : Search distance. lon @float : Center longitude. lat @float : Center latitude. bdim @float : Base time dimention in meters. buffer @integer : Buffer dimention in times of bdim. format @string : Output format returns: upper left corner coordinates and bottom right cooodinates in terms of * [[minlon, maxlat], [maxlon, minlat]] * {'w': minlon, 's': minlat, 'e': maxlon, 'n': maxlat} in case OSM format is required """ bt, _ = get_base_tile(lon, lat, bdim) nn = __get_box_dim(bt, dist) ul = mc.ul(bt.x-(nn+buffer), bt.y-(nn+buffer), bt.z) br = mc.ul(bt.x+(nn+buffer)+1, bt.y+(nn+buffer)+1, bt.z) if format is None: return Bbox(ul.lng, br.lat, br.lng, ul.lat) elif format == 'osm': keys = ('w', 's', 'e', 'n') return dict(zip(keys, map(str, (ul.lng, br.lat, br.lng, ul.lat,)))) # minx, miny, maxx, maxy else: raise NotImplementedError
def _write((tile, data)): if not contains_data((tile, data)): return print("Writing", tile) # Get the bounds of the tile. ulx, uly = mercantile.xy( *mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy( *mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) # TODO constantize tmp_path = "/vsimem/tile" # create GeoTIFF meta = creation_options.copy() meta["count"] = 1 meta["nodata"] = data.fill_value meta["dtype"] = data.dtype meta["width"] = CHUNK_SIZE meta["height"] = CHUNK_SIZE meta["transform"] = from_bounds(ulx, lry, lrx, uly, CHUNK_SIZE, CHUNK_SIZE) with rasterio.drivers(): with rasterio.open(tmp_path, "w", **meta) as tmp: tmp.write(data, 1) # write out output_uri = urlparse(out_dir) contents = bytearray(virtual_file_to_buffer(tmp_path)) if output_uri.scheme == "s3": # TODO use mapPartitions so that the client only needs to be # instantiated once per partition client = boto3.client("s3") bucket = output_uri.netloc # TODO strip out trailing slashes on the path if necessary key = "%s/%d/%d/%d.tif" % (output_uri.path[1:], tile.z, tile.x, tile.y) response = client.put_object( ACL="public-read", Body=bytes(contents), Bucket=bucket, # CacheControl="TODO", ContentType="image/tiff", Key=key ) else: output_path = os.path.join(out_dir, "%d/%d/%d.tif" % (tile.z, tile.x, tile.y)) mkdir_p(os.path.dirname(output_path)) f = open(output_path, "w") f.write(contents) f.close()
def make_transform(tilerange, zoom): ulx, uly = mercantile.xy( *mercantile.ul(tilerange["x"]["min"], tilerange["y"]["min"], zoom) ) lrx, lry = mercantile.xy( *mercantile.ul(tilerange["x"]["max"], tilerange["y"]["max"], zoom) ) xcell = (lrx - ulx) / float(tilerange["x"]["max"] - tilerange["x"]["min"]) ycell = (uly - lry) / float(tilerange["y"]["max"] - tilerange["y"]["min"]) return Affine(xcell, 0, ulx, 0, -ycell, uly)
def _f(tile): x, y, z = tile ulx, uly = mercantile.ul(x, y, z) lrx, lry = mercantile.ul(x + 1, y + 1, zoom) return ( (bounds[0] < ulx < bounds[2]) and (bounds[1] < uly < bounds[3]) and (bounds[0] < lrx < bounds[2]) and (bounds[1] < lry < bounds[3]) )
def __init__(self, center, bbox, zoom, basemap, opacity, size=None): self._configure_event_loop() point = center['geometry']['coordinates'] if size is None: size = IMAGE_SIZE self.num_tiles = [ math.ceil(size[x] / TILE_SIZE[x]) + 1 for x in (0, 1) ] center_tile = mercantile.tile(point[0], point[1], zoom) mercator = Proj(init='epsg:3857') wgs84 = Proj(init='epsg:4326') center_tile_bbox = BBox(mercantile.bounds(*center_tile), projection=wgs84).project(mercator, edge_points=0) center_to_image = world_to_image(center_tile_bbox, TILE_SIZE) center_to_world = image_to_world(center_tile_bbox, TILE_SIZE) center_point_px = center_to_image(*mercantile.xy(*point)) self.ul_tile = mercantile.tile(*transform( mercator, wgs84, *center_to_world(center_point_px[0] - math.ceil(IMAGE_SIZE[0] / 2), center_point_px[1] - math.ceil(IMAGE_SIZE[1] / 2)), zoom)) lr_tile = mercantile.Tile(x=min(2**zoom, self.ul_tile.x + self.num_tiles[0]), y=min(2**zoom, self.ul_tile.y + self.num_tiles[1]), z=zoom) ul = mercantile.xy(*mercantile.ul(*self.ul_tile)) lr = mercantile.xy(*mercantile.ul(*lr_tile)) self.image_bbox = BBox((ul[0], lr[1], lr[0], ul[1])) self.image_size = (TILE_SIZE[0] * self.num_tiles[0], TILE_SIZE[1] * self.num_tiles[1]) self.to_image = world_to_image(self.image_bbox, self.image_size) self.to_world = image_to_world(self.image_bbox, self.image_size) self.point_px = [ round(x) for x in self.to_image(*mercantile.xy(*point)) ] self.target_size = size self.point = point self.zoom = zoom self.basemap = basemap self._basemap_image = None self.opacity = opacity
def get_bbox(self, zoom=18): tile = self.get_tile(zoom) ne = mercantile.bounds( mercantile.tile(*mercantile.ul(tile.x + 1, tile.y + 1, zoom), zoom=zoom)) sw = mercantile.bounds( mercantile.tile(*mercantile.ul(tile.x - 1, tile.y - 1, zoom), zoom=zoom)) return sw.west, sw.south, ne.east, ne.north
def tiles_to_bounds(tiles: List[mercantile.Tile]) -> Tuple[float, float, float, float]: """Get bounds from a set of mercator tiles.""" zoom = tiles[0].z xyz = numpy.array([[t.x, t.y, t.z] for t in tiles]) extrema = { "x": {"min": xyz[:, 0].min(), "max": xyz[:, 0].max() + 1}, "y": {"min": xyz[:, 1].min(), "max": xyz[:, 1].max() + 1}, } ulx, uly = mercantile.ul(extrema["x"]["min"], extrema["y"]["min"], zoom) lrx, lry = mercantile.ul(extrema["x"]["max"], extrema["y"]["max"], zoom) return (ulx, lry, lrx, uly)
def process_tile(tile): """Process a single MBTiles tile Parameters ---------- tile : mercantile.Tile Returns: tile : mercantile.Tile The input tile. bytes : bytearray Image bytes corresponding to the tile. """ global base_kwds, resampling, src # Get the bounds of the tile. ulx, uly = mercantile.xy( *mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy( *mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) kwds = base_kwds.copy() kwds['transform'] = from_bounds(ulx, lry, lrx, uly, 256, 256) src_nodata = kwds.pop('src_nodata', None) dst_nodata = kwds.pop('dst_nodata', None) with rasterio.open('/vsimem/tileimg', 'w', **kwds) as tmp: reproject(rasterio.band(src, src.indexes), rasterio.band(tmp, tmp.indexes), src_nodata=src_nodata, dst_nodata=dst_nodata, num_threads=1, resampling=resampling) data = bytearray(virtual_file_to_buffer('/vsimem/tileimg')) # Workaround for https://bugs.python.org/issue23349. if sys.version_info[0] == 2 and sys.version_info[2] < 10: # Check for backported bug fix before re-ordering if kwds['driver'] == 'PNG' and data[0:8] == png_header: # Properly constructed PNG, no need to re-order bytes pass elif kwds['driver'] == 'JPEG' and data[0:4] == jpeg_header: # Properly constructed JPEG, no need to re-order bytes pass else: data[:] = data[-1:] + data[:-1] return tile, data
def pred_2geojson(csv_file, keyword, threshold): features = list() with open(csv_file, 'r') as csvfile: reader = csv.reader(csvfile) next(reader) for row in tqdm(reader): pred = json.loads(row[1]) pred_red = list(map(lambda x: round(x, 2), pred)) pred_obj = dict( zip(map(lambda x: 'p%s' % x, range(len(pred_red))), pred_red)) if keyword == "point": pred_j = ul(*[int(t) for t in row[0].split('-')]) feature_collection = Feature(geometry=dict( type='Point', coordinates=[pred_j.lng, pred_j.lat]), properties=pred_obj) else: pred_j = feature(Tile(*[int(t) for t in row[0].split('-')])) feature_ = Feature(geometry=pred_j['geometry'], properties=pred_obj) if pred_obj['p1'] >= float(threshold): features.append(feature_) feature_collection = FeatureCollection(features) with open('results_{}_{}.geojson'.format(keyword, threshold), 'w') as results: json.dump(feature_collection, results)
def test_xy_tile(): """x, y for the 486-332-10 tile is correctly calculated""" ul = mercantile.ul(486, 332, 10) xy = mercantile.xy(*ul) expected = (-1017529.7205322663, 7044436.526761846) for a, b in zip(expected, xy): assert round(a - b, 7) == 0
def test_ul(): expected = (-9.140625, 53.33087298301705) lnglat = mercantile.ul(486, 332, 10) for a, b in zip(expected, lnglat): assert round(a-b, 7) == 0 assert lnglat[0] == lnglat.lng assert lnglat[1] == lnglat.lat
def test_ul_tile_roundtrip(t): """ul and tile roundtrip""" lnglat = mercantile.ul(t) tile = mercantile.tile(lnglat.lng, lnglat.lat, t.z) assert tile.z == t.z assert tile.x == t.x assert tile.y == t.y
def find_bbox_from_tiles(tiles): """ Returns a dictionary of mercantile.LngLatBbox named tuples for each zoom level in tiles """ xyz = np.array([(t.x, t.y, t.z) for t in tiles]) zooms = np.unique(xyz[:, 2]) out_dict = {} for z in zooms: idx = np.where(xyz[:, 2] == z)[0] ul = xyz[idx, 0:2].min(0) ll = xyz[idx, 0:2].max(0) + 1 a = mercantile.ul(ul[0], ul[1], z) b = mercantile.ul(ll[0], ll[1], z) out_dict[z] = mercantile.LngLatBbox(a[0], b[1], b[0], a[1]) return out_dict
def get_tile(tx, ty, zoom): print tx, ty, zoom bounds = mercantile.bounds(tx, ty, zoom) print bounds lat1 = int(bounds.north) lat2 = int(bounds.south) lon1 = int(bounds.west) lon2 = int(bounds.east) tile = np.zeros((256, 256)) tile_base = mercantile.xy(*mercantile.ul(tx, ty, zoom)) print tile_base for lat in range(lat2, lat1 + 1): for lon in range(lon1, lon2 + 1): key = "meshmap:block:%d:%d" % (lat, lon) block = np.frombuffer(redis.get(key), dtype=np.uint8) block = block.reshape((120, 80)) blat1 = min(lat + 1, bounds.north) blat2 = max(lat, bounds.south) blon1 = max(lon, bounds.west) blon2 = min(lon + 1, bounds.east) print blat1, blat2, blon1, blon2 p1 = mercantile.xy(blon1, blat1) p2 = mercantile.xy(blon2, blat2) print p1, p2 print p1[0] - tile_base[0], p1[1] - tile_base[1]
def test_ul(args): expected = (-9.140625, 53.33087298301705) lnglat = mercantile.ul(*args) for a, b in zip(expected, lnglat): assert round(a - b, 7) == 0 assert lnglat[0] == lnglat.lng assert lnglat[1] == lnglat.lat
def process_tile(tile): """Process a single MBTiles tile.""" global base_kwds, src # Get the bounds of the tile. ulx, uly = mercantile.xy(*mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy(*mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) kwds = base_kwds.copy() kwds["transform"] = from_bounds(ulx, lry, lrx, uly, 256, 256) with rasterio.open("/vsimem/tileimg", "w", **kwds) as tmp: # Reproject the src dataset into image tile. for bidx in tmp.indexes: reproject(rasterio.band(src, bidx), rasterio.band(tmp, bidx)) # Get contents of the virtual file. contents = bytearray(virtual_file_to_buffer("/vsimem/tileimg")) return tile, contents
def process_tile(tile): """Process a single MBTiles tile.""" global base_kwds, src # Get the bounds of the tile. ulx, uly = mercantile.xy(*mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy(*mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) kwds = base_kwds.copy() kwds['transform'] = from_bounds(ulx, lry, lrx, uly, 256, 256) with rasterio.open('/vsimem/tileimg', 'w', **kwds) as tmp: # Reproject the src dataset into image tile. for bidx in tmp.indexes: reproject(rasterio.band(src, bidx), rasterio.band(tmp, bidx)) # Get contents of the virtual file and repair it. contents = bytearray(virtual_file_to_buffer('/vsimem/tileimg')) return tile, contents[-1:] + contents[:-1]
def _tile_worker(tile): """ For each tile, and given an open rasterio src, plus a`global_args` dictionary with attributes of `base_val`, `interval`, and a `writer_func`, warp a continous single band raster to a 512 x 512 mercator tile, then encode this tile into RGB. Parameters ----------- tile: list [x, y, z] indices of tile Returns -------- tile, buffer tuple with the input tile, and a bytearray with the data encoded into the format created in the `writer_func` """ x, y, z = tile bounds = [ c for i in ( mercantile.xy(*mercantile.ul(x, y + 1, z)), mercantile.xy(*mercantile.ul(x + 1, y, z)), ) for c in i ] toaffine = transform.from_bounds(*bounds + [512, 512]) out = np.empty((512, 512), dtype=src.meta["dtype"]) reproject( rasterio.band(src, 1), out, dst_transform=toaffine, dst_crs="epsg:3857", resampling=Resampling.bilinear, ) out = data_to_rgb(out, global_args["base_val"], global_args["interval"]) return tile, global_args["writer_func"](out, global_args["kwargs"].copy(), toaffine)
def process_chunk(tile, input, creation_options, resampling): """Process a single tile.""" from rasterio.warp import RESAMPLING input = input.replace("s3://", "/vsicurl/http://s3.amazonaws.com/") print("Chunking initial image for", tile) # Get the bounds of the tile. ulx, uly = mercantile.xy( *mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy( *mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) tmp_path = "/vsimem/tile" with rasterio.drivers(): with rasterio.open(input, "r") as src: meta = src.meta.copy() meta.update(creation_options) meta["height"] = CHUNK_SIZE meta["width"] = CHUNK_SIZE meta["transform"] = from_bounds(ulx, lry, lrx, uly, CHUNK_SIZE, CHUNK_SIZE) # write to a tmp file to allow GDAL to handle the transform with rasterio.open(tmp_path, "w", **meta) as tmp: # Reproject the src dataset into image tile. for bidx in src.indexes: reproject( source=rasterio.band(src, bidx), destination=rasterio.band(tmp, bidx), resampling=getattr(RESAMPLING, resampling), num_threads=multiprocessing.cpu_count(), ) # check for chunks containing only NODATA data = tmp.read(masked=True) if data.mask.all(): return # TODO hard-coded for the first band return (tile, data[0])
def test_xy(): ul = mercantile.ul(486, 332, 10) xy = mercantile.xy(*ul) expected = (-1017529.7205322663, 7044436.526761846) for a, b in zip(expected, xy): assert round(a-b, 7) == 0 xy = mercantile.xy(0.0, 0.0) expected = (0.0, 0.0) for a, b in zip(expected, xy): assert round(a-b, 7) == 0
def downsample((tile, data)): if data is None: return print("Downsampling", tile) # Get the bounds of the tile. ulx, uly = mercantile.xy( *mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy( *mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) # TODO constantize tmp_path = "/vsimem/tile" # create GeoTIFF meta = { "driver": "GTiff", "crs": "EPSG:3857", "nodata": data.fill_value, "count": 1, "dtype": data.dtype, "width": CHUNK_SIZE, "height": CHUNK_SIZE, "transform": from_bounds(ulx, lry, lrx, uly, CHUNK_SIZE, CHUNK_SIZE), } with rasterio.drivers(): with rasterio.open(tmp_path, "w", **meta) as tmp: # use GDAL to resample by writing an ndarray and immediately reading # it out into a smaller array tmp.write(data, 1) resampled = tmp.read( indexes=1, masked=True, out=ma.array(np.empty((CHUNK_SIZE / 2, CHUNK_SIZE / 2), data.dtype)), ) if resampled.mask.all(): return corner = CORNERS[(tile.x % 2, tile.y % 2)] return (mercantile.parent(tile), (corner, resampled))
def test_xy(): ul = mercantile.ul(486, 332, 10) xy = mercantile.xy(*ul) expected = (-1017529.7205322663, 7044436.526761846) for a, b in zip(expected, xy): assert round(a - b, 7) == 0 xy = mercantile.xy(0.0, 0.0) expected = (0.0, 0.0) for a, b in zip(expected, xy): assert round(a - b, 7) == 0
def get_and_store_file(tile_ind, list_format=('x', 'y', 'z')): """Fetch tile from internet and store on local drive""" # Only download a certain percentage of tiles # if random() > down_p['download_prob']: # # return 0 tile_ind = tile_ind.rstrip('\n').split() ##################################### # Construct url to image ##################################### # Allow for other x/y/z orders ind_pos = [list_format.index(letter) for letter in ['x', 'y', 'z']] ind_dict = dict(x=tile_ind[ind_pos[0]], y=tile_ind[ind_pos[1]], z=tile_ind[ind_pos[2]]) #url = down_p['url_template'].format(**ind_dict) service = Static(down_p['token']) coords = mercantile.ul(int(tile_ind[ind_pos[0]]), int(tile_ind[ind_pos[1]]), int(tile_ind[ind_pos[2]])) response = service.image('mapbox.satellite', lon=coords.lng, lat=coords.lat, z=tile_ind[ind_pos[2]], image_format='jpg', width=256, height=256) ############################################## # Setup file paths ############################################## # Check if tile exists already tile_fname = op.join(down_p['storage_dir'], '{x}-{y}-{z}.jpg'.format(**ind_dict)) #################################################### # Download to storage if file doesn't exist #################################################### if not op.exists(tile_fname): repeat_try = 10 while repeat_try: try: print("status code", response.status_code) if response.status_code == 200: with open(tile_fname, 'wb') as output: _ = output.write(response.content) print('Successfully saved', tile_fname) return 200 elif response.status_code == 429: print('URL Error') return 429 except response.status_code != 200: repeat_try -= 1 if repeat_try == 0: print('Too many repeats, quitting on {}'.format(tile_fname)) return else: print('File exists: {}'.format(tile_fname)) return 0
def merc2xy(x, y, z, sgeom): """ Apply some affine transformations to input shapely geometry for coordinates transformation from Mercatore to local tile pixels. sgeom @shapely.geom : Input polygon Returns a brand new shapely polygon in the new coordinates. """ MVT_EXTENT = 4096 X_min, Y_max = mercantile.xy(*mercantile.ul(x, y, z)) X_max, Y_min = mercantile.xy(*mercantile.ul(x + 1, y - 1, z)) geom_3857 = transform(transformer.transform, sgeom) tx, ty = -X_min, -2 * Y_max + Y_min geom_XY = translate(geom_3857, tx, ty) x_scale_factor = MVT_EXTENT / (X_max - X_min) y_scale_factor = -MVT_EXTENT / (Y_max - Y_min) geom_xy = scale(geom_XY, x_scale_factor, y_scale_factor, origin=(0, 0, 0)) return geom_xy
def union(inputtiles, parsenames): tiles = sutils.tile_parser(inputtiles, parsenames) xmin, xmax, ymin, ymax = sutils.get_range(tiles) zoom = sutils.get_zoom(tiles) # make an array of shape (xrange + 3, yrange + 3) burn = sutils.burnXYZs(tiles, xmin, xmax, ymin, ymax, 0) nw = mercantile.xy(*mercantile.ul(xmin, ymin, zoom)) se = mercantile.xy(*mercantile.ul(xmax + 1, ymax + 1, zoom)) aff = Affine( ((se[0] - nw[0]) / float(xmax - xmin + 1)), 0.0, nw[0], 0.0, -((nw[1] - se[1]) / float(ymax - ymin + 1)), nw[1], ) unprojecter = sutils.Unprojecter() unionedTiles = [ { "geometry": unprojecter.unproject(feature), "properties": {}, "type": "Feature", } for feature, shapes in features.shapes( np.asarray(np.flipud(np.rot90(burn)).astype(np.uint8), order="C"), transform=aff, ) if shapes == 1 ] return unionedTiles
def handle(self, *args, **options): # the mission ul = (-122.424130, 37.764861) br = (-122.404819, 37.749389) ZOOM = 19 ul_tile = mercantile.tile(*ul + (ZOOM, )) br_tile = mercantile.tile(*br + (ZOOM, )) for x in range(ul_tile.x, br_tile.x + 1): for y in range(ul_tile.y, br_tile.y + 1): lng, lat = mercantile.ul(x, y, ZOOM) Tile.objects.get_or_create(pt=Point(lng, lat))
def process_tile(tile): """Process a single MBTiles tile Parameters ---------- tile : mercantile.Tile Returns: tile : mercantile.Tile The input tile. bytes : bytearray Image bytes corresponding to the tile. """ global base_kwds, src # Get the bounds of the tile. ulx, uly = mercantile.xy(*mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy(*mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) kwds = base_kwds.copy() kwds['transform'] = from_bounds(ulx, lry, lrx, uly, 256, 256) src_nodata = kwds.pop('src_nodata', None) dst_nodata = kwds.pop('dst_nodata', None) with rasterio.open('/vsimem/tileimg', 'w', **kwds) as tmp: reproject(rasterio.band(src, src.indexes), rasterio.band(tmp, tmp.indexes), src_nodata=src_nodata, dst_nodata=dst_nodata, num_threads=1) data = bytearray(virtual_file_to_buffer('/vsimem/tileimg')) # Workaround for https://bugs.python.org/issue23349. if sys.version_info[0] == 2 and sys.version_info[2] < 10: data[:] = data[-1:] + data[:-1] return tile, data
def _tile_worker(tile): """ For each tile, and given an open rasterio src, plus a`global_args` dictionary with attributes of `base_val`, `interval`, and a `writer_func`, warp a continous single band raster to a 512 x 512 mercator tile, then encode this tile into RGB. Parameters ----------- tile: list [x, y, z] indices of tile Returns -------- tile, buffer tuple with the input tile, and a bytearray with the data encoded into the format created in the `writer_func` """ x, y, z = tile bounds = [c for i in (mercantile.xy(*mercantile.ul(x, y + 1, z)), mercantile.xy(*mercantile.ul(x + 1, y, z))) for c in i] toaffine = transform.from_bounds(*bounds + [512, 512]) out = np.empty((512, 512), dtype=src.meta['dtype']) reproject( rasterio.band(src, 1), out, dst_transform=toaffine, dst_crs="epsg:3857", resampling=RESAMPLING.bilinear) out = data_to_rgb(out, global_args['base_val'], global_args['interval']) return tile, global_args['writer_func'](out, global_args['kwargs'].copy(), toaffine)
def assemble_tif(out_path, zoom, tiles, tiles_url, extrema, tilesize): # Define Output COG parameters width = (extrema["x"]["max"] - extrema["x"]["min"]) * tilesize height = (extrema["y"]["max"] - extrema["y"]["min"]) * tilesize w, n = mercantile.xy( *mercantile.ul(extrema["x"]["min"], extrema["y"]["min"], zoom)) res = _meters_per_pixel(zoom, 0, tilesize=tilesize) params = { 'driver': "GTiff", 'dtype': "uint8", 'count': 3, 'width': width, 'height': height, 'crs': "epsg:3857", 'transform': Affine(res, 0, w, 0, -res, n), 'nodata': 0, 'tiled': True, 'blockxsize': tilesize, 'blockysize': tilesize, } output_profile = cog_profiles.get("deflate") with rasterio.Env(): with MemoryFile() as memfile: with memfile.open(**params) as mem: with futures.ThreadPoolExecutor(max_workers=50) as executor: future_work = [ executor.submit(_call_tile_endpoint, tile, tiles_url, extrema, tilesize) for tile in tiles ] for f in tqdm(futures.as_completed(future_work), total=len(future_work)): pass for f in _filter_futures(future_work): window, img = f with rasterio.open(BytesIO(img.content)) as src_dst: mem.write(src_dst.read(indexes=(1, 2, 3)), window=window) cog_translate( mem, out_path, output_profile, config=dict(GDAL_NUM_THREADS="ALL_CPUS", GDAL_TIFF_OVR_BLOCKSIZE="128"), )
def tile_affine(tile_row, tile_col, level): ''' a = width of a pixel b = row rotation (typically zero) c = x-coordinate of the upper-left corner of the upper-left pixel d = column rotation (typically zero) e = height of a pixel (typically negative) f = y-coordinate of the of the upper-left corner of the upper-left pixel reference: https://www.perrygeo.com/python-affine-transforms.html Arguments: tile_row {int} -- or y in Cartesian coordinate system tile_col {int} -- or x in Cartesian coordinate system ''' a = LEVEL_RESLUTION[level] b = 0 c, f = mercantile.xy(*mercantile.ul(tile_col, tile_row, level)) d = 0 e = -LEVEL_RESLUTION[level] return Affine(a, b, c, d, e, f)
def main(args): parser = argparse.ArgumentParser() parser.add_argument("-d", "--debug", action="store_true") parsed = parser.parse_args(args) global debug debug = parsed.debug scriptdir = os.path.dirname(os.path.realpath(__file__)) testdata_directory = os.path.join(scriptdir, "testdata") outdata_directory = os.path.join(testdata_directory, "out") wgs84 = TilePyramid("geodetic") wgs84_meta = MetaTilePyramid(wgs84, 16) # TilePyramid #=========== # tiles per zoomlevel try: matrix_width = wgs84.matrix_width(5) matrix_height = wgs84.matrix_height(5) assert (matrix_width, matrix_height) == (64, 32) print "tiles per zoomlevel OK" except: print "tiles per zoomlevel FAILED" raise # top left coordinate try: tile = wgs84.tile(5, 3, 3) tl = (tile.left,tile.top) assert tl == (-163.125, 73.125) print "top left coordinate OK" except: print "top left coordinate FAILED" print tl raise # tile bounding box try: tile = wgs84.tile(5, 3, 3) bbox = tile.bbox() testpolygon = Polygon([[-163.125, 73.125], [-157.5, 73.125], [-157.5, 67.5], [-163.125, 67.5], [-163.125, 73.125]]) assert bbox.equals(testpolygon) print "tile bounding box OK" except: print "tile bounding box FAILED" raise # tile bounding box with buffer try: tile = wgs84.tile(5, 3, 3) bbox = tile.bbox(1) testpolygon = Polygon([[-163.14697265625, 73.14697265625], [-157.47802734375, 73.14697265625], [-157.47802734375, 67.47802734375], [-163.14697265625, 67.47802734375], [-163.14697265625, 73.14697265625]]) assert bbox.equals(testpolygon) print "tile bounding box with buffer OK" except: print "tile bounding box with buffer FAILED" print bbox raise # tile bounds try: tile = wgs84.tile(5, 3, 3) bounds = tile.bounds() testbounds = (-163.125, 67.5, -157.5, 73.125) assert bounds == testbounds print "tile bounds OK" except: print "tile bounds FAILED" raise # tile bounds buffer try: tile = wgs84.tile(5, 3, 3) bounds = tile.bounds(1) testbounds = (-163.14697265625, 67.47802734375, -157.47802734375, 73.14697265625) assert bounds == testbounds print "tile bounds with buffer OK" except: print "tile bounds wigh buffer FAILED" raise # test bounding box bbox_location = os.path.join(testdata_directory, "bbox.geojson") tiled_out = os.path.join(outdata_directory, "bbox_tiles.geojson") zoom = 5 testtiles = [(5, 5, 33), (5, 6, 33), (5, 7, 33), (5, 8, 33), (5, 9, 33), (5, 10, 33), (5, 5, 34), (5, 6, 34), (5, 7, 34), (5, 8, 34), (5, 9, 34), (5, 10, 34), (5, 5, 35), (5, 6, 35), (5, 7, 35), (5, 8, 35), (5, 9, 35), (5, 10, 35),(5, 5, 36), (5, 6, 36), (5, 7, 36), (5, 8, 36), (5, 9, 36), (5, 10, 36), (5, 5, 37), (5, 6, 37), (5, 7, 37), (5, 8, 37), (5, 9, 37), (5, 10, 37),(5, 5, 38), (5, 6, 38), (5, 7, 38), (5, 8, 38), (5, 9, 38), (5, 10, 38), (5, 5, 39), (5, 6, 39), (5, 7, 39), (5, 8, 39), (5, 9, 39), (5, 10, 39), (5, 5, 40), (5, 6, 40), (5, 7, 40), (5, 8, 40), (5, 9, 40), (5, 10, 40), (5, 5, 41), (5, 6, 41), (5, 7, 41), (5, 8, 41), (5, 9, 41), (5, 10, 41)] with fiona.open(bbox_location) as bbox_file: try: bbox_tiles = [ (tile.zoom, tile.row, tile.col) for tile in wgs84.tiles_from_bbox(bbox_file, zoom) ] assert len(set(bbox_tiles).symmetric_difference(set(testtiles))) == 0 print "bounding box OK" except: print "bounding box FAILED" raise if debug: ## write debug output schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(tiled_out) except: pass with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink: for tile in bbox_tiles: zoom, row, col = tile feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) # tiles from Point point_location = os.path.join(testdata_directory, "point.geojson") tiled_out = os.path.join(outdata_directory, "point_tiles.geojson") zoom = 6 testtile = [(6, 14, 69)] with fiona.open(point_location) as point_file: point = shape(point_file[0]["geometry"]) try: point_tile = [ (tile.zoom, tile.row, tile.col) for tile in wgs84.tiles_from_geom(point, zoom) ] assert point_tile == testtile print "Point OK" except: print point_tile, testtile print "Point FAILED" raise if debug: ## write debug output schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(tiled_out) except: pass with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink: zoom, row, col = point_tile[0] feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) # tiles from MultiPoint multipoint_location = os.path.join(testdata_directory, "multipoint.geojson") tiled_out = os.path.join(outdata_directory, "multipoint_tiles.geojson") zoom = 9 testtiles = [(9, 113, 553), (9, 118, 558)] with fiona.open(multipoint_location) as multipoint_file: multipoint = shape(multipoint_file[0]["geometry"]) try: multipoint_tiles = [ (tile.zoom, tile.row, tile.col) for tile in wgs84.tiles_from_geom(multipoint, zoom) ] assert multipoint_tiles == testtiles print "MultiPoint OK" except: print "MultiPoint FAILED" print multipoint_tiles print testtiles raise if debug: ## write debug output schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(tiled_out) except: pass with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink: for tile in multipoint_tiles: zoom, row, col = tile feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) # tiles from LineString linestring_location = os.path.join(testdata_directory, "linestring.geojson") tiled_out = os.path.join(outdata_directory, "linestring_tiles.geojson") zoom = 6 testtiles = [(6, 14, 66), (6, 14, 67), (6, 14, 68), (6, 14, 69), (6, 14, 70), (6, 15, 70), (6, 15, 71), (6, 16, 71), (6, 16, 72), (6, 15, 73), (6, 16, 73), (6, 15, 74)] with fiona.open(linestring_location) as linestring_file: linestring = shape(linestring_file[0]["geometry"]) try: linestring_tiles = [ (tile.zoom, tile.row, tile.col) for tile in wgs84.tiles_from_geom(linestring, zoom) ] assert len(set(linestring_tiles).symmetric_difference(set(testtiles))) == 0 print "LineString OK" except: print "LineString FAILED" raise if debug: ## write debug output schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(tiled_out) except: pass with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink: for tile in linestring_tiles: zoom, row, col = tile feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) # tiles from MultiLineString multilinestring_location = os.path.join(testdata_directory, "multilinestring.geojson") tiled_out = os.path.join(outdata_directory, "multilinestring_tiles.geojson") zoom = 6 testtiles = [(6, 14, 66), (6, 14, 67), (6, 14, 68), (6, 14, 69), (6, 14, 70), (6, 15, 70), (6, 15, 71), (6, 16, 71), (6, 16, 72), (6, 15, 73), (6, 16, 73), (6, 15, 74), (6, 21, 74), (6, 22, 74), (6, 24, 74), (6, 25, 74), (6, 28, 74), (6, 29, 74), (6, 20, 75), (6, 21, 75), (6, 22, 75), (6, 23, 75), (6, 24, 75), (6, 25, 75), (6, 26, 75), (6, 27, 75), (6, 28, 75), (6, 29, 75), (6, 30, 75), (6, 31, 75), (6, 25, 76)] with fiona.open(multilinestring_location) as multilinestring_file: multilinestring = shape(multilinestring_file[0]["geometry"]) try: multilinestring_tiles = [ (tile.zoom, tile.row, tile.col) for tile in wgs84.tiles_from_geom(multilinestring, zoom) ] assert len(set(multilinestring_tiles).symmetric_difference(set(testtiles))) == 0 print "MultiLineString OK" except: print "MultiLineString FAILED" raise if debug: ## write debug output schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(tiled_out) except: pass with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink: for tile in multilinestring_tiles: zoom, row, col = tile feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) # tiles from Polygon polygon_location = os.path.join(testdata_directory, "polygon.geojson") tiled_out = os.path.join(outdata_directory, "polygon_tiles.geojson") zoom = 8 testtiles = [(8, 60, 269), (8, 61, 269), (8, 60, 270), (8, 61, 270), (8, 60, 271), (8, 61, 271), (8, 60, 272), (8, 61, 272), (8, 60, 273), (8, 61, 273), (8, 59, 274), (8, 60, 274), (8, 61, 274), (8, 58, 275), (8, 59, 275), (8, 60, 275), (8, 61, 275), (8, 58, 276), (8, 59, 276), (8, 60, 276), (8, 61, 276), (8, 62, 276), (8, 58, 277), (8, 59, 277), (8, 60, 277), (8, 61, 277), (8, 58, 278), (8, 59, 278), (8, 60, 278), (8, 61, 278), (8, 58, 279), (8, 59, 279), (8, 60, 279), (8, 61, 279), (8, 58, 280), (8, 59, 280), (8, 60, 280)] with fiona.open(polygon_location) as polygon_file: polygon = shape(polygon_file[0]["geometry"]) polygon_tiles = [ (tile.zoom, tile.row, tile.col) for tile in wgs84.tiles_from_geom(polygon, zoom) ] try: assert len(set(polygon_tiles).symmetric_difference(set(testtiles))) == 0 print "Polygon OK" except: print "Polygon FAILED" raise if debug: ## write debug output schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(tiled_out) except: pass with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink: for tile in polygon_tiles: zoom, row, col = tile feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) # tiles from MultiPolygon multipolygon_location = os.path.join(testdata_directory, "multipolygon.geojson") tiled_out = os.path.join(outdata_directory, "multipolygon_tiles.geojson") zoom = 10 testtiles = [(10, 243, 1081), (10, 244, 1081), (10, 245, 1081), (10, 242, 1082), (10, 243, 1082), (10, 244, 1082), (10, 245, 1082), (10, 241, 1083), (10, 242, 1083), (10, 243, 1083), (10, 244, 1083), (10, 245, 1083), (10, 241, 1084), (10, 242, 1084), (10, 243, 1084), (10, 244, 1084), (10, 245, 1084), (10, 241, 1085), (10, 242, 1085), (10, 243, 1085), (10, 244, 1085), (10, 245, 1085), (10, 241, 1086), (10, 242, 1086), (10, 243, 1086), (10, 244, 1086), (10, 245, 1086), (10, 242, 1087), (10, 243, 1087), (10, 244, 1087), (10, 245, 1087), (10, 241, 1088), (10, 242, 1088), (10, 243, 1088), (10, 244, 1088), (10, 241, 1089), (10, 242, 1089), (10, 243, 1089), (10, 244, 1089), (10, 241, 1090), (10, 242, 1090), (10, 243, 1090), (10, 244, 1090), (10, 241, 1091), (10, 242, 1091), (10, 243, 1091), (10, 244, 1091), (10, 241, 1092), (10, 242, 1092), (10, 243, 1092), (10, 244, 1092), (10, 240, 1093), (10, 241, 1093), (10, 242, 1093), (10, 244, 1093), (10, 245, 1093), (10, 240, 1094), (10, 241, 1094), (10, 242, 1094), (10, 243, 1094), (10, 244, 1094), (10, 245, 1094), (10, 246, 1094), (10, 240, 1095), (10, 241, 1095), (10, 242, 1095), (10, 243, 1095), (10, 244, 1095), (10, 245, 1095), (10, 246, 1095), (10, 241, 1096), (10, 244, 1096), (10, 245, 1096), (10, 246, 1096), (10, 245, 1097), (10, 246, 1097)] with fiona.open(multipolygon_location) as multipolygon_file: multipolygon = shape(multipolygon_file[0]["geometry"]) multipolygon_tiles = [ (tile.zoom, tile.row, tile.col) for tile in wgs84.tiles_from_geom(multipolygon, zoom) ] try: assert len(set(multipolygon_tiles).symmetric_difference(set(testtiles))) == 0 print "MultiPolygon OK" except: print "MultiPolygon FAILED" raise if debug: ## write debug output schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(tiled_out) except: pass with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink: for tile in multipolygon_tiles: zoom, row, col = tile feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) if debug: # writing output test files col, row = 2, 2 zoom = 5 metatiling = 2 wgs84_meta = MetaTilePyramid(wgs84, metatiling) antimeridian_location = os.path.join(testdata_directory, "antimeridian.geojson") with fiona.open(antimeridian_location) as antimeridian_file: geometries = [] for feature in antimeridian_file: geometries.append(shape(feature["geometry"])) antimeridian = cascaded_union(geometries) print "top left tile coordinates:" print "metaTilePyramid: %s" %([wgs84_meta.top_left_tile_coords(zoom, row, col)]) print "tile bounding box" print "metaTilePyramid: %s" %([mapping(wgs84.tile_bbox(zoom, row, col))]) print "tile bounds" print "metaTilePyramid: %s" %([wgs84_meta.tile_bounds(zoom, row, col)]) print "tiles from bbox" #print "metaTilePyramid: %s" %([wgs84_meta.tiles_from_bbox(antimeridian, zoom)]) print "tiles from geometry" ## write debug output tiled_out = os.path.join(outdata_directory, "tile_antimeridian_tiles.geojson") schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(tiled_out) except: pass tiles = wgs84.tiles_from_geom(antimeridian, zoom) print "TilePyramid: %s" %(len(tiles)) with fiona.open(tiled_out, 'w', 'GeoJSON', schema) as sink: for tile in tiles: zoom, row, col = tile feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) ## write debug output metatiled_out = os.path.join(outdata_directory, "metatile_antimeridian_tiles.geojson") schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(metatiled_out) except: pass metatiles = wgs84_meta.tiles_from_geom(antimeridian, zoom) print "metaTilePyramid: %s" %(len(metatiles)) with fiona.open(metatiled_out, 'w', 'GeoJSON', schema) as sink: for metatile in metatiles: zoom, row, col = metatile feature = {} feature['geometry'] = mapping(wgs84_meta.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) for metatiling in (1, 2, 4, 8, 16): wgs84_meta = MetaTilePyramid(wgs84, metatiling) for zoom in range(22): tilepyramid_width = wgs84.matrix_width(zoom) tilepyramid_height = wgs84.matrix_height(zoom) metatilepyramid_width = wgs84_meta.matrix_width(zoom) metatilepyramid_height = wgs84_meta.matrix_height(zoom) control_width = int(math.ceil( float(tilepyramid_width)/float(metatiling) )) control_height = int(math.ceil( float(tilepyramid_height)/float(metatiling) )) try: assert metatilepyramid_width == control_width assert metatilepyramid_height == control_height except: print "ERROR: metatile number" print "metatiling, zoom:", metatiling, zoom print "width:", metatilepyramid_width, control_width print "height:", metatilepyramid_height, control_height raise for metatiling in (1, 2, 4, 8, 16): wgs84_meta = MetaTilePyramid(wgs84, metatiling) for zoom in range(21): # check tuple assert isinstance(wgs84_meta.matrix_width(zoom), int) assert isinstance(wgs84_meta.matrix_height(zoom), int) # check metatile size metatile_x_size = round(wgs84_meta.metatile_x_size(zoom), ROUND) metatile_y_size = round(wgs84_meta.metatile_y_size(zoom), ROUND) # assert metatile size equals TilePyramid width and height at zoom 0 if zoom == 0: try: if metatiling == 1: assert (metatile_x_size * 2) == wgs84.x_size else: assert metatile_x_size == wgs84.x_size assert metatile_y_size == wgs84.y_size except: print "ERROR: zoom 0 metatile size not correct" print "metatiling, zoom:", metatiling, zoom print "metatile_x_size:", wgs84_meta.metatiling, metatile_x_size, wgs84.x_size print "metatile_y_size:", metatile_y_size, wgs84.y_size raise ## assert metatile size within TilePyramid bounds try: assert (metatile_x_size > 0.0) and ( metatile_x_size <= wgs84.x_size) assert (metatile_y_size > 0.0) and ( metatile_y_size <= wgs84.y_size) except: print "ERROR: metatile size" print zoom print "metatile_x_size:", metatile_x_size, wgs84_meta.x_size print "metatile_y_size:", metatile_y_size, wgs84_meta.x_size raise ## calculate control size from tiles tile_x_size = wgs84.tile_x_size(zoom) tile_y_size = wgs84.tile_y_size(zoom) we_control_size = round(tile_x_size * float(metatiling), ROUND) if we_control_size > wgs84.x_size: we_control_size = wgs84.x_size ns_control_size = round(tile_y_size * float(metatiling), ROUND) if ns_control_size > wgs84.y_size: ns_control_size = wgs84.y_size try: assert metatile_x_size == we_control_size assert metatile_y_size == ns_control_size except: print "ERROR: metatile size and control sizes" print "zoom, metatiling:", zoom, metatiling print metatile_x_size, we_control_size print metatile_y_size, ns_control_size raise # check metatile pixelsize (resolution) pixel_x_size = round(wgs84.pixel_x_size(zoom), ROUND) ctr_pixel_x_size = round(wgs84_meta.pixel_x_size(zoom), ROUND) pixel_y_size = round(wgs84.pixel_y_size(zoom), ROUND) ctr_pixel_y_size = round(wgs84_meta.pixel_y_size(zoom), ROUND) try: assert pixel_x_size == ctr_pixel_x_size assert pixel_y_size == ctr_pixel_y_size except: print "ERROR: metatile pixel size" print "zoom, metatiling:", zoom, metatiling print "pixel_x_size:", pixel_x_size, ctr_pixel_x_size print "pixel_y_size:", pixel_y_size, ctr_pixel_y_size raise if debug: fiji_borders = os.path.join(testdata_directory, "fiji.geojson") with fiona.open(fiji_borders, "r") as fiji: geometries = [] for feature in fiji: geometry = shape(feature['geometry']) geometries.append(geometry) union = cascaded_union(geometries) # tiles fiji_tiles = os.path.join(outdata_directory, "fiji_tiles.geojson") schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(fiji_tiles) except: pass metatiling = 4 zoom = 10 tiles = wgs84.tiles_from_geom(union, zoom) with fiona.open(fiji_tiles, 'w', 'GeoJSON', schema) as sink: for tile in tiles: zoom, row, col = tile feature = {} feature['geometry'] = mapping(wgs84.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) # metatiles fiji_metatiles = os.path.join(outdata_directory, "fiji_metatiles.geojson") schema = { 'geometry': 'Polygon', 'properties': {'col': 'int', 'row': 'int'} } try: os.remove(fiji_metatiles) except: pass wgs84_meta = MetaTilePyramid(wgs84, metatiling) metatiles = wgs84_meta.tiles_from_geom(union, zoom) with fiona.open(fiji_metatiles, 'w', 'GeoJSON', schema) as sink: for metatile in metatiles: zoom, row, col = metatile feature = {} feature['geometry'] = mapping(wgs84_meta.tile_bbox(zoom, row, col)) feature['properties'] = {} feature['properties']['col'] = col feature['properties']['row'] = row sink.write(feature) ## test get neighbors metatiling = 1 wgs84_meta = MetaTilePyramid(wgs84, metatiling) tile = wgs84_meta.tile(5, 4, 3) assert len(tile.get_neighbors()) == 8 ## test tile <--> metatile conversion metatile = [(10, 44, 33)] metatiling = 4 wgs84_meta = MetaTilePyramid(wgs84, metatiling) small_tile = wgs84_meta.tilepyramid.tile(10, 178, 133) test_metatile = [ (tile.zoom, tile.row, tile.col) for tile in wgs84_meta.tiles_from_bbox( small_tile.bbox(), 10 ) ] try: assert metatile == test_metatile print "OK: metatile <-> tile conversion" except: print metatile, test_metatile raise print "ERROR: metatile <-> tile conversion" # test mercator tile pyramid tile_pyramid = TilePyramid("mercator") assert tile_pyramid.srid == 3857 try: for zoom in range(15): assert ( (tile_pyramid.matrix_width(zoom), tile_pyramid.matrix_height(zoom) ) == (2**zoom, 2**zoom) ) print "OK: mercator tile matrix widths" except: print "ERROR: mercator tile matrix widths" from shapely.ops import transform from functools import partial import pyproj import mercantile example_tiles = [ (12, 1024, 512), (6, 32, 16), (0, 0, 0), ] for tile_idx in example_tiles: (zoom, row, col) = tile_idx mercantile_ul = Point( mercantile.ul(col, row, zoom).lng, mercantile.ul(col, row, zoom).lat, ) m_bounds = mercantile.bounds(col, row, zoom) mercantile_bbox = box(*m_bounds) project = partial( pyproj.transform, pyproj.Proj({"init": "epsg:3857"}), pyproj.Proj({"init": "epsg:4326"}) ) tilematrix_ul = transform( project, Point( Tile(tile_pyramid, zoom, row, col).left, Tile(tile_pyramid, zoom, row, col).top, ) ) tilematrix_bbox = transform( project, Tile(tile_pyramid, zoom, row, col).bbox() ) try: assert mercantile_ul.almost_equals( tilematrix_ul, decimal=GEOM_EQUALS_ROUND ) assert mercantile_bbox.almost_equals( tilematrix_bbox, decimal=GEOM_EQUALS_ROUND ) print "OK: mercator tile coordinates" except AssertionError: print "ERROR: mercator tile coordinates" print tile_idx, mercantile_ul, tilematrix_ul print tile_idx, mercantile_bbox, tilematrix_bbox tile_idx = (12, 1024, 512) tile = Tile(tile_pyramid, *tile_idx) for child in tile.get_children(): try: assert child.get_parent().id == tile.id print "OK: tile children and parent" except AssertionError: print child.parent.id, tile.id print "ERROR: tile children and parent" # get tiles over antimeridian: tile_pyramid = TilePyramid("geodetic") tile = tile_pyramid.tile(5, 0, 63) target_tiles = set( target_tile.id for target_tile in tile_pyramid.tiles_from_bounds(tile.bounds(256), 5) ) control_tiles = set( [ # tiles west of antimeridian (5, 0, 62), (5, 0, 63), (5, 1, 62), (5, 1, 63), # tiles east of antimeridian (5, 0, 0), (5, 1, 0) ] ) try: diff = control_tiles.difference(target_tiles) assert len(diff) == 0 print "OK: tiles over antimeridian" except AssertionError: print "ERROR: tiles over antimeridian" print diff tile = tile_pyramid.tile(5, 0, 0) target_tiles = set( target_tile.id for target_tile in tile_pyramid.tiles_from_bounds(tile.bounds(256), 5) ) control_tiles = set( [ # tiles west of antimeridian (5, 0, 63), (5, 1, 63), # tiles east of antimeridian (5, 0, 0), (5, 1, 0), (5, 0, 1), (5, 1, 1) ] ) try: diff = control_tiles.difference(target_tiles) assert len(diff) == 0 print "OK: tiles over antimeridian" except AssertionError: print "ERROR: tiles over antimeridian" print diff # get tiles over antimeridian: tile_pyramid = TilePyramid("mercator") tile = tile_pyramid.tile(5, 0, 31) target_tiles = set( target_tile.id for target_tile in tile_pyramid.tiles_from_bounds(tile.bounds(256), 5) ) control_tiles = set( [ # tiles west of antimeridian (5, 0, 30), (5, 0, 31), (5, 1, 30), (5, 1, 31), # tiles east of antimeridian (5, 0, 0), (5, 1, 0) ] ) try: diff = control_tiles.difference(target_tiles) assert len(diff) == 0 print "OK: tiles over antimeridian" except AssertionError: print "ERROR: tiles over antimeridian" print diff tile = tile_pyramid.tile(5, 0, 0) target_tiles = set( target_tile.id for target_tile in tile_pyramid.tiles_from_bounds(tile.bounds(256), 5) ) control_tiles = set( [ # tiles west of antimeridian (5, 0, 31), (5, 1, 31), # tiles east of antimeridian (5, 0, 0), (5, 1, 0), (5, 0, 1), (5, 1, 1) ] ) try: diff = control_tiles.difference(target_tiles) assert len(diff) == 0 print "OK: tiles over antimeridian" except AssertionError: print "ERROR: tiles over antimeridian" print diff # geometry over antimeridian tile_pyramid = TilePyramid("geodetic") geometry = loads("POLYGON ((184.375 90, 190 90, 180 84.375, 174.375 84.375, 184.375 90))") target_tiles = set( tile.id for tile in tile_pyramid.tiles_from_geom(geometry, 6) ) control_tiles = set( [ (6, 0, 127), (6, 1, 126), (6, 1, 127), (6, 0, 0), (6, 0, 1), (6, 0, 2), (6, 0, 3), (6, 1, 0), (6, 1, 1) ] ) try: diff = control_tiles.difference(target_tiles) assert len(diff) == 0 print "OK: geometry over antimeridian" except AssertionError: print "ERROR: geometry over antimeridian" print diff tile_pyramid = TilePyramid("mercator") geometry = loads("POLYGON ((-22037508.3427892 20037508.3427892, -20785164.07136488 20037508.3427892, -18785164.07136488 18785164.07136488, -20037508.3427892 18785164.07136488, -22037508.3427892 20037508.3427892))") target_tiles = set( tile.id for tile in tile_pyramid.tiles_from_geom(geometry, 6) ) tile_pyramid_bbox = box( tile_pyramid.left, tile_pyramid.bottom, tile_pyramid.right, tile_pyramid.top ) for tile in target_tiles: assert tile_pyramid.tile(*tile).bbox().within(tile_pyramid_bbox) control_tiles = set( [ # west of antimeridian (6, 0, 60), (6, 0, 61), (6, 0, 62), (6, 0, 63), (6, 1, 62), (6, 1, 63), # east of antimeridian (6, 0, 0), (6, 1, 0), (6, 1, 1) ] ) try: diff = control_tiles.difference(target_tiles) assert len(diff) == 0 print "OK: geometry over antimeridian" except AssertionError: print "ERROR: geometry over antimeridian" print target_tiles print diff # tile shapes tile_pyramid = TilePyramid("mercator") col, row = (0, 0) for zoom in range(10): tile = Tile(tile_pyramid, zoom, col, row) assert tile.shape() == (256, 256) metatile_pyramid = MetaTilePyramid(tile_pyramid, metatiles=8) control_shapes = [ (256, 256), (512, 512), (1024, 1024), (2048, 2048), (2048, 2048), (2048, 2048), (2048, 2048), (2048, 2048), (2048, 2048), (2048, 2048) ] for zoom, control_shape in zip(range(9), control_shapes): tile = Tile(metatile_pyramid, zoom, col, row) try: assert tile.shape() == control_shape print "OK: metatile shape at zoom ", zoom except AssertionError: print "ERROR: metatile shape at zoom", zoom print tile.id, tile.shape(), control_shape raise # tile shapes with pixelbuffer tile_pyramid = TilePyramid("mercator") test_tiles = [ (0, 0, 0), (1, 0, 0), # top left (2, 0, 1), # top middle (2, 0, 3), # top right (2, 3, 3), # bottom right (2, 3, 0), # bottom left (2, 3, 2), # bottom middle (2, 2, 0), # left middle (2, 2, 3), # right middle ] pixelbuffer = 2 tile_size = tile_pyramid.tile_size control_shapes = [ (tile_size, tile_size+2*pixelbuffer), (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer), # top left (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer), # top middle (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer), # top right (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer), # bottom right (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer), # bottom left (tile_size+1*pixelbuffer, tile_size+2*pixelbuffer), # bottom middle (tile_size+2*pixelbuffer, tile_size+2*pixelbuffer), # left middle (tile_size+2*pixelbuffer, tile_size+2*pixelbuffer), # right middle ] for test_tile, control_shape in zip(test_tiles, control_shapes): tile = Tile(tile_pyramid, *test_tile) try: assert tile.shape(pixelbuffer) == control_shape print "OK: tile pixelbuffer shape for tile ", test_tile except AssertionError: print "ERROR: tile pixelbuffer shape for tile", test_tile print tile.id, tile.shape(pixelbuffer), control_shape # metatile shapes with pixelbuffer pixelbuffer = 2 metatiling = 8 metatile_pyramid = MetaTilePyramid(tile_pyramid, metatiles=metatiling) tile_size = tile_pyramid.tile_size test_tiles = [ (0, 0, 0), (1, 0, 0), (2, 0, 0), (3, 0, 0), (4, 0, 0), (5, 0, 0), # top left (5, 0, 1), # top middle (5, 0, 3), # top right (5, 3, 3), # bottom right (5, 3, 0), # bottom left (5, 3, 2), # bottom middle (5, 2, 0), # left middle (5, 2, 3), # right middle ] for test_tile in test_tiles: tile = Tile(metatile_pyramid, *test_tile) left, bottom, right, top = tile.bounds(pixelbuffer) control_shape = ( int(round((top-bottom)/tile.pixel_y_size)), int(round((right-left)/tile.pixel_x_size)) ) try: assert tile.shape(pixelbuffer) == control_shape print "OK: tile pixelbuffer shape for metatile ", test_tile except AssertionError: print "ERROR: tile pixelbuffer shape for metatile", test_tile print tile.id, tile.shape(pixelbuffer), control_shape """intersecting function""" try: # equal metatiling: tile = Tile(TilePyramid("geodetic", metatiling=1), 3, 4, 5) pyramid = TilePyramid("geodetic", metatiling=1) assert len(tile.intersecting(pyramid)) == 1 assert tile.intersecting(pyramid)[0].id == tile.id # different metatiling: tile = Tile(TilePyramid("geodetic", metatiling=1), 3, 4, 5) pyramid_metatiling = 2 pyramid = TilePyramid("geodetic", metatiling=pyramid_metatiling) assert len(tile.intersecting(pyramid)) == 1 intersect = tile.intersecting(pyramid)[0] assert tile.bbox().within(intersect.bbox()) tile = Tile(TilePyramid("geodetic", metatiling=8), 13, 4, 5) pyramid_metatiling = 2 pyramid = TilePyramid("geodetic", metatiling=pyramid_metatiling) assert len(tile.intersecting(pyramid)) == 16 for intersect in tile.intersecting(pyramid): assert intersect.bbox().within(tile.bbox()) tile_list = set( tile.id for tile in tile.intersecting(pyramid) ) reversed_list = set( tile.id for tile in pyramid.intersecting(tile) ) assert not len(tile_list.symmetric_difference(reversed_list)) print "OK: intersecting function" except: print "ERROR: intersecting function" raise """affine objects""" try: for metatiling in [1, 2, 4, 8, 16]: pyramid = TilePyramid("geodetic", metatiling=metatiling) for zoom in range(22): tile = pyramid.tile(zoom, 0, 0) assert tile.pixel_x_size == tile.pixel_y_size assert tile.affine()[0] == -tile.affine()[4] assert tile.affine(pixelbuffer=10)[0] == \ -tile.affine(pixelbuffer=10)[4] print "OK: Affine objects" except: print "ERROR: Affine objects" raise
def z_key(tile): if tile.z > 1: return quadtree.encode(*list(reversed(mercantile.ul(*tile))), precision=tile.z) else: return ""
meta.update( driver='GTiff', dtype=rasterio.uint8, compress="deflate", predictor=1, nodata=None, tiled=True, sparse_ok=True, blockxsize=DST_BLOCK_SIZE, blockysize=DST_BLOCK_SIZE, height=DST_TILE_HEIGHT, width=DST_TILE_WIDTH, ) # Get the bounds of the tile. ulx, uly = mercantile.xy(*mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy(*mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) meta["affine"] = src.window_transform(window) ls = LightSource() hs = ls.hillshade(data, dx=dx, dy=dy, ) hs = (255.0 * hs).astype(np.uint8) with rasterio.open("windowed.tif", "w", **meta) as dst: # ignore the border pixels when writing dst.write(hs[BUFFER:-BUFFER, BUFFER:-BUFFER], 1)
def tile2degree(xtile, ytile, zoom): n = 2.0**zoom lng = xtile / n * 360.0 - 180.0 lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) lat = math.degrees(lat_rad) return (lng, lat) zoom = 22 # chongqing lng, lat = 106.516034, 29.543124 print(lng, lat) tile = mercantile.tile(lng, lat, zoom) print(tile) print(mercantile.ul(*tile)) box = mercantile.bounds(*tile) print(box) distance = great_circle((lat, box.west), (lat, box.east)) print(distance.meters, 'm') distance = great_circle((box.north, lng), (box.south, lng)) print(distance.meters, 'm') # beijing lng, lat = 116.324662, 39.961028 xtile, ytile = degree2tile(lng, lat, zoom) print(lng, lat) print(xtile, ytile) print(tile2degree(xtile, ytile, zoom)) box = mercantile.bounds(xtile, ytile, zoom) print(box)
def _get_buffered_bounds(minlon, minlat, maxlon, maxlat, zoom=18, classic=True): """ minlon, minlat, maxlon, maxlat @float : Bbox limits; zoon @integer : Square tile zoom level or hexagonal tile resolution; classic @boolean : Whether to use classic square tiles or Uber H3 hexagonal ones. Returns the bbox limits that fits to the tiles touched by the bbox area introduced. In this way you are sure to fetch all the inolved points. """ resolution = zoom if classic: # resolution = zoom ultile = mt.tile(minlon, maxlat, zoom) left, top = mt.ul(*ultile) rbtile = mt.tile(maxlon, minlat, zoom) brtile = lambda x, y, z: (x+1, y+1, z) right, bottom = mt.ul(*brtile(*rbtile)) # get_tile = lambda lon, lat: mt.tile(lon, lat, zoom) else: # ultile = h3.geo_to_h3(maxlat, minlon, zoom) # resolution = min(12, zoom) ultile = h3.geo_to_h3(maxlat, minlon, resolution) ulboundary = h3.h3_to_geo_boundary(ultile) P1 = Point(ulboundary[0][::-1]) P3 = Point(ulboundary[3][::-1]) P1_3857 = transform(transformer.transform, P1) P3_3857 = transform(transformer.transform, P3) buffer = P1_3857.distance(P3_3857) ul_3857_ = transform(transformer.transform, Point((minlon, maxlat,))) ul_3857 = translate(ul_3857_, -buffer, buffer) br_3857_ = transform(transformer.transform, Point((maxlon, minlat,))) br_3857 = translate(br_3857_, buffer, -buffer) left, top = to3857(*ul_3857.coords[0], inverse=True) right, bottom = to3857(*br_3857.coords[0], inverse=True) # import pdb; pdb.set_trace() # # bltile = h3.geo_to_h3(minlat, minlon, resolution) # blboundary = h3.h3_to_geo_boundary(bltile) # # brtile = h3.geo_to_h3(minlat, maxlon, resolution) # brboundary = h3.h3_to_geo_boundary(brtile) # # urtile = h3.geo_to_h3(maxlat, maxlon, resolution) # urboundary = h3.h3_to_geo_boundary(urtile) # # lats, lons = zip(*chain(ulboundary, brboundary, blboundary, urboundary)) # left, right = min(lons), max(lons) # top, bottom = max(lats), min(lats) return left, bottom, right, top, resolution,
def process_tile(tile): """Process a single MBTiles tile Parameters ---------- tile : mercantile.Tile Returns ------- tile : mercantile.Tile The input tile. bytes : bytearray Image bytes corresponding to the tile. """ global base_kwds, resampling, src # Get the bounds of the tile. ulx, uly = mercantile.xy( *mercantile.ul(tile.x, tile.y, tile.z)) lrx, lry = mercantile.xy( *mercantile.ul(tile.x + 1, tile.y + 1, tile.z)) kwds = base_kwds.copy() kwds['transform'] = transform_from_bounds(ulx, lry, lrx, uly, kwds['width'], kwds['height']) src_nodata = kwds.pop('src_nodata', None) dst_nodata = kwds.pop('dst_nodata', None) warnings.simplefilter('ignore') with MemoryFile() as memfile: with memfile.open(**kwds) as tmp: # determine window of source raster corresponding to the tile # image, with small buffer at edges try: west, south, east, north = transform_bounds(TILES_CRS, src.crs, ulx, lry, lrx, uly) tile_window = window_from_bounds(west, south, east, north, transform=src.transform) adjusted_tile_window = Window( tile_window.col_off - 1, tile_window.row_off - 1, tile_window.width + 2, tile_window.height + 2) tile_window = adjusted_tile_window.round_offsets().round_shape() # if no data in window, skip processing the tile if not src.read_masks(1, window=tile_window).any(): return tile, None except ValueError: log.info("Tile %r will not be skipped, even if empty. This is harmless.", tile) reproject(rasterio.band(src, tmp.indexes), rasterio.band(tmp, tmp.indexes), src_nodata=src_nodata, dst_nodata=dst_nodata, num_threads=1, resampling=resampling) return tile, memfile.read()
def tile2degree(xtile, ytile, zoom): n = 2.0 ** zoom lng = xtile / n * 360.0 - 180.0 lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) lat = math.degrees(lat_rad) return (lng, lat) zoom = 22 # chongqing lng, lat = 106.516034, 29.543124 print lng, lat tile = mercantile.tile(lng, lat, zoom) print tile print mercantile.ul(*tile) box = mercantile.bounds(*tile) print box distance = great_circle((lat, box.west), (lat, box.east)) print distance.meters, 'm' distance = great_circle((box.north, lng), (box.south, lng)) print distance.meters, 'm' # beijing lng, lat = 116.324662, 39.961028 xtile, ytile = degree2tile(lng, lat, zoom) print lng, lat print xtile, ytile print tile2degree(xtile, ytile, zoom) box = mercantile.bounds(xtile, ytile, zoom) print box
# https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/get-a-static-map # https://discourse.metabase.com/t/satellite-map/7969 # https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/imagery-metadata count = 0 for i in range(meta_data["begin_x"],meta_data["end_x"]+1): for j in range(meta_data["begin_y"], meta_data["end_y"]+1): # Fetching the map-tile for satellite image # im_s = ts.tile_as_image(i,j,meta_data["zoom_level"]) print("The current count : ",count) # Finding the lat long co_ordinates = mercantile.ul(i, j, meta_data["zoom_level"]) print(co_ordinates.lng , co_ordinates.lat) # Storing the data my_data["Sector"].append("sector"+str(count)) my_data["Lat"].append(co_ordinates.lat) my_data["Long"].append(co_ordinates.lng) my_data["Name"].append(getName(co_ordinates.lat, co_ordinates.lng)) my_data["Greenery"].append(0) # Saving the images/satellite # path_s = os.path.join("images","roadmap","sector"+str(count)+".png") # im_s.save(path_s)
def create_overview_cogs( mosaic_path: str, output_profile: Dict, prefix: str = "mosaic_ovr", max_overview_level: int = 6, method: str = "first", config: Dict = None, threads=1, in_memory: bool = True, ) -> None: """ Create Low resolution mosaic image from a mosaicJSON. The output will be a web optimized COG with bounds matching the mosaicJSON bounds and with its resolution matching the mosaic MinZoom - 1. Attributes ---------- mosaic_path : str, required Mosaic definition path. output_profile : dict, required prefix : str max_overview_level : int method: str, optional pixel_selection method name (default is 'first'). config : dict Rasterio Env options. threads: int, optional maximum number of threads to use (default is 1). in_memory: bool, optional Force COG creation in memory (default is True). """ pixel_method = PIXSEL_METHODS[method] with MosaicBackend(mosaic_path) as mosaic: base_zoom = mosaic.metadata["minzoom"] - 1 mosaic_quadkey_zoom = mosaic.quadkey_zoom bounds = mosaic.metadata["bounds"] mosaic_quadkeys = set(mosaic._quadkeys) # Select a random quakey/asset and get dataset info tile = mercantile.quadkey_to_tile(random.sample(mosaic_quadkeys, 1)[0]) assets = mosaic.assets_for_tile(*tile) info = _get_info(assets[0]) extrema = tile_extrema(bounds, base_zoom) tilesize = 256 resolution = _meters_per_pixel(base_zoom, 0, tilesize=tilesize) # Create multiples files if coverage is too big extremas = _split_extrema(extrema, max_ovr=max_overview_level) for ix, extrema in enumerate(extremas): click.echo(f"Part {1 + ix}/{len(extremas)}", err=True) output_path = f"{prefix}_{ix}.tif" blocks = list(_get_blocks(extrema, tilesize)) random.shuffle(blocks) width = (extrema["x"]["max"] - extrema["x"]["min"]) * tilesize height = (extrema["y"]["max"] - extrema["y"]["min"]) * tilesize w, n = mercantile.xy(*mercantile.ul( extrema["x"]["min"], extrema["y"]["min"], base_zoom)) params = dict( driver="GTiff", dtype=info["dtype"], count=len(info["band_descriptions"]), width=width, height=height, crs="epsg:3857", transform=Affine(resolution, 0, w, 0, -resolution, n), nodata=info["nodata_value"], ) params.update(**output_profile) config = config or {} with rasterio.Env(**config): with ExitStack() as ctx: if in_memory: tmpfile = ctx.enter_context(MemoryFile()) tmp_dst = ctx.enter_context(tmpfile.open(**params)) else: tmpfile = ctx.enter_context( TemporaryRasterFile(output_path)) tmp_dst = ctx.enter_context( rasterio.open(tmpfile.name, "w", **params)) def _get_tile(wind): idx, window = wind x = extrema["x"]["min"] + idx[1] y = extrema["y"]["min"] + idx[0] t = mercantile.Tile(x, y, base_zoom) kds = set(find_quadkeys(t, mosaic_quadkey_zoom)) if not mosaic_quadkeys.intersection(kds): return window, None, None try: (tile, mask), _ = mosaic.tile( t.x, t.y, t.z, tilesize=tilesize, pixel_selection=pixel_method(), ) except NoAssetFoundError: return window, None, None return window, tile, mask with futures.ThreadPoolExecutor( max_workers=threads) as executor: future_work = [ executor.submit(_get_tile, item) for item in blocks ] with click.progressbar( futures.as_completed(future_work), length=len(future_work), show_percent=True, label="Loading tiles", ) as future: for res in future: pass for f in _filter_futures(future_work): window, tile, mask = f if tile is None: continue tmp_dst.write(tile, window=window) if info["nodata_type"] == "Mask": tmp_dst.write_mask(mask.astype("uint8"), window=window) min_tile_size = tilesize = min( int(output_profile["blockxsize"]), int(output_profile["blockysize"]), ) overview_level = get_maximum_overview_level( tmp_dst.width, tmp_dst.height, minsize=min_tile_size) overviews = [2**j for j in range(1, overview_level + 1)] tmp_dst.build_overviews(overviews) copy(tmp_dst, output_path, copy_src_overviews=True, **params)
def to_mbtiles(self, filename, zoom=None, tile_bounds=False, drop_empty=False): """ Export tile package to mbtiles v1.1 file, optionally limited to zoom levels. If filename exists, it will be overwritten. If filename does not include the suffix '.mbtiles' it will be added. Parameters ---------- filename: string name of mbtiles file zoom: int or list-like of ints, default: None (all tiles exported) zoom levels to export to mbtiles tile_bounds: bool if True, will use the tile bounds of the highest zoom level exported to determine tileset bounds drop_empty: bool, default False if True, tiles that are empty will not be output """ if self.format.lower() == "mixed": raise ValueError( "Mixed format tiles are not supported for export to mbtiles") if not filename.endswith(".mbtiles"): filename = "{0}.mbtiles".format(filename) with MBtiles(filename, "w") as mbtiles: if zoom is None: zoom = self.zoom_levels elif isinstance(zoom, int): zoom = [zoom] zoom = list(zoom) zoom.sort() tiles = (tile for tile in self.read_tiles(zoom, flip_y=True) if not (drop_empty and hashlib.sha1( tile.data).hexdigest() in EMPTY_TILES)) mbtiles.write_tiles(tiles) if tile_bounds: # Calculate bounds based on maximum zoom to be exported highest_zoom = zoom[-1] min_row, max_row = mbtiles.row_range(highest_zoom) min_col, max_col = mbtiles.col_range(highest_zoom) # get upper left coordinate xmin, ymax = mercantile.ul(min_col, min_row, highest_zoom) # get bottom right coordinate # since we are using ul(), we need to go 1 tile beyond the range to get the right side of the # tiles we have xmax, ymin = mercantile.ul(max_col + 1, max_row + 1, highest_zoom) bounds = (xmin, ymin, xmax, ymax) else: bounds = self.bounds # Center zoom level is middle zoom level center = "{0:4f},{1:4f},{2}".format( bounds[0] + (bounds[2] - bounds[0]) / 2.0, bounds[1] + (bounds[3] - bounds[1]) / 2.0, (zoom[0] + zoom[-1]) // 2, ) mbtiles.meta.update({ "name": self.name, "description": self.summary, # not description, which is optional "version": self.version, "attribution": self.attribution, "tags": self.tags, "credits": self.credits, "use_constraints": self.use_constraints, "type": "overlay", "format": self.format.lower().replace("jpeg", "jpg")[:3], "bounds": ",".join("{0:4f}".format(v) for v in bounds), "center": center, "minzoom": zoom[0], "maxzoom": zoom[-1], "legend": json.dumps(self.legend) if self.legend else "", })