Customization
Custom mosaic creation¶
MosaicJSON._create_mosaic()
method is the low level method that creates mosaicjson document. It has multiple required arguments and options with default values which more advanced users would change.
# cogeo_mosaic.mosaic.MosaicJSON._create_mosaic
def _create_mosaic(
cls,
features: Sequence[Dict],
minzoom: int,
maxzoom: int,
quadkey_zoom: Optional[int] = None,
accessor: Callable[[Dict], str] = default_accessor,
asset_filter: Callable = default_filter,
version: str = "0.0.3",
tilematrixset: Optional[morecantile.TileMatrixSet] = None,
asset_type: Optional[str] = None,
asset_prefix: Optional[str] = None,
data_type: Optional[str] = None,
colormap: Optional[Dict[int, Tuple[int, int, int, int]]] = None,
layers: Optional[Dict] = None,
quiet: bool = True,
**kwargs,
):
Custom Accessor¶
MosaicJSON create
method takes a list of GeoJSON features has input, those can be the output of cogeo_mosaic.utils.get_footprints or can be provided by the user (e.g STAC items). MosaicJSON defines it's tile assets as a MUST be arrays of strings (url or sceneid) pointing to a COG
. To access those values, _create_mosaic
needs to know which property to read from the GeoJSON feature.
The accessor option is here to enable user to pass their own accessor model. By default, _create_mosaic
expect features from get_footprints
and thus COG path stored in feature["properties"]["path"]
.
Example:
from cogeo_mosaic.mosaic import MosaicJSON
features = [{"url": "1.tif", "geometry": {...}}, {"url": "2.tif", "geometry": {...}}]
minzoom = 1
maxzoom = 6
custom_id = lambda feature: feature["url"]
# 'from_features' will pass all args and kwargs to '_create_mosaic'
mosaicjson = MosaicJSON.from_features(
features,
minzoom,
maxzoom,
accessor=custom_id,
)
Custom asset filtering¶
On mosaicjson creation ones would want to perform more advanced assets filtering or sorting. To enable this, users can define their own filter
method and pass it using the asset_filter
options.
!!! In the current implementation, asset_filter
method have to allow at least 3 arguments:
- tile - morecantile.Tile: Morecantile tile
- dataset - Sequence[Dict]: GeoJSON Feature list intersecting with the tile
- geoms - Sequence[polygons]: Geos Polygon list for the features
Example:
import datetime
from cogeo_mosaic.mosaic import MosaicJSON, default_filter
features = [{"url": "20190101.tif", "geometry": {...}}, {"url": "20190102.tif", "geometry": {...}}]
minzoom = 1
maxzoom = 6
def custom_filter(**args, **kwargs):
"""Default filter + sort."""
dataset = default_filter(**args, **kwargs)
return sorted(
dataset,
key=lambda x: datetime.datetime.strptime(x["url"].split(".")[0], "%Y%m%d")
)
mosaicjson = MosaicJSON.from_features(
features,
minzoom,
maxzoom,
asset_filter=custom_filter,
)
Custom mosaic update¶
Update method is backend specific because you don't write a mosaicjson document in the same way in AWS S3 and in AWS DynamoDB.
The main method is defined in cogeo_mosaic.backends.base.BaseBackend.
On update, here is what is happening:
1. create mosaic with the new dataset
2. loop through the new quadkeys
and edit old
mosaic assets
3. update bounds, center and version of the updated mosaic
4. write the mosaic
# cogeo_mosaic.backends.base.BaseBackend
def update(
self,
features: Sequence[Dict],
add_first: bool = True,
quiet: bool = False,
**kwargs,
):
"""Update existing MosaicJSON on backend."""
# Create mosaic with the new features
new_mosaic = self.mosaic_def.from_features(
features,
self.mosaic_def.minzoom,
self.mosaic_def.maxzoom,
tilematrixset=self.mosaic_def.tilematrixset,
quadkey_zoom=self.quadkey_zoom,
quiet=quiet,
**kwargs,
)
# Loop through the new `quadkeys` and edit `old` mosaic assets
for quadkey, new_assets in new_mosaic.tiles.items():
tile = self.tms.quadkey_to_tile(quadkey)
assets = self.tile(*tile)
assets = [*new_assets, *assets] if add_first else [*assets, *new_assets]
# [PLACEHOLDER] add custom sorting algorithm (e.g based on path name)
self.mosaic_def.tiles[quadkey] = assets
# Update bounds, center and version of the updated mosaic
bounds = bbox_union(new_mosaic.bounds, self.mosaic_def.bounds)
self.mosaic_def._increase_version() # Increate mosaicjson document version
self.mosaic_def.bounds = bounds
self.mosaic_def.center = (
(bounds[0] + bounds[2]) / 2,
(bounds[1] + bounds[3]) / 2,
self.mosaic_def.minzoom,
)
# Write the mosaic
if self.input:
self.write()
return
Sometime you'll will want to do more advanced filtering/sorting with the newly dataset stack (e.g keep a max number of COG). For this you'll need to create custom backend:
from cogeo_mosaic.backends.s3 import S3Backend
import morecantile
class CustomS3Backend(S3Backend):
_backend_name = "Custom AWS S3"
def update(
self,
features: Sequence[Dict],
quiet: bool = False,
max_image: int = 5,
**kwargs,
):
"""Update existing MosaicJSON on backend."""
new_mosaic = self.mosaic_def.from_features(
features,
self.mosaic_def.minzoom,
self.mosaic_def.maxzoom,
quadkey_zoom=self.quadkey_zoom,
quiet=quiet,
**kwargs,
)
mosaic_tms = self.mosaic_def.tilematrixset or morecantile.tms.get("WebMercatorQuad")
for quadkey, new_assets in new_mosaic.tiles.items():
tile = mosaic_tms.quadkey_to_tile(quadkey)
assets = self.tile(*tile)
assets = [*new_assets, *assets]
self.mosaic_def.tiles[quadkey] = assets[:maximum_items_per_tile]
bounds = bbox_union(new_mosaic.bounds, self.mosaic_def.bounds)
self.mosaic_def._increase_version() # Increate mosaicjson document version
self.mosaic_def.bounds = bounds
self.mosaic_def.center = (
(bounds[0] + bounds[2]) / 2,
(bounds[1] + bounds[3]) / 2,
self.mosaic_def.minzoom,
)
self.write()
return