banesullivan / localtileserver

🌐 dynamic tile server for visualizing rasters in Jupyter with ipyleaflet or folium
https://localtileserver.banesullivan.com
MIT License
284 stars 26 forks source link

Passing rasterio.Env context to tile server #182

Open giswqs opened 7 months ago

giswqs commented 7 months ago

Can localtileserver visualize an image in a requester-pay S3 bucket, like the NAIP imagery on AWS? Rasterio can read the image, but it seems localtileserver can't render it.

import rasterio
from rasterio.session import AWSSession
from ipyleaflet import Map
from localtileserver import TileClient, get_leaflet_tile_layer

aws_session = AWSSession(
    aws_access_key_id='***',
    aws_secret_access_key='***',
    requester_pays=True
)

s3_path = 's3://naip-analytic/tn/2021/60cm/rgbir_cog/34084/m_3408401_ne_16_060_20210404.tif'
# s3_path = 's3://maxar-opendata/events/Kahramanmaras-turkey-earthquake-23/ard/36/120022103023/2023-02-21/104001008314FC00-ms.tif'
with rasterio.Env(session=aws_session):

    src = rasterio.open(s3_path)
    print(src.meta)

    client = TileClient(src)
    t = get_leaflet_tile_layer(client, band=[1, 2, 3])
    m = Map(center=client.center(), zoom=client.default_zoom)
    m.add_layer(t)

m
banesullivan commented 7 months ago

The with rasterio.Env(session=aws_session): context likely won't work (at this time) since localtileserver launches a new thread for the tile server (not passing the context).

If you set these AWS env params in the environment of the host, it should work without issue.

I'm still trying to massively refactor localtileserver and will try to account for this usage scenario better

banesullivan commented 6 months ago

This is definitely doable, but I'm trying to come up with the best/safest approach here.

One thing I just realized is the os.environ seems to be shared across the threads:

import rasterio
from rasterio.session import AWSSession
from fastapi import FastAPI
from server_thread import ServerThread
import requests
import os
import json

app = FastAPI()
@app.get("/")
def root():
    # this is where we would do all our tileserving magic and need access to the Env
    return json.loads(os.environ["LOCALTILESERVER_ENV"])

aws_session = AWSSession(
    aws_unsigned=True
)

with rasterio.Env(session=aws_session):
    os.environ["LOCALTILESERVER_ENV"] = json.dumps(rasterio.env.getenv())
    server = ServerThread(app)
    response = requests.get(f"http://{server.host}:{server.port}/")

respons.json()

But this doesn't feel right to me... and I'm pretty sure would conflict if multiple Envs are set in one session.

giswqs commented 5 months ago

Just came accross the rio-tiler-pds package. Seems relevant to the issue here.

vincentsarago commented 5 months ago

https://github.com/developmentseed/titiler/issues/186 might be of interest

giswqs commented 5 months ago

@banesullivan @vincentsarago Thank you both for the pointers. I was able to make it work by setting the environment variables.

import os
from ipyleaflet import Map
from localtileserver import TileClient, get_leaflet_tile_layer

os.environ["AWS_ACCESS_KEY_ID"] = "YOUR-KEY"
os.environ["AWS_SECRET_ACCESS_KEY"] = "YOUR-TOKEN"
os.environ["AWS_REQUEST_PAYER"] = "requester"

src = 's3://naip-analytic/tn/2021/60cm/rgbir_cog/34084/m_3408401_ne_16_060_20210404.tif'
client = TileClient(src)
t = get_leaflet_tile_layer(client, indexes=[1, 2, 3])
m = Map(center=client.center(), zoom=client.default_zoom)
m.add_layer(t)
m

image