Skip to content

Commit

Permalink
add InMemoryBackend (#163)
Browse files Browse the repository at this point in the history
* update docs

* Update docs/advanced/backends.md

Co-authored-by: Kyle Barron <kylebarron2@gmail.com>

* Update docs/advanced/backends.md

Co-authored-by: Kyle Barron <kylebarron2@gmail.com>

* Update docs/advanced/backends.md

Co-authored-by: Kyle Barron <kylebarron2@gmail.com>

* InMemoryBackend -> MemoryBackend

Co-authored-by: Kyle Barron <kylebarron2@gmail.com>
  • Loading branch information
vincentsarago and kylebarron committed Feb 5, 2021
1 parent bc0ac5d commit 098dcac
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* update mosaicJSON.bounds type definition to match rio-tiler BaseReader definition (https://github.com/developmentseed/cogeo-mosaic/issues/158)
* add default bounds/minzoom/maxzoom values matching the mosaicjson default in the backends (https://github.com/developmentseed/cogeo-mosaic/pull/162)
* raise an error when trying to pass `mosaic_def` in read-only backend (https://github.com/developmentseed/cogeo-mosaic/pull/162)
* add `MemoryBackend` (https://github.com/developmentseed/cogeo-mosaic/pull/163)

## 3.0.0b1 (2020-12-18)

Expand Down
6 changes: 5 additions & 1 deletion cogeo_mosaic/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from cogeo_mosaic.backends.base import BaseBackend
from cogeo_mosaic.backends.dynamodb import DynamoDBBackend
from cogeo_mosaic.backends.file import FileBackend
from cogeo_mosaic.backends.memory import MemoryBackend
from cogeo_mosaic.backends.s3 import S3Backend
from cogeo_mosaic.backends.sqlite import SQLiteBackend
from cogeo_mosaic.backends.stac import STACBackend
Expand All @@ -16,8 +17,11 @@ def MosaicBackend(url: str, *args: Any, **kwargs: Any) -> BaseBackend:
"""Select mosaic backend for url."""
parsed = urlparse(url)

if not url or url == ":memory:":
return MemoryBackend(*args, **kwargs)

# `stac+https//{hostname}/{path}`
if parsed.scheme and parsed.scheme.startswith("stac+"):
elif parsed.scheme and parsed.scheme.startswith("stac+"):
url = url.replace("stac+", "")
return STACBackend(url, *args, **kwargs)

Expand Down
54 changes: 54 additions & 0 deletions cogeo_mosaic/backends/memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""cogeo-mosaic In-Memory backend."""

from typing import Dict, Tuple, Type

import attr
from morecantile import TileMatrixSet
from rio_tiler.constants import WEB_MERCATOR_TMS
from rio_tiler.io import BaseReader, COGReader

from cogeo_mosaic.backends.base import BaseBackend, _convert_to_mosaicjson
from cogeo_mosaic.mosaic import MosaicJSON


@attr.s
class MemoryBackend(BaseBackend):
"""InMemory Backend Adapter
Examples:
>>> with MemoryBackend(mosaicJSON) as mosaic:
mosaic.tile(0, 0, 0)
"""

mosaic_def: MosaicJSON = attr.ib(converter=_convert_to_mosaicjson)
reader: Type[BaseReader] = attr.ib(default=COGReader)
reader_options: Dict = attr.ib(factory=dict)
backend_options: Dict = attr.ib(factory=dict)

# TMS is outside the init because mosaicJSON and cogeo-mosaic only
# works with WebMercator (mercantile) for now.
tms: TileMatrixSet = attr.ib(init=False, default=WEB_MERCATOR_TMS)

# default values for bounds and zoom
bounds: Tuple[float, float, float, float] = attr.ib(
init=False, default=(-180, -90, 180, 90)
)
minzoom: int = attr.ib(init=False, default=0)
maxzoom: int = attr.ib(init=False, default=30)

path: str = attr.ib(init=False, default=":memory:")

_backend_name = "MEM"

def __attrs_post_init__(self):
"""Post Init."""
self.minzoom = self.mosaic_def.minzoom
self.maxzoom = self.mosaic_def.maxzoom
self.bounds = self.mosaic_def.bounds

def write(self, overwrite: bool = True):
"""Write mosaicjson document."""
pass

def _read(self) -> MosaicJSON:
pass
18 changes: 18 additions & 0 deletions docs/advanced/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ Read only backend won't allow `mosaic_def` in there `__init__` method. `.write()
- **HTTP/HTTPS** (`http://`, `https://`)
- **STAC** (`stac+:https://`). Based on [SpatioTemporal Asset Catalog](https://github.com/radiantearth/stac-spec) API.

#### In-Memory

If you have a mosaicjson document and want to use the different backend methods you can use the special `MemoryBackend`.

```python
with MemoryBackend(mosaic_def=mosaicjson) as mosaic:
img = mosaic.tile(1, 1, 1)
```


## MosaicBackend

To ease the usage we added a helper function to use the right backend based on the uri schema: `cogeo_mosaic.backends.MosaicBackend`
Expand Down Expand Up @@ -47,6 +57,14 @@ with MosaicBackend("https://mosaic.com/amosaic.json.gz") as mosaic:

with MosaicBackend("stac+https://my-stac.api/search", {"collections": ["satellite"]}, 10, 12) as mosaic:
assert isinstance(mosaic, cogeo_mosaic.backends.stac.STACBackend)

# In Memory (write)
# You can pass either None or ':memory:' to define an in-memory backend
with MosaicBackend(":memory:", mosaic_def=mosaic) as mosaic:
assert isinstance(mosaic, cogeo_mosaic.backends.memory.MemoryBackend)

with MosaicBackend(None, mosaic_def=mosaic) as mosaic:
assert isinstance(mosaic, cogeo_mosaic.backends.memory.MemoryBackend)
```

## STAC Backend
Expand Down
12 changes: 12 additions & 0 deletions docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,25 @@ with MosaicBackend("s3://mybucket/amosaic.json") as mosaic:
```

#### In Memory

```python
from cogeo_mosaic.mosaic import MosaicJSON
from cogeo_mosaic.backends import MosaicBackend

from cogeo_mosaic.backends.memory import MemoryBackend

mosaic_definition = MosaicJSON.from_urls(["1.tif", "2.tif"])

# If set to None or :memory:, MosaicBackend will use the MemoryBackend
with MosaicBackend(":memory:", mosaic_def=mosaicdata) as mosaic:
assert isinstance(mosaic, MemoryBackend)
img, assets_used = mosaic.tile(1, 2, 3)

with MosaicBackend(None, mosaic_def=mosaicdata) as mosaic:
assert isinstance(mosaic, MemoryBackend)
img, assets_used = mosaic.tile(1, 2, 3)

with MemoryBackend(mosaic_def=mosaicdata) as mosaic:
img, assets_used = mosaic.tile(1, 2, 3)
```

Expand Down
28 changes: 23 additions & 5 deletions tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from cogeo_mosaic.backends import MosaicBackend
from cogeo_mosaic.backends.dynamodb import DynamoDBBackend
from cogeo_mosaic.backends.file import FileBackend
from cogeo_mosaic.backends.memory import MemoryBackend
from cogeo_mosaic.backends.s3 import S3Backend
from cogeo_mosaic.backends.sqlite import SQLiteBackend
from cogeo_mosaic.backends.stac import STACBackend
Expand Down Expand Up @@ -639,16 +640,22 @@ def test_mosaic_crud_error(mosaic_path):
...


def test_BaseReader():
"""Test BaseReader heritance methods."""
def test_InMemoryReader():
"""Test MemoryBackend."""
assets = [asset1, asset2]
mosaicdef = MosaicJSON.from_urls(assets, quiet=False)

# add some offset to the center to make
# sure BaseBackend forward center from the mosaic definition
mosaicdef.center = [x + 1 for x in mosaicdef.center]
with MosaicBackend(":memory:", mosaic_def=mosaicdef) as mosaic:
assert isinstance(mosaic, MemoryBackend)
assert mosaic.path == ":memory:"
mosaic.write()
mosaic._read()

with MosaicBackend(None, mosaic_def=mosaicdef) as mosaic:
assert isinstance(mosaic, MemoryBackend)
assert mosaic.path == ":memory:"

with MemoryBackend(mosaic_def=mosaicdef) as mosaic:
(t, _), assets_used = mosaic.tile(150, 182, 9)
assert t.shape

Expand Down Expand Up @@ -705,6 +712,17 @@ def test_BaseReader():

assert mosaic.spatial_info

mosaic_oneasset = MosaicJSON.from_urls([asset1], quiet=True)
with MemoryBackend(mosaic_def=mosaic_oneasset) as mosaic:
assert isinstance(mosaic, MemoryBackend)
assert len(mosaic.get_assets(150, 182, 9)) == 1
features = get_footprints([asset2], quiet=True)
mosaic.update(features)
assets = mosaic.get_assets(150, 182, 9)
assert len(assets) == 2
assert assets[0] == asset2
assert assets[1] == asset1


def test_sqlite_backend():
"""Test sqlite backend."""
Expand Down

0 comments on commit 098dcac

Please sign in to comment.