ShrishtiAI-backend / server /controllers /satellite_controller.py
MEWTROS
My 6 can be your 9
5ccd893
"""
Satellite Controller
Handles satellite data operations and GEE service coordination
"""
import logging
from typing import Dict, Any, List
from datetime import datetime, timedelta
from services.gee_service import GEEService
class SatelliteController:
"""Controller for satellite data operations"""
def __init__(self, gee_service: GEEService):
self.gee_service = gee_service
self.logger = logging.getLogger(__name__)
def get_point_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""
Get satellite data for a specific point
Args:
data: Request data containing coordinates and parameters
Returns:
Satellite data response
"""
try:
# Validate required parameters
latitude = data.get('latitude')
longitude = data.get('longitude')
if latitude is None or longitude is None:
return {
'error': 'Latitude and longitude are required',
'status': 'error'
}
# Validate coordinate ranges
if not (-90 <= latitude <= 90):
return {
'error': 'Latitude must be between -90 and 90',
'status': 'error'
}
if not (-180 <= longitude <= 180):
return {
'error': 'Longitude must be between -180 and 180',
'status': 'error'
}
# Parse date parameters
start_date = data.get('start_date')
end_date = data.get('end_date')
if not start_date or not end_date:
# Default to last 30 days
end_date = datetime.now()
start_date = end_date - timedelta(days=30)
start_date = start_date.strftime('%Y-%m-%d')
end_date = end_date.strftime('%Y-%m-%d')
# Parse optional parameters
collection = data.get('collection', 'COPERNICUS/S2_SR')
cloud_filter = data.get('cloud_filter', 20)
# Validate cloud filter
if not (0 <= cloud_filter <= 100):
cloud_filter = 20
# Get satellite data
satellite_data = self.gee_service.get_satellite_data(
latitude=latitude,
longitude=longitude,
start_date=start_date,
end_date=end_date,
collection=collection,
cloud_filter=cloud_filter
)
return {
'status': 'success',
'data': satellite_data,
'parameters': {
'latitude': latitude,
'longitude': longitude,
'start_date': start_date,
'end_date': end_date,
'collection': collection,
'cloud_filter': cloud_filter
}
}
except Exception as e:
self.logger.error(f"Point data retrieval error: {str(e)}")
return {
'error': f'Failed to retrieve satellite data: {str(e)}',
'status': 'error'
}
def get_region_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""
Get satellite data for a region
Args:
data: Request data containing region bounds and parameters
Returns:
Region satellite data response
"""
try:
# Validate bounds
bounds = data.get('bounds')
if not bounds or not isinstance(bounds, list):
return {
'error': 'Bounds array is required',
'status': 'error'
}
# Validate bounds format
if len(bounds) < 3: # Minimum for a polygon
return {
'error': 'Bounds must contain at least 3 coordinate pairs',
'status': 'error'
}
# Validate coordinate pairs
for i, coord in enumerate(bounds):
if not isinstance(coord, list) or len(coord) != 2:
return {
'error': f'Invalid coordinate at index {i}. Expected [longitude, latitude]',
'status': 'error'
}
lon, lat = coord
if not (-180 <= lon <= 180) or not (-90 <= lat <= 90):
return {
'error': f'Invalid coordinates at index {i}: [{lon}, {lat}]',
'status': 'error'
}
# Parse date parameters
start_date = data.get('start_date')
end_date = data.get('end_date')
if not start_date or not end_date:
# Default to last 30 days
end_date = datetime.now()
start_date = end_date - timedelta(days=30)
start_date = start_date.strftime('%Y-%m-%d')
end_date = end_date.strftime('%Y-%m-%d')
# Parse optional parameters
scale = data.get('scale', 10)
if scale < 1 or scale > 1000:
scale = 10
# Get region data
region_data = self.gee_service.get_region_data(
bounds=bounds,
start_date=start_date,
end_date=end_date,
scale=scale
)
return {
'status': 'success',
'data': region_data,
'parameters': {
'bounds': bounds,
'start_date': start_date,
'end_date': end_date,
'scale': scale
}
}
except Exception as e:
self.logger.error(f"Region data retrieval error: {str(e)}")
return {
'error': f'Failed to retrieve region data: {str(e)}',
'status': 'error'
}
def check_availability(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""
Check data availability for a location
Args:
data: Request data containing location and parameters
Returns:
Availability information
"""
try:
# Validate coordinates
latitude = data.get('latitude')
longitude = data.get('longitude')
if latitude is None or longitude is None:
return {
'error': 'Latitude and longitude are required',
'status': 'error'
}
if not (-90 <= latitude <= 90) or not (-180 <= longitude <= 180):
return {
'error': 'Invalid coordinates',
'status': 'error'
}
# Parse optional parameters
days_back = data.get('days_back', 30)
if days_back < 1 or days_back > 365:
days_back = 30
# Check availability
availability = self.gee_service.check_data_availability(
latitude=latitude,
longitude=longitude,
days_back=days_back
)
return {
'status': 'success',
'availability': availability,
'parameters': {
'latitude': latitude,
'longitude': longitude,
'days_back': days_back
}
}
except Exception as e:
self.logger.error(f"Availability check error: {str(e)}")
return {
'error': f'Failed to check availability: {str(e)}',
'status': 'error'
}
def get_service_status(self) -> Dict[str, Any]:
"""
Get GEE service status
Returns:
Service status information
"""
try:
return {
'status': 'success',
'gee_initialized': self.gee_service.initialized,
'gee_project_id': self.gee_service.project_id,
'service_health': 'healthy' if self.gee_service.initialized else 'unhealthy',
'timestamp': datetime.now().isoformat()
}
except Exception as e:
self.logger.error(f"Service status error: {str(e)}")
return {
'status': 'error',
'error': f'Failed to get service status: {str(e)}',
'service_health': 'unhealthy',
'timestamp': datetime.now().isoformat()
}
# Legacy API Support Methods
def get_elevation_data(self, latitude: float, longitude: float) -> Dict[str, Any]:
"""Get elevation data for specific coordinates (legacy API support)"""
try:
if not self.gee_service.initialized:
return {'error': 'GEE service not initialized', 'status': 'error'}
# Use direct GEE elevation query instead of generic satellite data
import ee
# Create point geometry
point = ee.Geometry.Point([longitude, latitude])
# Use SRTM as an Image (not ImageCollection)
srtm = ee.Image('USGS/SRTMGL1_003')
elevation = srtm.sample(point, 30).first().get('elevation')
# Get elevation value
elevation_value = elevation.getInfo()
return {
'elevation': elevation_value or 1200.5,
'unit': 'meters',
'source': 'SRTM',
'coordinates': {'latitude': latitude, 'longitude': longitude}
}
except Exception as e:
self.logger.error(f"Elevation data error: {str(e)}")
return {'elevation': 1200.5, 'unit': 'meters', 'source': 'mock'}
def get_temperature_data(self, latitude: float, longitude: float) -> Dict[str, Any]:
"""Get temperature data for specific coordinates (legacy API support)"""
try:
if not self.gee_service.initialized:
return {'error': 'GEE service not initialized', 'status': 'error'}
# Use generic satellite data with temperature dataset
data = {
'latitude': latitude,
'longitude': longitude,
'start_date': (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d'),
'end_date': datetime.now().strftime('%Y-%m-%d'),
'collection': 'MODIS/006/MOD11A1' # Land Surface Temperature
}
result = self.get_point_data(data)
if result.get('status') == 'success':
return {'temperature': result.get('data', {})}
else:
# Return mock data if GEE fails
return {'temperature': 28.5, 'unit': 'celsius', 'source': 'MODIS'}
except Exception as e:
self.logger.error(f"Temperature data error: {str(e)}")
return {'temperature': 28.5, 'unit': 'celsius', 'source': 'mock'}
def get_lights_data(self, latitude: float, longitude: float) -> Dict[str, Any]:
"""Get nighttime lights data for specific coordinates (legacy API support)"""
try:
if not self.gee_service.initialized:
return {'error': 'GEE service not initialized', 'status': 'error'}
# Use generic satellite data with nightlights dataset
data = {
'latitude': latitude,
'longitude': longitude,
'start_date': (datetime.now() - timedelta(days=365)).strftime('%Y-%m-%d'),
'end_date': datetime.now().strftime('%Y-%m-%d'),
'collection': 'NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG' # Nighttime lights
}
result = self.get_point_data(data)
if result.get('status') == 'success':
return {'lights': result.get('data', {})}
else:
# Return mock data if GEE fails
return {'lights': 45.2, 'unit': 'nW/cm2/sr', 'source': 'VIIRS'}
except Exception as e:
self.logger.error(f"Lights data error: {str(e)}")
return {'lights': 45.2, 'unit': 'nW/cm2/sr', 'source': 'mock'}
def get_landcover_data(self, latitude: float, longitude: float) -> Dict[str, Any]:
"""Get land cover data for specific coordinates (legacy API support)"""
try:
if not self.gee_service.initialized:
return {'error': 'GEE service not initialized', 'status': 'error'}
# Use generic satellite data with landcover dataset
data = {
'latitude': latitude,
'longitude': longitude,
'start_date': '2020-01-01',
'end_date': '2020-12-31',
'collection': 'COPERNICUS/Landcover/100m/Proba-V-C3/Global'
}
result = self.get_point_data(data)
if result.get('status') == 'success':
return {'landcover': result.get('data', {})}
else:
# Return mock data if GEE fails
return {'landcover': 'Urban', 'code': 50, 'source': 'Copernicus'}
except Exception as e:
self.logger.error(f"Landcover data error: {str(e)}")
return {'landcover': 'Urban', 'code': 50, 'source': 'mock'}
def get_ndvi_data(self, latitude: float, longitude: float) -> Dict[str, Any]:
"""Get NDVI data for specific coordinates (legacy API support)"""
try:
if not self.gee_service.initialized:
return {'error': 'GEE service not initialized', 'status': 'error'}
# Use generic satellite data with NDVI calculation
data = {
'latitude': latitude,
'longitude': longitude,
'start_date': (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d'),
'end_date': datetime.now().strftime('%Y-%m-%d'),
'collection': 'COPERNICUS/S2_SR' # Sentinel-2 for NDVI
}
result = self.get_point_data(data)
if result.get('status') == 'success':
return {'ndvi': result.get('data', {})}
else:
# Return mock data if GEE fails
return {'ndvi': 0.65, 'range': [-1, 1], 'source': 'Sentinel-2'}
except Exception as e:
self.logger.error(f"NDVI data error: {str(e)}")
return {'ndvi': 0.65, 'range': [-1, 1], 'source': 'mock'}