"""
Report generation tool for comprehensive analysis.
"""
from datetime import datetime
from typing import Optional, List, Literal
import structlog
from geosight.tools.geocoding import resolve_location
from geosight.tools.indices import calculate_ndvi, calculate_ndwi
from geosight.tools.classification import detect_land_cover
from geosight.tools.change_detection import detect_changes
logger = structlog.get_logger(__name__)
async def generate_report(
start_date: str,
end_date: str,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
location_name: Optional[str] = None,
radius_km: float = 10.0,
analyses: Optional[List[str]] = None,
output_format: Literal["pdf", "html", "markdown"] = "markdown",
report_title: Optional[str] = None,
) -> dict:
"""Generate comprehensive analysis report."""
try:
lat, lon, display_name = await resolve_location(
location_name=location_name,
latitude=latitude,
longitude=longitude,
)
if analyses is None:
analyses = ["ndvi", "land_cover"]
logger.info(
"generating_report",
location=display_name,
analyses=analyses,
)
title = report_title or f"Satellite Analysis Report: {display_name}"
# Run requested analyses
results = {}
if "ndvi" in analyses:
results["ndvi"] = await calculate_ndvi(
start_date=start_date,
end_date=end_date,
latitude=lat,
longitude=lon,
radius_km=radius_km,
)
if "ndwi" in analyses:
results["ndwi"] = await calculate_ndwi(
start_date=start_date,
end_date=end_date,
latitude=lat,
longitude=lon,
radius_km=radius_km,
)
if "land_cover" in analyses:
results["land_cover"] = await detect_land_cover(
date=end_date,
latitude=lat,
longitude=lon,
radius_km=radius_km,
)
if "change_detection" in analyses:
results["change_detection"] = await detect_changes(
date_before=start_date,
date_after=end_date,
latitude=lat,
longitude=lon,
radius_km=radius_km,
)
# Generate report content
if output_format == "markdown":
report_content = generate_markdown_report(
title, display_name, lat, lon, start_date, end_date, radius_km, results
)
elif output_format == "html":
report_content = generate_html_report(
title, display_name, lat, lon, start_date, end_date, radius_km, results
)
else:
report_content = generate_markdown_report(
title, display_name, lat, lon, start_date, end_date, radius_km, results
)
summary = f"""📄 **Report Generated Successfully**
📍 **Location:** {display_name}
📅 **Period:** {start_date} to {end_date}
📐 **Area:** {(radius_km * 2) ** 2:.1f} km²
📊 **Analyses included:** {', '.join(analyses)}
📝 **Format:** {output_format.upper()}
The report includes detailed analysis results with visualizations.
"""
return {
"summary": summary,
"report_content": report_content,
"format": output_format,
"title": title,
"analyses_completed": list(results.keys()),
"statistics": {
"location": display_name,
"latitude": lat,
"longitude": lon,
"start_date": start_date,
"end_date": end_date,
"radius_km": radius_km,
},
}
except ValueError as e:
return {"summary": f"❌ **Error:** {str(e)}", "error": str(e)}
except Exception as e:
logger.error("report_generation_error", error=str(e), exc_info=True)
return {"summary": f"❌ **Report generation failed:** {str(e)}", "error": str(e)}
def generate_markdown_report(
title: str,
location: str,
lat: float,
lon: float,
start_date: str,
end_date: str,
radius_km: float,
results: dict,
) -> str:
"""Generate Markdown report."""
sections = [
f"# {title}",
"",
f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
"",
"## Executive Summary",
"",
f"This report presents satellite imagery analysis for **{location}** ",
f"covering the period from {start_date} to {end_date}.",
"",
"## Study Area",
"",
f"- **Location:** {location}",
f"- **Coordinates:** {lat:.4f}°N, {lon:.4f}°E",
f"- **Analysis Area:** {(radius_km * 2):.1f} km × {(radius_km * 2):.1f} km ({(radius_km * 2) ** 2:.1f} km²)",
f"- **Date Range:** {start_date} to {end_date}",
"",
]
if "ndvi" in results:
ndvi = results["ndvi"]
stats = ndvi.get("statistics", {})
sections.extend([
"## Vegetation Analysis (NDVI)",
"",
"The Normalized Difference Vegetation Index (NDVI) measures vegetation health and density.",
"",
f"- **Mean NDVI:** {stats.get('mean_ndvi', 'N/A'):.3f}" if isinstance(stats.get('mean_ndvi'), (int, float)) else "- **Mean NDVI:** N/A",
f"- **Dense Vegetation:** {stats.get('dense_vegetation_pct', 0):.1f}%",
f"- **Moderate Vegetation:** {stats.get('moderate_vegetation_pct', 0):.1f}%",
f"- **Sparse Vegetation:** {stats.get('sparse_vegetation_pct', 0):.1f}%",
"",
])
if "ndwi" in results:
ndwi = results["ndwi"]
stats = ndwi.get("statistics", {})
sections.extend([
"## Water Analysis (NDWI)",
"",
"The Normalized Difference Water Index (NDWI) identifies water bodies and moisture.",
"",
f"- **Water Coverage:** {stats.get('water_coverage_pct', 0):.1f}%",
f"- **Water Area:** {stats.get('water_area_km2', 0):.2f} km²",
"",
])
if "land_cover" in results:
lc = results["land_cover"]
stats = lc.get("statistics", {})
classes = stats.get("class_percentages", {})
sections.extend([
"## Land Cover Classification",
"",
"Machine learning classification of land cover types:",
"",
])
for class_name, pct in sorted(classes.items(), key=lambda x: -x[1]):
if pct > 0.5:
sections.append(f"- **{class_name}:** {pct:.1f}%")
sections.append("")
if "change_detection" in results:
cd = results["change_detection"]
stats = cd.get("statistics", {})
sections.extend([
"## Change Detection",
"",
f"Analysis of changes between {start_date} and {end_date}:",
"",
f"- **Changed Area:** {stats.get('changed_area_km2', 0):.2f} km²",
f"- **Change Percentage:** {stats.get('change_percentage', 0):.1f}%",
"",
])
sections.extend([
"## Methodology",
"",
"This analysis uses Sentinel-2 satellite imagery processed through the GeoSight platform.",
"Machine learning models are applied for land cover classification and object detection.",
"",
"## Disclaimer",
"",
"This report is generated automatically and should be verified with ground truth data.",
"",
"---",
f"*Report generated by GeoSight MCP Server v1.0.0*",
])
return "\n".join(sections)
def generate_html_report(
title: str,
location: str,
lat: float,
lon: float,
start_date: str,
end_date: str,
radius_km: float,
results: dict,
) -> str:
"""Generate HTML report."""
markdown_content = generate_markdown_report(
title, location, lat, lon, start_date, end_date, radius_km, results
)
# Simple markdown to HTML conversion
html_content = f"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{title}</title>
<style>
body {{ font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }}
h1 {{ color: #2c3e50; border-bottom: 2px solid #3498db; }}
h2 {{ color: #34495e; margin-top: 30px; }}
ul {{ list-style-type: disc; }}
.stats {{ background: #f8f9fa; padding: 15px; border-radius: 5px; }}
</style>
</head>
<body>
<pre>{markdown_content}</pre>
</body>
</html>"""
return html_content