Skip to content

Commit

Permalink
Add planet mosaic endpoint, close #41
Browse files Browse the repository at this point in the history
  • Loading branch information
drewbo committed Aug 5, 2020
1 parent 44497c6 commit dddc8d9
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 0 deletions.
2 changes: 2 additions & 0 deletions covid_api/api/api_v1/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
groups,
tiles,
metadata,
planet,
)

api_router = APIRouter()
Expand All @@ -22,3 +23,4 @@
api_router.include_router(sites.router, tags=["sites"])
api_router.include_router(groups.router, tags=["indicator groups"])
api_router.include_router(detections.router, tags=["detections"])
api_router.include_router(planet.router, tags=["planet"])
85 changes: 85 additions & 0 deletions covid_api/api/api_v1/endpoints/planet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""API planet mosaic tiles."""

from typing import Any, Dict, Union, Optional

import re

from functools import partial

import numpy as np
import requests

from fastapi import APIRouter, Depends, Query, Path
from starlette.concurrency import run_in_threadpool

from rio_tiler.profiles import img_profiles
from rio_tiler.utils import render

from covid_api.api import utils
from covid_api.db.memcache import CacheLayer
from covid_api.ressources.enums import ImageType
from covid_api.ressources.common import drivers, mimetype
from covid_api.ressources.responses import TileResponse


_render = partial(run_in_threadpool, render)
_tile = partial(run_in_threadpool, utils.planet_mosaic_tile)

router = APIRouter()
responses = {
200: {
"content": {
"image/png": {},
"image/jpg": {},
"image/webp": {},
"image/tiff": {},
"application/x-binary": {},
},
"description": "Return an image.",
}
}
tile_routes_params: Dict[str, Any] = dict(
responses=responses, tags=["planet"], response_class=TileResponse
)


@router.get(r"/planet/{z}/{x}/{y}", **tile_routes_params)
async def tile(
z: int = Path(..., ge=0, le=30, description="Mercator tiles's zoom level"),
x: int = Path(..., description="Mercator tiles's column"),
y: int = Path(..., description="Mercator tiles's row"),
scenes: str = Query(..., description="Comma separated Planets scenes to mosaic."),
cache_client: CacheLayer = Depends(utils.get_cache),
) -> TileResponse:
"""Handle /planet requests."""
timings = []
headers: Dict[str, str] = {}

tile_hash = utils.get_hash(**dict(z=z, x=x, y=y, scenes=scenes,))

content = None
if cache_client:
try:
content, ext = cache_client.get_image_from_cache(tile_hash)
headers["X-Cache"] = "HIT"
except Exception:
content = None

if not content:
with utils.Timer() as t:
tile, mask = await _tile(scenes, x, y, z)
timings.append(("Read", t.elapsed))

content = await _render(tile, mask)

timings.append(("Format", t.elapsed))

if cache_client and content:
cache_client.set_image_cache(tile_hash, (content))

if timings:
headers["X-Server-Timings"] = "; ".join(
["{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings]
)

return TileResponse(content, media_type=mimetype["png"], headers=headers)
35 changes: 35 additions & 0 deletions covid_api/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
import time
import json
import hashlib
import math
import random
from io import BytesIO


import numpy as np
from shapely.geometry import shape, box
from rasterstats.io import bounds_window
from rasterio.io import MemoryFile
import requests

from starlette.requests import Request

Expand All @@ -28,6 +34,7 @@

from covid_api.db.memcache import CacheLayer
from covid_api.models.timelapse import Feature
from covid_api.core.config import PLANET_API_KEY


def get_cache(request: Request) -> CacheLayer:
Expand Down Expand Up @@ -679,3 +686,31 @@ def get_custom_cmap(cname) -> Dict:


ColorMapName = Enum("ColorMapNames", [(a, a) for a in COLOR_MAP_NAMES]) # type: ignore


def planet_mosaic_tile(scenes, x, y, z):
mosaic_tile = np.zeros((4, 256, 256), dtype=np.uint8)
for scene in scenes.split(","):
api_num = math.floor(random.random() * 3) + 1
url = f"https://tiles{api_num}.planet.com/data/v1/PSScene3Band/{scene}/{z}/{x}/{y}.png?api_key={PLANET_API_KEY}"
r = requests.get(url)
with MemoryFile(BytesIO(r.content)) as memfile:
with memfile.open() as src:
data = src.read()
print(data.sum())
# any place we don't have data yet, add some
mosaic_tile = np.where(
mosaic_tile[3] == 0, mosaic_tile + data, mosaic_tile
)

# if the tile is full, stop
if np.count_nonzero(mosaic_tile[3]) == mosaic_tile[3].size:
break

# salt the resulting image
salt = np.random.randint(0, 3, (256, 256), dtype=np.uint8)
mosaic_tile[:3] = np.where(
mosaic_tile[:3] < 254, mosaic_tile[:3] + salt, mosaic_tile[:3]
)

return mosaic_tile[:3], mosaic_tile[3]
2 changes: 2 additions & 0 deletions covid_api/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@

INDICATOR_BUCKET = os.environ.get("INDICATOR_BUCKET", "covid-eo-data")
DT_FORMAT = "%Y-%m-%d"

PLANET_API_KEY = os.getenv("PLANET_API_KEY")

0 comments on commit dddc8d9

Please sign in to comment.