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()