def test_xy_bounds(args): expected = (-1017529.7205322663, 7005300.768279833, -978393.962050256, 7044436.526761846) bounds = mercantile.xy_bounds(*args) for a, b in zip(expected, bounds): assert round(a - b, 7) == 0
def tile(address, tile_x, tile_y, tile_z, rgb=None, tilesize=256, nodata=None, alpha=None): """Create mercator tile from any images. Attributes ---------- address : str file url. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. rgb : tuple, int, optional (default: (1, 2, 3)) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. nodata: int or float, optional (defaults: None) alpha: int, optional (defaults: None) Force alphaband if not present in the dataset metadata Returns ------- data : numpy ndarray mask: numpy array """ with rasterio.open(address) as src: wgs_bounds = transform_bounds(*[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) nodata = nodata if nodata is not None else src.nodata if not rgb: rgb = src.indexes if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds('Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) return utils.tile_band_worker(address, tile_bounds, tilesize, indexes=rgb, nodata=nodata, alpha=alpha)
def worker(path): raster = rasterio_open(path) w, s, e, n = transform_bounds(raster.crs, "EPSG:4326", *raster.bounds) transform, _, _ = calculate_default_transform(raster.crs, "EPSG:3857", raster.width, raster.height, w, s, e, n) tiles = [mercantile.Tile(x=x, y=y, z=z) for x, y, z in mercantile.tiles(w, s, e, n, args.zoom)] tiled = [] for tile in tiles: try: w, s, e, n = mercantile.xy_bounds(tile) # inspired by rio-tiler, cf: https://github.com/mapbox/rio-tiler/pull/45 warp_vrt = WarpedVRT( raster, crs="epsg:3857", resampling=Resampling.bilinear, add_alpha=False, transform=from_bounds(w, s, e, n, args.ts, args.ts), width=math.ceil((e - w) / transform.a), height=math.ceil((s - n) / transform.e), ) data = warp_vrt.read( out_shape=(len(raster.indexes), args.ts, args.ts), window=warp_vrt.window(w, s, e, n) ) image = np.moveaxis(data, 0, 2) # C,H,W -> H,W,C except: sys.exit("Error: Unable to tile {} from raster {}.".format(str(tile), raster)) tile_key = (str(tile.x), str(tile.y), str(tile.z)) if not args.label and len(tiles_map[tile_key]) == 1 and is_border(image): progress.update() continue if len(tiles_map[tile_key]) > 1: out = os.path.join(splits_path, str(tiles_map[tile_key].index(path))) else: out = args.out x, y, z = map(int, tile) if not args.label: ret = tile_image_to_file(out, mercantile.Tile(x=x, y=y, z=z), image) if args.label: ret = tile_label_to_file(out, mercantile.Tile(x=x, y=y, z=z), palette, image) if not ret: sys.exit("Error: Unable to write tile {} from raster {}.".format(str(tile), raster)) if len(tiles_map[tile_key]) == 1: progress.update() tiled.append(mercantile.Tile(x=x, y=y, z=z)) return tiled
def test_xy_bounds(args): expected = ( -1017529.7205322663, 7005300.768279833, -978393.962050256, 7044436.526761846, ) bounds = mercantile.xy_bounds(*args) for a, b in zip(expected, bounds): assert round(a - b, 7) == 0
def test_tile_read_extmask(): """Read masked area.""" # non-boundless tile covering the masked part mercator_tile = mercantile.Tile(x=876431, y=1603669, z=22) bounds = mercantile.xy_bounds(mercator_tile) with rasterio.Env(GDAL_DISABLE_READDIR_ON_OPEN="TRUE"): with rasterio.open(S3_EXTMASK_PATH) as src_dst: arr, mask = reader.part(src_dst, bounds, 256, 256) assert arr.shape == (3, 256, 256) assert mask.shape == (256, 256) assert not mask.all() # boundless tile covering the masked part mercator_tile = mercantile.Tile(x=876431, y=1603668, z=22) bounds = mercantile.xy_bounds(mercator_tile) with rasterio.Env(GDAL_DISABLE_READDIR_ON_OPEN="EMPTY_DIR"): with rasterio.open(S3_MASK_PATH) as src_dst: arr, mask = reader.part(src_dst, bounds, 256, 256) assert arr.shape == (3, 256, 256) assert not mask.all()
def __init__(self, access_token=os.environ.get("DG_MAPS_API_TOKEN"), url="https://api.mapbox.com/v4/digitalglobe.nal0g75k/{z}/{x}/{y}.png", zoom=22, bounds=None): self.zoom_level = zoom self._token = access_token self._name = "image-{}".format(str(uuid.uuid4())) self._url_template = url + "?access_token={token}" _first_tile = mercantile.Tile(z=self.zoom_level, x=0, y=0) _last_tile = mercantile.Tile(z=self.zoom_level, x=180, y=-85.05) g = box(*mercantile.xy_bounds(_first_tile)).union(box(*mercantile.xy_bounds(_last_tile))) self._full_bounds = g.bounds # TODO: populate rest of fields automatically self._tile_size = 256 self._nbands = 3 self._dtype = "uint8" self.bounds = self._expand_bounds(bounds) self._chunks = tuple([self._nbands] + [self._tile_size, self._tile_size])
def test_crop_and_get_tile_do_the_same_when_image_is_populated(self): coords = mercantile.xy_bounds(*tiles[15]) shape = GeoVector(Polygon.from_bounds(*coords), WEB_MERCATOR_CRS) raster = self.read_only_virtual_geo_raster() with NamedTemporaryFile(mode='w+b', suffix=".tif") as rf: raster.save(rf.name) raster = GeoRaster2.open(rf.name) tile15 = raster.get_tile(*tiles[15]) raster._populate_from_rasterio_object(read_image=True) cropped_15 = raster.crop(shape, MERCATOR_RESOLUTION_MAPPING[15]) self.assertEqual(tile15, cropped_15)
def geotiff_options( tile_x, tile_y, tile_z, tilesize: int = 256, dst_crs: CRS = CRS.from_epsg(3857)) -> Dict: """GeoTIFF options.""" bounds = mercantile.xy_bounds(mercantile.Tile(x=tile_x, y=tile_y, z=tile_z)) dst_transform = from_bounds(*bounds, tilesize, tilesize) return dict(crs=dst_crs, transform=dst_transform)
def read_tile(self, z, x, y): """Read raster tile data and mask.""" mercator_tile = mercantile.Tile(x=x, y=y, z=z) tile_bounds = mercantile.xy_bounds(mercator_tile) return tile_read( self.path, tile_bounds, self.tiles_size, indexes=self.indexes, nodata=self.nodata, )
def __init__(self, extent, map_dir, map_zoom=(MIN_ZOOM, MAX_ZOOM)): self._map_dir = map_dir self._min_zoom = map_zoom[0] self._max_zoom = map_zoom[1] # We need a manager to share the list of database names between processes self._manager = multiprocessing.Manager() self._database_names = self._manager.list() # Get whole tiles that span the image's extent self._tiles = list(mercantile.tiles(*extent, self._max_zoom)) tile_0 = self._tiles[0] tile_N = self._tiles[-1] self._tile_start_coords = (tile_0.x, tile_0.y) self._tile_end_coords = (tile_N.x, tile_N.y) # Tiled area in world coordinates (metres) bounds_0 = mercantile.xy_bounds(tile_0) bounds_N = mercantile.xy_bounds(tile_N) tile_world = Rect(bounds_0.left, bounds_0.top, bounds_N.right, bounds_N.bottom) # Tiled area in tile pixel coordinates tile_extent = Rect(0, 0, TILE_SIZE[0] * (tile_N.x - tile_0.x + 1), TILE_SIZE[1] * (tile_N.y - tile_0.y + 1)) # Affine transform from world to tile pixel coordinates sx = tile_extent.width / tile_world.width sy = tile_extent.height / tile_world.height world_to_tile = Affine((sx, -sy), (tile_world.x0, tile_world.y0), (0, 0)) # Extent in world coordinates (metres) sw = mercantile.xy(*extent[:2]) ne = mercantile.xy(*extent[2:]) # Converted to tile pixel coordinates self._image_rect = Rect(world_to_tile.transform(sw[0], ne[1]), world_to_tile.transform(ne[0], sw[1])) self._processes = []
def geojson_tile_burn(tile, features, tile_size, burn_value=1, epsg=4326): """Burn tile with GeoJSON features.""" shapes = ((geometry, burn_value) for feature in features for geometry in geojson_to_mercator(feature, epsg)) bounds = mercantile.xy_bounds(tile) transform = from_bounds(*bounds, tile_size, tile_size) return rasterize(shapes, out_shape=(tile_size, tile_size), transform=transform)
def read_val_at_pixel(self, grid, lat, lon, zoom): """Interpolate the row/column of a webtile from a lat/lon/zoom and extract the corresponding value from `grid`.""" tile = mercantile.tile(lon, lat, zoom) size = len(grid) box = mercantile.xy_bounds(tile) x, y = mercantile.xy(lon, lat) width = box.right - box.left height = box.top - box.bottom i = int(round(size * (box.top - y) / height)) j = int(round(size * (x - box.left) / width)) return grid[i][j]
def test_crop_and_get_tile_do_the_same_when_image_is_populated_first_for_low_zoom( self): coords = mercantile.xy_bounds(*tiles[11]) shape = GeoVector(Polygon.from_bounds(*coords), WEB_MERCATOR_CRS) raster = self.metric_raster() with NamedTemporaryFile(mode='w+b', suffix=".tif") as rf: raster.save(rf.name) raster = GeoRaster2.open(rf.name) raster._populate_from_rasterio_object(read_image=True) tile11 = raster.get_tile(*tiles[11]) cropped_11 = raster.crop(shape, mercator_zoom_to_resolution[11]) self.assertEqual(tile11, cropped_11)
def test_crop_and_get_tile_do_the_same(self): coords = mercantile.xy_bounds(*tiles[15]) shape = GeoVector(Polygon.from_bounds(*coords), WEB_MERCATOR_CRS) raster = self.metric_raster() with NamedTemporaryFile(mode='w+b', suffix=".tif") as rf: raster.save(rf.name) raster2 = GeoRaster2.open(rf.name) tile15 = raster2.get_tile(*tiles[15]) # load the image data raster2.image cropped15 = raster2.crop(shape, mercator_zoom_to_resolution[15]) self.assertEqual(tile15, cropped15)
def tile(bucket, key, tile_x, tile_y, tile_z, rgb=(1, 2, 3), tilesize=256, prefix='s3:/'): """Create mercator tile from AWS hosted images and encodes it in base64. Attributes ---------- bucket : str AWS bucket's name. key : str AWS file's key. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. tileformat : str Image format to return (Accepted: "jpg" or "png") rgb : tuple, int, optional (default: (1, 2, 3)) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. Returns ------- out : numpy ndarray """ source_address = '{}/{}/{}'.format(prefix, bucket, key) with rasterio.open(source_address) as src: wgs_bounds = transform_bounds( *[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) nodata = src.nodata if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( 'Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) w, s, e, n = tile_bounds out = utils.tile_band_worker(source_address, tile_bounds, tilesize, indexes=rgb, nodata=nodata) return out
def _download_image(self): """ return glued tiles as PIL image :param west: west longitude in degrees :param south: south latitude in degrees :param east: east longitude in degrees :param north: north latitude in degrees :param zoom: wanted size :return: Image """ tiles = list( mercantile.tiles(self._west, self._south, self._east, self._north, self.zoom)) min_x = min_y = max_x = max_y = None for tile in tiles: min_x = min(min_x, tile.x) if min_x is not None else tile.x min_y = min(min_y, tile.y) if min_y is not None else tile.y max_x = max(max_x, tile.x) if max_x is not None else tile.x max_y = max(max_y, tile.y) if max_y is not None else tile.y out_img = PIL.Image.new('RGB', ((max_x - min_x + 1) * self.TILE_SIZE, (max_y - min_y + 1) * self.TILE_SIZE)) pool = multiprocessing.Pool(self.pool_workers) results = pool.map(self._download_tile, tiles) pool.close() pool.join() for img, tile in results: left = tile.x - min_x top = tile.y - min_y bounds = (left * self.TILE_SIZE, top * self.TILE_SIZE, (left + 1) * self.TILE_SIZE, (top + 1) * self.TILE_SIZE) out_img.paste(img, bounds) tiles_bounding = list(mercantile.xy_bounds(t) for t in tiles) min_mercator_lng = min(t.left for t in tiles_bounding) max_mercator_lng = max(t.right for t in tiles_bounding) min_mercator_lat = min(t.bottom for t in tiles_bounding) max_mercator_lat = max(t.top for t in tiles_bounding) kx = out_img.size[0] / (max_mercator_lng - min_mercator_lng) ky = out_img.size[1] / (max_mercator_lat - min_mercator_lat) self._image = out_img self._left = min_mercator_lng self._right = max_mercator_lng self._top = max_mercator_lat self._bottom = min_mercator_lat self._kx = kx self._ky = ky
def test_implicitely_transform_to_base_srid(self): DJANGO_MAJOR = VERSION[0] if DJANGO_MAJOR < 3: # superfluous parenthesis for unknown reason expected_transform = "ST_Transform((ST_MAKEENVELOPE(-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244, 3857)), 4326)" else: expected_transform = "ST_Transform(ST_MAKEENVELOPE(-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244, 3857), 4326)" features = Feature.objects.filter(geom__intersects=MakeEnvelope( *mercantile.xy_bounds(0, 0, 0), 3857)) self.assertIn(expected_transform, str(features.query))
def get_tile(self, x, y, z, sort_by=None, desc=False, bands=None): """Generate mercator tile from rasters in FeatureCollection. Parameters ---------- x: int x coordinate of tile y: int y coordinate of tile z: int zoom level sort_by: str attribute in feature to sort by desc: bool True for descending order, False for ascending bands: list list of indices of requested bads, default None which returns all bands Returns ------- GeoRaster2 """ bb = mercantile.xy_bounds(x, y, z) roi = GeoVector.from_bounds(xmin=bb.left, ymin=bb.bottom, xmax=bb.right, ymax=bb.top, crs=WEB_MERCATOR_CRS) filtered_fc = self.filter(roi) def _get_tiled_feature(feature): return feature.get_tiled_feature(x, y, z, bands) with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executer: tiled_features = list( executer.map(_get_tiled_feature, filtered_fc, timeout=CONCURRENCY_TIMEOUT)) # tiled_features can be sort for different merge strategies if sort_by is not None: tiled_features = sorted(tiled_features, reverse=desc, key=lambda f: f[sort_by]) tiles = [f['tile'] for f in tiled_features] if tiles: tile = merge_all(tiles, roi) return tile else: return None
def test_crop_and_get_tile_do_the_same(self): coords = mercantile.xy_bounds(*tiles[15]) shape = GeoVector(Polygon.from_bounds(*coords), WEB_MERCATOR_CRS) raster = self.read_only_virtual_geo_raster() with NamedTemporaryFile(mode='w+b', suffix=".tif") as rf: raster.save(rf.name) raster2 = GeoRaster2.open(rf.name) tile15 = raster2.get_tile(*tiles[15]) # load the image data raster2.image cropped15 = raster2.crop(shape, MERCATOR_RESOLUTION_MAPPING[15]) self.assertEqual(tile15, cropped15)
def test_crop_in_memory_and_off_memory_without_resizing_are_the_same(self): coords = mercantile.xy_bounds(*tiles[18]) shape = GeoVector(Polygon.from_bounds(*coords), WEB_MERCATOR_CRS) raster = self.read_only_virtual_geo_raster() with NamedTemporaryFile(mode='w+b', suffix=".tif") as rf: raster.save(rf.name) raster2 = GeoRaster2.open(rf.name) off_memory_crop = raster2.crop(shape) # load the image data raster2.image in_memory_crop = raster2.crop(shape) self.assertEqual(off_memory_crop, in_memory_crop)
def worker(path): raster = rasterio_open(path) w, s, e, n = transform_bounds(raster.crs, "EPSG:4326", *raster.bounds) tiles = [mercantile.Tile(x=x, y=y, z=z) for x, y, z in mercantile.tiles(w, s, e, n, args.zoom)] tiled = [] for tile in tiles: if cover and tile not in cover: continue w, s, e, n = mercantile.xy_bounds(tile) warp_vrt = WarpedVRT( raster, crs="epsg:3857", resampling=Resampling.bilinear, add_alpha=False, transform=from_bounds(w, s, e, n, width, height), width=width, height=height, ) data = warp_vrt.read(out_shape=(len(raster.indexes), width, height), window=warp_vrt.window(w, s, e, n)) image = np.moveaxis(data, 0, 2) # C,H,W -> H,W,C tile_key = (str(tile.x), str(tile.y), str(tile.z)) if ( not args.label and len(tiles_map[tile_key]) == 1 and is_nodata(image, args.nodata, args.nodata_threshold, args.keep_borders) ): progress.update() continue if len(tiles_map[tile_key]) > 1: out = os.path.join(splits_path, str(tiles_map[tile_key].index(path))) else: out = args.out x, y, z = map(int, tile) if not args.label: tile_image_to_file(out, mercantile.Tile(x=x, y=y, z=z), image) if args.label: tile_label_to_file(out, mercantile.Tile(x=x, y=y, z=z), palette, image) if len(tiles_map[tile_key]) == 1: progress.update() tiled.append(mercantile.Tile(x=x, y=y, z=z)) return tiled
def tile(address, tile_x, tile_y, tile_z, indexes=None, tilesize=256, nodata=None): """ Create mercator tile from any images. Attributes ---------- address : str file url. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. indexes : tuple, int, optional (default: (1, 2, 3)) Bands indexes for the RGB combination. tilesize : int, optional (default: 256) Output image size. nodata: int or float, optional Overwrite nodata value for mask creation. Returns ------- data : numpy ndarray mask: numpy array """ with rasterio.open(address) as src: wgs_bounds = transform_bounds(*[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) indexes = indexes if indexes is not None else src.indexes if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds( 'Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) return utils.tile_read(src, tile_bounds, tilesize, indexes=indexes, nodata=nodata)
def tile( src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT], x: int, y: int, z: int, tilesize: int = 256, **kwargs, ) -> Tuple[numpy.ndarray, numpy.ndarray]: """ Read mercator tile from an image. Attributes ---------- src_dst : rasterio.io.DatasetReader rasterio.io.DatasetReader object x : int Mercator tile X index. y : int Mercator tile Y index. z : int Mercator tile ZOOM level. tilesize : int, optional Output tile size. Default is 256. kwargs : Any, optional Additional options to forward to part() Returns ------- data : numpy ndarray mask: numpy array """ warnings.warn( "'rio_tiler.reader.tile' will be deprecated in rio-tiler 2.0", DeprecationWarning, ) bounds = transform_bounds( src_dst.crs, constants.WGS84_CRS, *src_dst.bounds, densify_pts=21 ) if not tile_exists(bounds, z, x, y): raise TileOutsideBounds(f"Tile {z}/{x}/{y} is outside image bounds") tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z)) return part( src_dst, tile_bounds, tilesize, tilesize, dst_crs=constants.WEB_MERCATOR_CRS, **kwargs, )
def geojson_tile_burn(tile, features, srid, ts, burn_value=1): """Burn tile with GeoJSON features.""" shapes = ((geometry, burn_value) for feature in features for geometry in geojson_reproject(feature, srid, 3857)) bounds = mercantile.xy_bounds(tile) transform = from_bounds(*bounds, ts, ts) try: return rasterize(shapes, out_shape=(ts, ts), transform=transform) except: return None
def upstream_sources_for_tile(tile, catalog, min_zoom=None, max_zoom=None): """Render a tile's source footprints.""" bounds = Bounds(mercantile.xy_bounds(tile), WEB_MERCATOR_CRS) shape = (512, 512) resolution = get_resolution_in_meters(bounds, shape) return catalog.get_sources( bounds, resolution, min_zoom=min_zoom, max_zoom=max_zoom, include_geometries=True, )
def tile(sceneid, tile_x, tile_y, tile_z, rgb=(7, 6, 5), tilesize=256): """Create mercator tile from CBERS data. Attributes ---------- sceneid : str CBERS sceneid. tile_x : int Mercator tile X index. tile_y : int Mercator tile Y index. tile_z : int Mercator tile ZOOM level. rgb : tuple, int, optional (default: ('5', '6', '7')) Bands index for the RGB combination. tilesize : int, optional (default: 256) Output image size. Returns ------- data : numpy ndarray mask: numpy array """ if not isinstance(rgb, tuple): rgb = tuple((rgb, )) scene_params = utils.cbers_parse_scene_id(sceneid) cbers_address = '{}/{}'.format(CBERS_BUCKET, scene_params['key']) with rasterio.open('{}/{}_BAND6.tif'.format(cbers_address, sceneid)) as src: wgs_bounds = transform_bounds( *[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21) if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y): raise TileOutsideBounds('Tile {}/{}/{} is outside image bounds'.format( tile_z, tile_x, tile_y)) mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z) tile_bounds = mercantile.xy_bounds(mercator_tile) addresses = ['{}/{}_BAND{}.tif'.format(cbers_address, sceneid, band) for band in rgb] _tiler = partial(utils.tile_band_worker, bounds=tile_bounds, tilesize=tilesize, nodata=0) with futures.ThreadPoolExecutor(max_workers=3) as executor: data, masks = zip(*list(executor.map(_tiler, addresses))) mask = np.all(masks, axis=0).astype(np.uint8) * 255 return np.concatenate(data), mask
def read_tile(src_path, tile): """Benchmark rio-tiler.utils._tile_read.""" tile_bounds = mercantile.xy_bounds(tile) # We make sure to not store things in cache. with rasterio.Env(GDAL_CACHEMAX=0, NUM_THREADS="all"): with rasterio.open(src_path) as src_dst: return reader.part( src_dst, tile_bounds, 256, 256, resampling_method="nearest", dst_crs=constants.WEB_MERCATOR_CRS, )
def read_tile(self, z, x, y): """Read raster tile data and mask.""" mercator_tile = mercantile.Tile(x=x, y=y, z=z) tile_bounds = mercantile.xy_bounds(mercator_tile) data, mask = tile_read( self.path, tile_bounds, self.tiles_size, indexes=self.indexes, nodata=self.nodata, ) data = (data[0] + data[1]) / 2 return data.astype(numpy.uint8), mask
def test_tile_read_wrong_nodata(): """Return empty mask on wrong nodata.""" # non-boundless tile covering the nodata part mercator_tile = mercantile.Tile(x=438217, y=801835, z=21) bounds = mercantile.xy_bounds(mercator_tile) arr, mask = utils.tile_read(S3_NODATA_PATH, bounds, 256, indexes=(1, 2, 3), nodata=1000) assert arr.shape == (3, 256, 256) assert mask.all() # Mask boundless values mercator_tile = mercantile.Tile(x=109554, y=200458, z=19) bounds = mercantile.xy_bounds(mercator_tile) arr, mask = utils.tile_read(S3_NODATA_PATH, bounds, 256, indexes=(1, 2, 3), nodata=1000) assert arr.shape == (3, 256, 256) assert not mask.all()
def worker(tile): tick = time.monotonic() progress.update() x, y, z = map(str, [tile.x, tile.y, tile.z]) try: os.makedirs(os.path.join(args.out, z, x), exist_ok=True) except: return tile, None, False path = os.path.join(args.out, z, x, "{}.{}".format(y, args.format)) if os.path.isfile(path): # already downloaded return tile, None, True if args.type == "XYZ": url = args.url.format(x=tile.x, y=tile.y, z=tile.z) elif args.type == "TMS": tile.y = (2**tile.z) - tile.y - 1 url = args.url.format(x=tile.x, y=tile.y, z=tile.z) elif args.type == "WMS": xmin, ymin, xmax, ymax = xy_bounds(tile) url = args.url.format(xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax) res = tile_image_from_url(session, url, args.timeout) if res is None: # let's retry once res = tile_image_from_url(session, url, args.timeout) if res is None: return tile, url, False try: tile_image_to_file(args.out, tile, res) # cv2.imwrite(path, cv2.imdecode(np.fromstring(res.read(), np.uint8), cv2.IMREAD_COLOR)) except OSError: return tile, url, False tock = time.monotonic() time_for_req = tock - tick time_per_worker = args.workers / args.rate if time_for_req < time_per_worker: time.sleep(time_per_worker - time_for_req) return tile, url, True
def optimize_group(group, quadkey): """Try to find the minimal number of assets to cover tile This optimization implies _both_ that - assets will be ordered in the MosaicJSON in order of sort of the entire tile - the total number of assets is kept to a minimum Computing the absolute minimum of assets to cover the tile may not in general be possible in finite time, so this is a naive method that should work relatively well for this use case. Returns group also sorted with respect to intersection of entire tile. """ tile = mercantile.quadkey_to_tile(quadkey) tile_geom = box(*mercantile.xy_bounds(tile)) final_assets = [] while True: # Find intersection percent group['int_pct'] = group.geometry.intersection( tile_geom).area / tile_geom.area # Remove features with no tile overlap group = group.loc[group['int_pct'] > 0] if len(group) == 0: # There are many ocean/border tiles on the edges of available maps # that by definition don't have full coverage break # Sort by cover of region of tile that is left group = group.sort_values('int_pct', ascending=False) # Remove top asset and add to final_assets top_asset = group.iloc[0] group = group.iloc[1:] final_assets.append(top_asset) # Recompute tile_geom, removing overlap with top_asset tile_geom = tile_geom.difference(top_asset.geometry) # When total area is covered, stop if tile_geom.area - 1e-4 < 0: break if len(group) == 0: # There are many ocean/border tiles on the edges of available maps # that by definition don't have full coverage break return gpd.GeoDataFrame(final_assets)
def burn(tile, features, size): '''Burn tile with features. Args: tile: the mercantile tile to burn. features: the geojson features to burn. size: the size of burned image. Returns: image: rasterized file of size with features burned. ''' # the value you want in the output raster where a shape exists burnval = 1 shapes = ((geometry, burnval) for feature in features for geometry in feature_to_mercator(feature)) bounds = mercantile.xy_bounds(tile) transform = from_bounds(*bounds, size, size) result = rasterize(shapes, out_shape=(size, size), transform=transform) return Image.fromarray(result, mode='P')