mapster_space / app.py
rodolphethinks1's picture
Update app.py
bb7aeac verified
import os
import gradio as gr
import numpy as np
import io
import random
from PIL import Image
from dotenv import load_dotenv
import pystac_client
from datetime import datetime
from src.auth.auth import S3Connector
from src.utils.utils import extract_s3_path_from_url
from src.utils.stac_client import ProductDownloader
# Load environment variables
load_dotenv()
# Get credentials from environment variables
ACCESS_KEY_ID = os.environ.get("ACCESS_KEY_ID")
SECRET_ACCESS_KEY = os.environ.get("SECRET_ACCESS_KEY")
ENDPOINT_URL = 'https://eodata.dataspace.copernicus.eu'
# Initialize the connector
s3_connector = S3Connector(
endpoint_url=ENDPOINT_URL,
access_key_id=ACCESS_KEY_ID,
secret_access_key=SECRET_ACCESS_KEY
)
# Connect to S3
s3_connector.connect()
s3_client = s3_connector.get_s3_client()
ENDPOINT_STAC = "https://stac.dataspace.copernicus.eu/v1/"
catalog = pystac_client.Client.open(ENDPOINT_STAC)
def fetch_sentinel_image(longitude, latitude, date_from, date_to, cloud_cover):
"""Fetch a Sentinel image based on criteria."""
try:
# Use the coordinates from inputs
LON, LAT = float(longitude), float(latitude)
# Use the date range from inputs
date_range = f"{date_from}/{date_to}"
cloud_query = f"eo:cloud_cover<{cloud_cover}"
items_txt = catalog.search(
collections=['sentinel-2-l2a'],
intersects=dict(type="Point", coordinates=[LON, LAT]),
datetime=date_range,
query=[cloud_query]
).item_collection()
if len(items_txt) == 0:
return None, f"No images found for the specified criteria at coordinates ({LON}, {LAT}) with cloud cover < {cloud_cover}%."
# Randomly select an image from the available items
selected_item = random.choice(items_txt)
# Format datetime for readability
datetime_str = selected_item.properties.get('datetime', 'N/A')
try:
dt = datetime.fromisoformat(datetime_str.replace('Z', '+00:00'))
formatted_date = dt.strftime('%Y-%m-%d %H:%M:%S UTC')
except:
formatted_date = datetime_str
# Extract metadata for display
metadata = f"""
## Product Information
- **Location**: {LAT}°N, {LON}°E
- **Date**: {formatted_date}
- **Cloud Cover**: {selected_item.properties.get('eo:cloud_cover', 'N/A')}%
- **Cloud Cover Threshold**: < {cloud_cover}%
- **Satellite**: {selected_item.properties.get('platform', 'N/A')}
- **Product ID**: {selected_item.id}
- **Items Found**: {len(items_txt)} matching products
"""
# Get the TCI_60m asset from the randomly selected item
product_url = extract_s3_path_from_url(selected_item.assets['TCI_60m'].href)
print(f"Selected product URL: {product_url}")
# Initialize the handler with the S3 connector
handler = ProductDownloader(s3_client=s3_client, bucket_name='eodata')
# Get the image content as bytes
product_bytes, filename = handler.get_product_content(product_url)
print(f"Downloaded {filename}, content size: {len(product_bytes)} bytes")
# Convert to PIL Image
img = Image.open(io.BytesIO(product_bytes))
return img, metadata
except ValueError as ve:
error_message = f"Invalid input: {str(ve)}. Please ensure longitude and latitude are valid numbers."
print(error_message)
return None, error_message
except Exception as e:
error_message = f"Error: {str(e)}"
print(error_message)
return None, error_message
# Create Gradio interface
with gr.Blocks(title="Sentinel Product Viewer") as demo:
gr.Markdown("# Sentinel-2 Product Viewer")
gr.Markdown("Browse and view Sentinel-2 satellite product")
with gr.Row():
with gr.Column(scale=1):
# Location inputs
with gr.Row():
longitude = gr.Number(label="Longitude", value=15.0, minimum=-180, maximum=180)
latitude = gr.Number(label="Latitude", value=50.0, minimum=-90, maximum=90)
# Date range inputs
with gr.Row():
date_from = gr.Textbox(label="Date From (YYYY-MM-DD)", value="2024-05-01")
date_to = gr.Textbox(label="Date To (YYYY-MM-DD)", value="2025-02-01")
# Cloud cover slider
cloud_cover = gr.Slider(
label="Max Cloud Cover (%)",
minimum=0,
maximum=100,
value=50,
step=5
)
# Diverse landscape location buttons
gr.Markdown("### Diverse Locations")
with gr.Row():
italy_btn = gr.Button("Italy")
amazon_btn = gr.Button("Amazon Rainforest")
with gr.Row():
tokyo_btn = gr.Button("Tokyo")
great_barrier_btn = gr.Button("Great Barrier Reef")
with gr.Row():
iceland_btn = gr.Button("Iceland Glacier")
canada_btn = gr.Button("Baffin Island")
fetch_btn = gr.Button("Fetch Random Image", variant="primary")
with gr.Column(scale=2):
image_output = gr.Image(type="pil", label="Sentinel-2 Image")
metadata_output = gr.Markdown(label="Image Metadata")
# Button click handlers for diverse landscapes
italy_btn.click(lambda: (12.39, 42.05), outputs=[longitude, latitude])
amazon_btn.click(lambda: (-64.7, -3.42), outputs=[longitude, latitude])
tokyo_btn.click(lambda: (139.70, 35.65), outputs=[longitude, latitude])
great_barrier_btn.click(lambda: (150.97, -20.92), outputs=[longitude, latitude])
iceland_btn.click(lambda: (-18.17, 64.61), outputs=[longitude, latitude])
# rice_terraces_btn.click(lambda: (121.1, 16.9), outputs=[longitude, latitude])
canada_btn.click(lambda: (-71.56, 67.03), outputs=[longitude, latitude])
# Main search button
fetch_btn.click(
fn=fetch_sentinel_image,
inputs=[longitude, latitude, date_from, date_to, cloud_cover],
outputs=[image_output, metadata_output]
)
gr.Markdown("## About")
gr.Markdown("""
This application allows you to browse and view Sentinel-2 satellite imagery using the Copernicus Data Space Ecosystem.
- **Location**: Enter longitude and latitude coordinates or select distinctive landscapes
- **TCI Images**: The images shown are true color (RGB) composites at 60m resolution
- **Date Range**: Specify the date range to search for images
- **Cloud Cover**: Adjust the maximum acceptable cloud cover percentage
- **Random Selection**: A random image that matches the criteria will be selected for display
""")
demo.launch()