# ======================================================================= # ШАГ 1: Импорт библиотек # ======================================================================= import os import json import ee import geemap import gradio as gr import html # ======================================================================= # ШАГ 2: Аутентификация и инициализация GEE (АДАПТИРОВАНО ДЛЯ HF SPACES) # ======================================================================= print("\n--- Инициализация Google Earth Engine ---") try: # Проверяем, запущено ли приложение в Hugging Face Spaces и есть ли секрет if 'GEE_CREDENTIALS' in os.environ: print("Аутентификация через секрет Hugging Face...") # Декодируем JSON-строку из секрета creds_json_str = os.environ['GEE_CREDENTIALS'] creds_json = json.loads(creds_json_str) # Создаем учетные данные и инициализируем GEE credentials = ee.ServiceAccountCredentials(creds_json['client_email'], key_data=creds_json_str) ee.Initialize(credentials=credentials, project=creds_json['project_id']) print("✅ Аутентификация через сервисный аккаунт GEE прошла успешно.") else: # Резервный вариант для локального запуска (если секрета нет) print("Секрет не найден, попытка локальной аутентификации...") ee.Authenticate() ee.Initialize(project='gen-lang-client-0605302377') print("✅ Локальная аутентификация и инициализация GEE прошли успешно.") except Exception as e: print(f"🔴 Критическая ошибка на этапе инициализации GEE: {e}") # В среде HF это приведет к ошибке приложения, что и требуется. # ======================================================================= # ШАГ 3: Обучение модели-классификатора # (Код остается без изменений) # ======================================================================= gee_classifier = None bands_for_training = ['B2', 'B3', 'B4', 'B8', 'NDVI'] def add_ndvi(image): ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI') return image.addBands(ndvi) def train_classifier(): global gee_classifier if gee_classifier: print("✅ Классификатор уже обучен.") return print("⏳ Обучение классификатора GEE... Это может занять около минуты.") try: desert = ee.FeatureCollection('projects/gen-lang-client-0605302377/assets/kalmykia_desert_samples').map(lambda f: f.set('class', 0)) solonchak = ee.FeatureCollection('projects/gen-lang-client-0605302377/assets/kalmykia_solonchak_samples').map(lambda f: f.set('class', 1)) arid = ee.FeatureCollection('projects/gen-lang-client-0605302377/assets/kalmykia_arid_samples').map(lambda f: f.set('class', 2)) greenery = ee.FeatureCollection('projects/gen-lang-client-0605302377/assets/kalmykia_greenery_samples').map(lambda f: f.set('class', 3)) water = ee.FeatureCollection('projects/gen-lang-client-0605302377/assets/kalmykia_water_samples').map(lambda f: f.set('class', 4)) training_points = desert.merge(solonchak).merge(arid).merge(greenery).merge(water) roi_for_training = training_points.geometry().bounds() image_for_training = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED') \ .filterDate('2023-06-01', '2023-09-01') \ .filterBounds(roi_for_training) \ .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10)) \ .map(add_ndvi) \ .median() \ .clip(roi_for_training) training_data = image_for_training.select(bands_for_training).sampleRegions( collection=training_points, properties=['class'], scale=10, tileScale=4) gee_classifier = ee.Classifier.smileRandomForest(numberOfTrees=50).train( features=training_data, classProperty='class', inputProperties=bands_for_training) print("✅ Модель-классификатор успешно обучена на 5 классах!") except Exception as e: print(f"🔴 Критическая ошибка во время обучения модели: {e}") train_classifier() # ======================================================================= # ШАГ 4: Функции для работы Gradio-приложения # (Код остается без изменений) # ======================================================================= def get_region_info(region_name): regions = { "Элиста (тестовая область)": {"center": [46.307, 44.258], "bbox": [44.15, 46.25, 44.35, 46.35]}, "Озеро Сарпа (Калмыкия)": {"center": [47.85, 44.75], "bbox": [44.65, 47.80, 44.85, 47.90]}, "Арзгир (Ставроп. край)": {"center": [45.38, 44.22], "bbox": [44.12, 45.33, 44.32, 45.43]}, "Будённовск (Ставроп. край)": {"center": [44.78, 44.15], "bbox": [44.05, 44.73, 44.25, 44.83]}, "Москва (тестовая область)": {"center": [55.75, 37.61], "bbox": [37.5, 55.7, 37.7, 55.8]} } return regions.get(region_name) def generate_classified_map(region_info, year, classifier): if not classifier: return None, "Ошибка: Классификатор не обучен." print(f"-- Запрос на генерацию карты для {year} года...") roi_to_classify = ee.Geometry.Rectangle(region_info['bbox']) collection = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED') \ .filterDate(f'{year}-06-01', f'{year}-09-01') \ .filterBounds(roi_to_classify) \ .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 25)) if collection.size().getInfo() == 0: return None, f"⚠️ Не найдено снимков за {year} год." image_to_classify = collection.map(add_ndvi).median().clip(roi_to_classify) classified_image = image_to_classify.classify(classifier) class_palette = ['#e3a25a', '#ffffff', '#ffff00', '#00ff00', '#0000FF'] vis_params = {'min': 0, 'max': 4, 'palette': class_palette} map_id = classified_image.getMapId(vis_params) print(f"-- ✅ Карта для {year} года сгенерирована.") return {"center": region_info['center'], "tile_url": map_id['tile_fetcher'].url_format, "year": year}, None def create_map_iframe_html(map_data, region_name): center, tile_url, year = map_data['center'], map_data['tile_url'], map_data['year'] iframe_content = f"""
{msg}
") if msg: messages.append(msg) while len(outputs) < 3: outputs.append(None) final_message = "✅ Готово. " + " ".join(messages) return outputs[0], outputs[1], outputs[2], final_message # ======================================================================= # ШАГ 5: Создание и запуск интерфейса Gradio # (Код остается почти без изменений) # ======================================================================= with gr.Blocks(css=".gradio-container {max-width: 1200px !important;}") as demo: gr.Markdown("# 🛰️ Анализ почвенного покрова") gr.Markdown("Выберите регион и три года для анализа. Карты появятся ниже.") with gr.Row(): with gr.Column(scale=1): region_dropdown = gr.Dropdown( label="Регион", choices=["Элиста (тестовая область)", "Озеро Сарпа (Калмыкия)", "Арзгир (Ставроп. край)", "Будённовск (Ставроп. край)", "Москва (тестовая область)"], value="Элиста (тестовая область)") year1_slider = gr.Slider(label="Год 1", minimum=2019, maximum=2025, step=1, value=2019) year2_slider = gr.Slider(label="Год 2", minimum=2019, maximum=2025, step=1, value=2021) year3_slider = gr.Slider(label="Год 3", minimum=2019, maximum=2025, step=1, value=2023) submit_button = gr.Button("Сгенерировать карты", variant="primary") status_message = gr.Markdown() with gr.Row(): map1_output = gr.HTML() map2_output = gr.HTML() map3_output = gr.HTML() submit_button.click( fn=process_and_display_maps, inputs=[region_dropdown, year1_slider, year2_slider, year3_slider], outputs=[map1_output, map2_output, map3_output, status_message]) # --- ЗАПУСК ПРИЛОЖЕНИЯ --- print("\n--- Запуск Gradio интерфейса ---") demo.launch()