File size: 5,418 Bytes
4c92eeb
9b5b26a
 
 
c19d193
4c92eeb
6aae614
9b5b26a
 
4c92eeb
 
 
9b5b26a
 
4c92eeb
 
9b5b26a
4c92eeb
 
 
 
 
 
9b5b26a
4c92eeb
 
 
 
 
 
 
 
 
 
 
 
 
 
9b5b26a
4c92eeb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b5b26a
4c92eeb
8c01ffb
 
ae7a494
4c92eeb
ae7a494
e121372
4c92eeb
 
 
 
13d500a
8c01ffb
9b5b26a
8c01ffb
4c92eeb
861422e
4c92eeb
8c01ffb
8fe992b
4c92eeb
8c01ffb
 
 
 
 
 
861422e
8fe992b
 
4c92eeb
 
7bf3ee6
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
from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
import os
from tools.final_answer import FinalAnswerTool
from Gradio_UI import GradioUI

# Fetch USGS API credentials from environment variables (set in Hugging Face Spaces secrets)
USGS_USERNAME = os.getenv("USGS_USERNAME")
USGS_TOKEN = os.getenv("USGS_TOKEN")

@tool
def fetch_landsat_image(lat_min: float, lon_min: float, lat_max: float, lon_max: float, start_date: str, end_date: str) -> str:
    """Fetches download URLs for all available Landsat images from USGS M2M API and returns them as clickable HTML links.
    Args:
        lat_min: Minimum latitude of the bounding box
        lon_min: Minimum longitude of the bounding box
        lat_max: Maximum latitude of the bounding box
        lon_max: Maximum longitude of the bounding box
        start_date: Start date in 'YYYY-MM-DD' format
        end_date: End date in 'YYYY-MM-DD' format
    """
    if not USGS_USERNAME or not USGS_TOKEN:
        return "<p>Error: USGS_USERNAME or USGS_TOKEN not set in environment variables.</p>"

    service_url = "https://m2m.cr.usgs.gov/api/api/json/stable/"

    def send_request(url, data, api_key=None):
        json_data = json.dumps(data)
        headers = {'X-Auth-Token': api_key} if api_key else {}
        response = requests.post(url, json_data, headers=headers)
        output = response.json()
        if output.get('errorCode'):
            return f"Error: {output['errorCode']} - {output['errorMessage']}"
        return output['data']

    try:
        # Step 1: Authenticate
        login_payload = {'username': USGS_USERNAME, 'token': USGS_TOKEN}
        api_key = send_request(service_url + "login-token", login_payload)
        if not api_key or isinstance(api_key, str) and "Error" in api_key:
            return "<p>Authentication failed. Check username and token in secrets.</p>"

        # Step 2: Search scenes
        spatial_filter = {
            'filterType': "mbr",
            'lowerLeft': {'latitude': lat_min, 'longitude': lon_min},
            'upperRight': {'latitude': lat_max, 'longitude': lon_max}
        }
        temporal_filter = {'start': start_date, 'end': end_date}
        search_payload = {
            'datasetName': 'landsat_ot_c2_l1',
            'spatialFilter': spatial_filter,
            'temporalFilter': temporal_filter,
            'maxResults': 50,
            'sortField': 'cloudCover',
            'sortDirection': 'ASC'
        }
        scenes = send_request(service_url + "scene-search", search_payload, api_key)
        if isinstance(scenes, str) and "Error" in scenes:
            return f"<p>{scenes}</p>"
        if not scenes['results']:
            return "<p>No Landsat scenes found for the given area and time range.</p>"

        # Step 3: Get download options
        entity_ids = [scene['entityId'] for scene in scenes['results']]
        download_options_payload = {
            'datasetName': 'landsat_ot_c2_l1',
            'entityIds': entity_ids
        }
        download_options = send_request(service_url + "download-options", download_options_payload, api_key)
        if isinstance(download_options, str) and "Error" in download_options:
            return f"<p>{download_options}</p>"

        # Step 4: Request downloads
        downloads = [opt for opt in download_options if opt['available']]
        if not downloads:
            return "<p>No available downloads for these scenes.</p>"

        label = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        download_request_payload = {
            'downloads': [{'entityId': opt['entityId'], 'productId': opt['id']} for opt in downloads],
            'label': label
        }
        request_results = send_request(service_url + "download-request", download_request_payload, api_key)
        if isinstance(request_results, str) and "Error" in request_results:
            return f"<p>{request_results}</p>"

        # Step 5: Generate HTML with clickable links
        urls = [download['url'] for download in request_results['availableDownloads']]
        if not urls:
            return "<p>No download URLs immediately available. Try again later.</p>"

        html_output = f"<p>Found {len(urls)} available Landsat scenes:</p><ul>"
        for i, (scene, url) in enumerate(zip(scenes['results'][:len(urls)], urls)):
            html_output += f'<li>Cloud Cover: {scene["cloudCover"]}% - <a href="{url}" target="_blank">Download Scene {i+1}</a></li>'
        html_output += "</ul>"
        return html_output

    except Exception as e:
        return f"<p>Error fetching Landsat images: {str(e)}</p>"



final_answer = FinalAnswerTool()

model = HfApiModel(
    max_tokens=2096,
    temperature=0.5,
    model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
    custom_role_conversions=None,
)

image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)

with open("prompts.yaml", "r") as stream:
    prompt_templates = yaml.safe_load(stream)

agent = CodeAgent(
    model=model,
    tools=[fetch_landsat_image, final_answer],
    max_steps=6,
    verbosity_level=1,
    grammar=None,
    planning_interval=None,
    name=None,
    description=None,
    prompt_templates=prompt_templates
)

# Launch Gradio UI
interface = GradioUI(agent)
interface.launch(share=False)  # 'share=True' for public link in Spaces