Spaces:
Sleeping
Sleeping
File size: 7,010 Bytes
1fc9fc6 bb7aeac | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | 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()
|