This document covers every process available in the FirstLight platform: from local development to cloud deployment, from algorithm selection to end-to-end workflows.
- Running Locally
- Running on a Server
- Deployment Options
- Available Models & Algorithms
- Processing Tracks & Pipelines
- Module Interconnections
- Generating Visualizations
- Creating Analytics
- End-to-End Workflow Examples
- Quick Reference
# Clone the repository
git clone https://github.com/gpriceless/firstlight.git
cd firstlight
# Create virtual environment (recommended)
python -m venv .venv
source .venv/bin/activate # Linux/Mac
# .venv\Scripts\activate # Windows
# Install in development mode
pip install -e .
# Install geospatial dependencies
pip install rasterio geopandas xarray pyproj shapely# All tests
./run_tests.py
# By hazard type
./run_tests.py flood # Flood detection tests
./run_tests.py wildfire # Wildfire tests
./run_tests.py storm # Storm damage tests
# By component
./run_tests.py schemas # Schema validation
./run_tests.py intent # Intent resolution
./run_tests.py providers # Data providers
./run_tests.py algorithms # All algorithm tests
# By algorithm
./run_tests.py --algorithm sar # SAR threshold
./run_tests.py --algorithm ndwi # NDWI optical
./run_tests.py --algorithm hand # HAND model
./run_tests.py --algorithm dnbr # Burn severity
./run_tests.py --algorithm thermal # Thermal anomaly
./run_tests.py --algorithm wind # Wind damage
# Test types
./run_tests.py --quick # Fast tests only
./run_tests.py slow # Slow/comprehensive tests
./run_tests.py integration # Integration tests
# List all categories
./run_tests.py --listThe flight command provides access to all platform capabilities:
# Get help
flight --help
flight info # Show system info
# Data discovery
flight discover --area area.geojson --start 2024-09-15 --end 2024-09-20 --event flood
flight discover --bbox -80.5,25.5,-80.0,26.0 --event wildfire --format json
# Data ingestion
flight ingest --area area.geojson --source sentinel1 --output ./data/
flight ingest --area area.geojson --source sentinel2,landsat8 --output ./data/
# Analysis
flight analyze --input ./data/ --algorithm sar_threshold --output ./results/
flight analyze --input ./data/ --algorithm ndwi --confidence 0.8 --output ./results/
# Validation
flight validate --input ./results/ --checks sanity,cross_validation
flight validate --input ./results/ --reference ground_truth.geojson
# Export
flight export --input ./results/ --format geotiff,geojson,pdf --output ./products/
# Full pipeline
flight run --area area.geojson --event flood --profile laptop --output ./products/
flight run --area area.geojson --event wildfire --profile workstation --output ./products/
# Monitoring
flight status --workdir ./products/
flight resume --workdir ./products/Profiles adapt processing to your hardware:
| Profile | Memory | Workers | Tile Size | Use Case |
|---|---|---|---|---|
edge |
1 GB | 1 | 128px | Raspberry Pi, embedded devices |
laptop |
2 GB | 2 | 256px | Local development |
workstation |
8 GB | 4 | 512px | Desktop processing |
cloud |
32 GB | 16 | 1024px | Server deployment |
Usage:
flight run --area miami.geojson --event flood --profile laptop
flight run --area california.geojson --event wildfire --profile cloudCreate a .flight.yaml in your project or home directory:
# .flight.yaml - User configuration
profile: laptop
cache_dir: ~/.flight/cache
log_level: info
providers:
sentinel1:
api_key: ${COPERNICUS_API_KEY}
sentinel2:
api_key: ${COPERNICUS_API_KEY}
landsat:
use_usgs: true
output:
formats:
- geotiff
- geojson
include_provenance: true# Development server with hot reload
python -m api.main
# Production with uvicorn
uvicorn api.main:app --host 0.0.0.0 --port 8000 --workers 4
# With gunicorn (recommended for production)
gunicorn api.main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000API documentation available at:
- Swagger UI:
http://localhost:8000/api/docs - ReDoc:
http://localhost:8000/api/redoc
# Submit event for processing
curl -X POST http://localhost:8000/api/v1/events \
-H "Content-Type: application/json" \
-d '{
"intent": {"class": "flood.coastal"},
"spatial": {"type": "Polygon", "coordinates": [[[-80.3, 25.7], [-80.1, 25.7], [-80.1, 25.9], [-80.3, 25.9], [-80.3, 25.7]]]},
"temporal": {"start": "2024-09-15T00:00:00Z", "end": "2024-09-20T23:59:59Z"}
}'
# Check status
curl http://localhost:8000/api/v1/events/{event_id}/status
# Get products
curl http://localhost:8000/api/v1/events/{event_id}/products
curl http://localhost:8000/api/v1/events/{event_id}/products/flood_extent.geojson --output flood.geojson
# Browse catalog
curl http://localhost:8000/api/v1/catalog/algorithms
curl http://localhost:8000/api/v1/catalog/providers
curl http://localhost:8000/api/v1/catalog/event-classes
# Health check
curl http://localhost:8000/api/v1/health# Start full stack (API + Workers + Redis + PostgreSQL)
docker compose up -d
# Start specific services
docker compose up -d api worker
# View logs
docker compose logs -f api
docker compose logs -f worker
# Stop all services
docker compose down
# With custom configuration
POSTGRES_PASSWORD=secure_password docker compose up -dFor CLI-only operations without external services:
# Run CLI commands
docker compose -f docker-compose.minimal.yml run --rm cli flight --help
docker compose -f docker-compose.minimal.yml run --rm cli flight analyze --help
# Run full pipeline
docker compose -f docker-compose.minimal.yml run --rm cli flight run \
--area /app/examples/flood_event.yaml \
--output /app/output/# Create namespace
kubectl apply -f deploy/kubernetes/namespace.yaml
# Apply configuration
kubectl apply -f deploy/kubernetes/configmaps/
kubectl apply -f deploy/kubernetes/secrets/
# Deploy storage
kubectl apply -f deploy/kubernetes/persistentvolumes/
# Deploy services
kubectl apply -f deploy/kubernetes/deployments/api.yaml
kubectl apply -f deploy/kubernetes/deployments/worker.yaml
kubectl apply -f deploy/kubernetes/services/
# Configure ingress
kubectl apply -f deploy/kubernetes/ingress.yaml
# Enable autoscaling
kubectl apply -f deploy/kubernetes/hpa.yamlCheck deployment:
kubectl get pods -n firstlight
kubectl get services -n firstlight
kubectl logs -f deployment/api -n firstlightECS (Elastic Container Service):
# Deploy using AWS CLI
aws ecs create-cluster --cluster-name flight-cluster
aws ecs register-task-definition --cli-input-json file://deploy/aws/ecs/task-definition.json
aws ecs create-service --cli-input-json file://deploy/aws/ecs/service.jsonLambda (Serverless):
# Deploy Lambda function
cd deploy/aws/lambda
sam build
sam deploy --guidedAWS Batch (Large-scale processing):
# Submit batch job
aws batch submit-job \
--job-name flood-analysis \
--job-queue flight-queue \
--job-definition flight-analysis \
--container-overrides '{
"command": ["flight", "run", "--area", "s3://bucket/area.geojson", "--event", "flood"]
}'Azure Kubernetes Service (AKS):
# Deploy to AKS
kubectl apply -f deploy/azure/aks/deployment.yamlAzure Container Instances:
az container create \
--resource-group flight-rg \
--name flight-api \
--image firstlight-api:latest \
--ports 8000 \
--environment-variables LOG_LEVEL=infoCloud Run:
gcloud run deploy flight-api \
--image gcr.io/your-project/firstlight-api:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticatedGKE (Google Kubernetes Engine):
kubectl apply -f deploy/gcp/kubernetes/deployment.yamlUsing Ansible:
# Standalone deployment
ansible-playbook -i inventory deploy/on-prem/standalone.yaml
# Cluster deployment
ansible-playbook -i inventory deploy/on-prem/cluster.yamlFor resource-constrained devices (Raspberry Pi, NVIDIA Jetson):
# Build edge image
docker build -f deploy/edge/Dockerfile.arm64 -t flight-edge .
# Run on edge device
docker run -v /data:/data flight-edge flight run \
--area /data/area.geojson \
--event flood \
--profile edge| Algorithm | ID | Data Required | Accuracy | Best For |
|---|---|---|---|---|
| SAR Threshold | flood.baseline.threshold_sar |
Sentinel-1 SAR | 75-90% | Cloud cover, all-weather |
| NDWI Optical | flood.baseline.ndwi_optical |
Sentinel-2, Landsat | 80-92% | Clear conditions |
| Change Detection | flood.baseline.change_detection |
Pre/post imagery | 78-88% | Event comparison |
| HAND Model | flood.baseline.hand_model |
DEM + flood extent | 70-85% | Flood depth estimation |
| UNet Segmentation | flood.advanced.unet_segmentation |
Multi-sensor | 85-95% | GPU available (experimental) |
| Ensemble Fusion | flood.advanced.ensemble_fusion |
Multiple outputs | 88-95% | High confidence needed |
SAR Threshold (core/analysis/library/baseline/flood/threshold_sar.py):
from core.analysis.library.baseline.flood import ThresholdSARAlgorithm
algo = ThresholdSARAlgorithm()
result = algo.execute(
sar_data,
pixel_size_m=10.0,
threshold_db=-15.0, # Default threshold
min_area_pixels=25
)
print(f"Flood area: {result.statistics['flood_area_ha']} hectares")NDWI Optical (core/analysis/library/baseline/flood/ndwi_optical.py):
from core.analysis.library.baseline.flood import NDWIOpticalAlgorithm
algo = NDWIOpticalAlgorithm()
result = algo.execute(
optical_data,
green_band="B03",
nir_band="B08",
threshold=0.3
)| Algorithm | ID | Data Required | Use Case |
|---|---|---|---|
| Thermal Anomaly | wildfire.baseline.thermal_anomaly |
MODIS thermal | Active fire detection |
| dNBR Burn Severity | wildfire.baseline.nbr_differenced |
Pre/post optical | Post-fire severity |
| Burned Area Classifier | wildfire.baseline.ba_classifier |
Multi-temporal optical | Burned/unburned mapping |
dNBR Burn Severity (core/analysis/library/baseline/wildfire/nbr_differenced.py):
from core.analysis.library.baseline.wildfire import NBRDifferencedAlgorithm
algo = NBRDifferencedAlgorithm()
result = algo.execute(
pre_image=pre_fire_data,
post_image=post_fire_data,
nir_band="B08",
swir_band="B12"
)
# Severity classes: unburned, low, moderate-low, moderate-high, high| Algorithm | ID | Data Required | Use Case |
|---|---|---|---|
| Wind Damage | storm.baseline.wind_damage |
Optical/SAR + wind data | Tree/vegetation damage |
| Structural Damage | storm.baseline.structural_damage |
High-res optical | Building damage assessment |
The system automatically selects algorithms based on available data:
from core.analysis.selection import DeterministicSelector
selector = DeterministicSelector()
algorithms = selector.select(
event_class="flood.coastal",
available_data=["sentinel1_sar", "copernicus_dem"],
constraints={"max_cloud_cover": 0.4}
)
# Returns: ["flood.baseline.threshold_sar", "flood.baseline.hand_model"]The system supports hierarchical event classification:
Flood Events:
flood.riverine- River overflow floodingflood.coastal.storm_surge- Coastal storm surgeflood.coastal.tsunami- Tsunami inundationflood.pluvial- Urban/rainfall floodingflood.flash- Flash flooding
Wildfire Events:
wildfire.forest- Forest fireswildfire.grassland- Grassland fireswildfire.urban- Urban interface fires
Storm Events:
storm.tropical- Hurricane/typhoon damagestorm.severe_convective- Tornado damagestorm.winter- Blizzard/ice damage
Create custom pipelines in YAML:
# my_flood_pipeline.yaml
id: custom_flood_pipeline
name: Custom SAR Flood Mapping
version: "1.0.0"
applicable_classes:
- flood.riverine
- flood.coastal
inputs:
- name: sar_pre_event
type: raster
source: sentinel1_grd
temporal_role: pre_event
required: true
- name: sar_post_event
type: raster
source: sentinel1_grd
temporal_role: post_event
required: true
- name: dem
type: raster
source: copernicus_dem
required: true
steps:
- id: normalize_sar_pre
processor: ingestion.normalize_sar
inputs: [sar_pre_event]
parameters:
calibration: sigma0
output_db: true
- id: normalize_sar_post
processor: ingestion.normalize_sar
inputs: [sar_post_event]
parameters:
calibration: sigma0
output_db: true
- id: speckle_filter
processor: sar.speckle_filter
inputs: [normalize_sar_post.output]
parameters:
method: lee
window_size: 7
- id: change_detection
processor: flood.sar_change_detection
inputs:
- normalize_sar_pre.output
- speckle_filter.output
parameters:
threshold_db: -3.0
- id: terrain_mask
processor: flood.terrain_mask
inputs:
- change_detection.output
- dem
parameters:
slope_threshold_degrees: 15
outputs:
- name: flood_extent
type: raster
format: cog
step: terrain_mask
quality_checks:
- type: spatial_coherence
- type: temporal_consistencyRun custom pipeline:
flight run --pipeline my_flood_pipeline.yaml --area area.geojson --output ./products/| Source | Type | Provider | Availability |
|---|---|---|---|
| Sentinel-1 | SAR | Copernicus | Free, global |
| Sentinel-2 | Optical | Copernicus | Free, global |
| Landsat 8/9 | Optical | USGS | Free, global |
| MODIS | Optical/Thermal | NASA | Free, global |
| Copernicus DEM | Elevation | Copernicus | Free, global |
| SRTM | Elevation | NASA | Free, 60°N-56°S |
| FABDEM | Elevation | Bristol/CEDA | Free, academic |
| ERA5 | Weather | ECMWF | Free, global |
| GFS | Weather | NOAA | Free, global |
| OpenStreetMap | Vector | OSM | Free, global |
┌─────────────────────────────────────────────────────────────────────┐
│ USER INTERFACES │
├─────────────┬─────────────┬─────────────────────────────────────────┤
│ CLI │ REST API │ Python SDK │
│ (flight) │ (FastAPI) │ (import core.*) │
└──────┬──────┴──────┬──────┴─────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ AGENT ORCHESTRATION │
├─────────────┬─────────────┬─────────────┬─────────────┬─────────────┤
│ Orchestrator│ Discovery │ Pipeline │ Quality │ Reporting │
│ Agent │ Agent │ Agent │ Agent │ Agent │
└──────┬──────┴──────┬──────┴──────┬──────┴──────┬──────┴──────┬──────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ CORE PROCESSING LAYER │
├─────────────┬─────────────┬─────────────┬─────────────┬─────────────┤
│ Intent │ Data │ Analysis │ Quality │ Resilience │
│ Resolution │ Discovery │ Execution │ Control │ & Fallback │
│ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │
│ │Resolver │ │ │ STAC │ │ │Algorithm│ │ │ Sanity │ │ │ Degrade │ │
│ │Classifier│ │ │ WMS/WCS │ │ │ Library │ │ │ Checks │ │ │ Mode │ │
│ │Registry │ │ │Providers│ │ │ Selector│ │ │CrossVal │ │ │Fallbacks│ │
│ └─────────┘ │ └─────────┘ │ │ Assembly│ │ │Uncertain│ │ └─────────┘ │
│ │ │ │ Runner │ │ └─────────┘ │ │
│ │ │ └─────────┘ │ │ │
└──────┬──────┴──────┬──────┴──────┬──────┴──────┬──────┴──────┬──────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ DATA LAYER │
├─────────────┬─────────────┬─────────────┬─────────────┬─────────────┤
│ Ingestion │ Fusion │ Cache │ Storage │ Provenance │
│ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │
│ │Normalize│ │ │Alignment│ │ │ R-tree │ │ │ Local │ │ │ Lineage │ │
│ │Validate │ │ │Correct │ │ │ Index │ │ │ Cloud │ │ │Tracking │ │
│ │ Enrich │ │ │ Merge │ │ │ TTL │ │ │ COG │ │ └─────────┘ │
│ │ Persist │ │ └─────────┘ │ └─────────┘ │ │ Zarr │ │ │
│ └─────────┘ │ │ │ └─────────┘ │ │
└─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘
Intent Resolution:
from core.intent import IntentResolver
resolver = IntentResolver()
result = resolver.resolve("flooding in coastal Miami after Hurricane Milton")
print(result.resolved_class) # "flood.coastal.storm_surge"
print(result.confidence) # 0.92
print(result.reasoning) # Explanation of classificationData Discovery:
from core.data.broker import DataBroker
broker = DataBroker()
datasets = await broker.discover(
spatial={"type": "Polygon", "coordinates": [...]},
temporal={"start": "2024-09-15", "end": "2024-09-20"},
event_class="flood.coastal",
constraints={"max_cloud_cover": 0.4}
)
for ds in datasets:
print(f"{ds.provider}: {ds.id}, resolution: {ds.resolution_m}m")Data Ingestion:
from core.data.ingestion import DataIngester
ingester = DataIngester()
normalized_data = await ingester.ingest(
source="sentinel1",
dataset_id="S1A_IW_GRDH_1SDV_20240917...",
target_crs="EPSG:32617",
target_resolution=10.0
)Algorithm Execution:
from core.analysis.library.baseline.flood import ThresholdSARAlgorithm
algo = ThresholdSARAlgorithm()
result = algo.execute(sar_data, pixel_size_m=10.0)
# Access results
flood_mask = result.data # numpy array
stats = result.statistics # dict with area, etc.
quality = result.quality_metrics # confidence scoresQuality Control:
from core.quality import SanityChecker, CrossValidator, UncertaintyEstimator
# Sanity checks
checker = SanityChecker()
issues = checker.check(result, event_class="flood.coastal")
# Cross-validation
validator = CrossValidator()
consensus = validator.validate([result1, result2, result3])
# Uncertainty
estimator = UncertaintyEstimator()
uncertainty_map = estimator.estimate(result)Multi-Sensor Fusion:
from core.analysis.fusion import MultiSensorFusion
fusion = MultiSensorFusion()
fused_result = fusion.fuse(
results=[sar_result, optical_result],
weights=[0.6, 0.4],
method="weighted_consensus"
)from agents.orchestrator import OrchestratorAgent
from agents.discovery import DiscoveryAgent
from agents.pipeline import PipelineAgent
# Create agents
orchestrator = OrchestratorAgent()
discovery = DiscoveryAgent()
pipeline = PipelineAgent()
# Submit event
event = orchestrator.submit(event_spec)
# Agents communicate via message bus
# Orchestrator delegates to Discovery → Pipeline → Quality → Reporting
result = await orchestrator.wait_for_completion(event.id)# Export to multiple formats
flight export --input ./results/ --format geotiff,geojson,pdf,png --output ./products/
# Format-specific options
flight export --input ./results/ --format geotiff --cog --compression lzw
flight export --input ./results/ --format geojson --simplify 10m
flight export --input ./results/ --format pdf --include-maps --include-statisticsfrom core.visualization import MapGenerator, ReportGenerator
# Generate flood extent map
map_gen = MapGenerator()
map_gen.create_flood_map(
flood_extent=result.data,
background="satellite",
output_path="flood_map.png",
title="Miami Flood Extent - Sept 17, 2024",
legend=True,
scale_bar=True,
north_arrow=True
)
# Generate uncertainty visualization
map_gen.create_uncertainty_map(
uncertainty=uncertainty_layer,
output_path="uncertainty_map.png",
colormap="RdYlGn_r"
)
# Generate PDF report
report_gen = ReportGenerator()
report_gen.generate(
event=event_spec,
results=analysis_results,
quality_metrics=quality_report,
output_path="flood_report.pdf",
template="emergency_response"
)| Type | Description | Output |
|---|---|---|
| Flood extent map | Binary flood/no-flood overlay | PNG, GeoTIFF |
| Flood depth map | Continuous depth estimation | PNG, GeoTIFF |
| Burn severity map | Severity classification | PNG, GeoTIFF |
| Change magnitude | Pre/post difference | PNG, GeoTIFF |
| Uncertainty map | Confidence visualization | PNG, GeoTIFF |
| Time series | Temporal progression | PNG, GIF |
| Vector overlay | GeoJSON on basemap | HTML, PNG |
| PDF report | Complete analysis report |
Every algorithm output includes statistics:
result = algo.execute(data)
# Access statistics
stats = result.statistics
print(f"Flood area: {stats['flood_area_ha']} hectares")
print(f"Flood area: {stats['flood_area_km2']} km²")
print(f"Affected pixels: {stats['affected_pixel_count']}")
print(f"Total pixels: {stats['total_pixel_count']}")
print(f"Flood percentage: {stats['flood_percentage']}%")from core.analytics import AreaCalculator, ZonalStatistics, ImpactEstimator
# Calculate area by administrative zone
zonal = ZonalStatistics()
stats_by_zone = zonal.calculate(
raster=flood_extent,
zones=admin_boundaries, # GeoDataFrame
statistics=["area", "percentage", "max_depth"]
)
# Impact estimation
impact = ImpactEstimator()
impact_report = impact.estimate(
flood_extent=flood_extent,
population_data=population_raster,
building_footprints=buildings_geojson,
infrastructure=roads_and_utilities
)
print(f"Affected population: {impact_report['affected_population']}")
print(f"Affected buildings: {impact_report['affected_buildings']}")
print(f"Road km flooded: {impact_report['flooded_road_km']}")from core.analytics import TimeSeriesAnalyzer
# Analyze flood progression
ts_analyzer = TimeSeriesAnalyzer()
progression = ts_analyzer.analyze(
images=[day1_data, day2_data, day3_data, day4_data],
timestamps=["2024-09-15", "2024-09-16", "2024-09-17", "2024-09-18"],
algorithm="flood.baseline.threshold_sar"
)
# Plot flood area over time
progression.plot_area_time_series()
# Get peak flood timing
print(f"Peak flood date: {progression.peak_date}")
print(f"Peak flood area: {progression.peak_area_km2} km²")from core.quality.reporting import QualityReporter
reporter = QualityReporter()
quality_report = reporter.generate(
result=analysis_result,
validation_data=ground_truth, # Optional
cross_validation_results=cv_results
)
print(f"Overall confidence: {quality_report['overall_confidence']}")
print(f"Spatial coherence: {quality_report['spatial_coherence_score']}")
print(f"Algorithm agreement: {quality_report['algorithm_agreement']}")
print(f"Issues found: {quality_report['issues']}")Scenario: Hurricane made landfall in Miami, need flood extent mapping.
# Step 1: Create event specification
cat > miami_flood.yaml << 'EOF'
id: evt_miami_flood_2024
intent:
class: flood.coastal.storm_surge
spatial:
type: Polygon
coordinates:
- [[-80.3, 25.7], [-80.1, 25.7], [-80.1, 25.9], [-80.3, 25.9], [-80.3, 25.7]]
temporal:
start: "2024-09-15T00:00:00Z"
end: "2024-09-20T23:59:59Z"
reference_time: "2024-09-17T12:00:00Z"
constraints:
max_cloud_cover: 0.4
required_data_types: [sar, dem]
priority: critical
EOF
# Step 2: Discover available data
flight discover --event miami_flood.yaml --format json > available_data.json
# Step 3: Run full analysis pipeline
flight run --event miami_flood.yaml --profile workstation --output ./miami_products/
# Step 4: Check status (if running async)
flight status --workdir ./miami_products/
# Step 5: Validate results
flight validate --input ./miami_products/ --checks sanity,cross_validation
# Step 6: Export final products
flight export --input ./miami_products/ --format geotiff,geojson,pdf --output ./miami_final/Output files:
miami_final/flood_extent.tif- GeoTIFF flood maskmiami_final/flood_extent.geojson- Vector polygonsmiami_final/flood_depth.tif- Depth estimation (if HAND model ran)miami_final/uncertainty.tif- Confidence layermiami_final/report.pdf- Analysis reportmiami_final/provenance.json- Full lineage record
Scenario: Forest fire in California, need burn severity mapping.
import asyncio
from core.intent import IntentResolver
from core.data.broker import DataBroker
from core.data.ingestion import DataIngester
from core.analysis.selection import DeterministicSelector
from core.analysis.library.baseline.wildfire import NBRDifferencedAlgorithm
from core.quality import SanityChecker, QualityReporter
from core.visualization import MapGenerator, ReportGenerator
async def analyze_wildfire():
# Define event parameters
event = {
"intent": {"class": "wildfire.forest"},
"spatial": {
"type": "Polygon",
"coordinates": [[[-121.5, 38.8], [-121.2, 38.8], [-121.2, 39.1],
[-121.5, 39.1], [-121.5, 38.8]]]
},
"temporal": {
"start": "2024-08-10T00:00:00Z",
"end": "2024-08-25T23:59:59Z",
"reference_time": "2024-08-15T18:00:00Z"
}
}
# Step 1: Discover data
broker = DataBroker()
datasets = await broker.discover(
spatial=event["spatial"],
temporal=event["temporal"],
event_class=event["intent"]["class"],
constraints={"max_cloud_cover": 0.3}
)
print(f"Found {len(datasets)} datasets")
# Step 2: Select best pre/post images
pre_image = next(d for d in datasets if d.temporal_role == "pre_event")
post_image = next(d for d in datasets if d.temporal_role == "post_event")
# Step 3: Ingest and normalize
ingester = DataIngester()
pre_data = await ingester.ingest(pre_image, target_resolution=10.0)
post_data = await ingester.ingest(post_image, target_resolution=10.0)
# Step 4: Run burn severity algorithm
algo = NBRDifferencedAlgorithm()
result = algo.execute(
pre_image=pre_data,
post_image=post_data,
nir_band="B08",
swir_band="B12"
)
print(f"Burned area: {result.statistics['burned_area_ha']} hectares")
print(f"High severity: {result.statistics['high_severity_ha']} hectares")
# Step 5: Quality control
checker = SanityChecker()
issues = checker.check(result, event_class="wildfire.forest")
if issues:
print(f"Quality issues: {issues}")
# Step 6: Generate outputs
map_gen = MapGenerator()
map_gen.create_burn_severity_map(
severity=result.data,
output_path="burn_severity.png",
title="California Fire Burn Severity"
)
reporter = QualityReporter()
quality_report = reporter.generate(result)
report_gen = ReportGenerator()
report_gen.generate(
event=event,
results=result,
quality_metrics=quality_report,
output_path="wildfire_report.pdf"
)
return result
# Run analysis
result = asyncio.run(analyze_wildfire())Scenario: Tornado struck Oklahoma, need structural damage assessment.
# Step 1: Submit event via API
curl -X POST http://localhost:8000/api/v1/events \
-H "Content-Type: application/json" \
-d '{
"intent": {
"class": "storm.severe_convective",
"original_input": "tornado damage assessment"
},
"spatial": {
"type": "Polygon",
"coordinates": [[[-97.6, 35.4], [-97.3, 35.4], [-97.3, 35.6], [-97.6, 35.6], [-97.6, 35.4]]]
},
"temporal": {
"start": "2024-05-18T00:00:00Z",
"end": "2024-05-22T23:59:59Z",
"reference_time": "2024-05-19T20:30:00Z"
},
"constraints": {
"max_cloud_cover": 0.2,
"min_resolution_m": 3
},
"priority": "high"
}'
# Response: {"event_id": "evt_123456", "status": "queued"}
# Step 2: Poll for status
curl http://localhost:8000/api/v1/events/evt_123456/status
# Response: {"status": "processing", "progress": 45, "current_step": "algorithm_execution"}
# Step 3: Wait for completion, then get products
curl http://localhost:8000/api/v1/events/evt_123456/products
# Response: {
# "products": [
# {"name": "damage_extent.geojson", "size_bytes": 524288},
# {"name": "damage_severity.tif", "size_bytes": 10485760},
# {"name": "report.pdf", "size_bytes": 2097152}
# ]
# }
# Step 4: Download products
curl http://localhost:8000/api/v1/events/evt_123456/products/damage_extent.geojson \
--output damage_extent.geojson
curl http://localhost:8000/api/v1/events/evt_123456/products/report.pdf \
--output storm_report.pdfProcess multiple events simultaneously:
# Start full stack
docker compose up -d
# Submit multiple events (they process in parallel)
for event in flood_miami.yaml wildfire_california.yaml storm_oklahoma.yaml; do
curl -X POST http://localhost:8000/api/v1/events \
-H "Content-Type: application/json" \
-d @examples/$event &
done
# Monitor all events
watch -n 5 'curl -s http://localhost:8000/api/v1/events | jq ".events[] | {id, status, progress}"'# Start analysis (may take hours for large areas)
flight run --area large_area.geojson --event flood --profile cloud --output ./analysis/
# If interrupted (Ctrl+C, system restart, etc.), resume:
flight resume --workdir ./analysis/
# The system tracks checkpoints and resumes from last completed step# Discovery
flight discover --area X.geojson --event flood # Find available data
flight discover --bbox -80.5,25.5,-80.0,26.0 --event wildfire
# Analysis
flight run --event X.yaml --profile laptop # Full pipeline
flight analyze --input ./data/ --algorithm sar_threshold
# Validation
flight validate --input ./results/ --checks sanity
flight validate --input ./results/ --reference truth.geojson
# Export
flight export --input ./results/ --format geotiff,geojson,pdf
# Status
flight status --workdir ./products/
flight resume --workdir ./products/# Intent
from core.intent import IntentResolver
# Data
from core.data.broker import DataBroker
from core.data.ingestion import DataIngester
# Algorithms
from core.analysis.library.baseline.flood import (
ThresholdSARAlgorithm,
NDWIOpticalAlgorithm,
ChangeDetectionAlgorithm,
HANDModelAlgorithm
)
from core.analysis.library.baseline.wildfire import (
ThermalAnomalyAlgorithm,
NBRDifferencedAlgorithm,
BurnedAreaClassifier
)
from core.analysis.library.baseline.storm import (
WindDamageAlgorithm,
StructuralDamageAlgorithm
)
# Selection
from core.analysis.selection import DeterministicSelector
# Quality
from core.quality import SanityChecker, CrossValidator, UncertaintyEstimator
from core.quality.reporting import QualityReporter
# Visualization
from core.visualization import MapGenerator, ReportGeneratorid: evt_unique_id
intent:
class: flood.coastal | wildfire.forest | storm.severe_convective
source: explicit | inferred
confidence: 0.0-1.0
spatial:
type: Polygon
coordinates: [[[lon1, lat1], [lon2, lat2], ...]]
crs: EPSG:4326
bbox: [min_lon, min_lat, max_lon, max_lat]
temporal:
start: "YYYY-MM-DDTHH:MM:SSZ"
end: "YYYY-MM-DDTHH:MM:SSZ"
reference_time: "YYYY-MM-DDTHH:MM:SSZ"
constraints:
max_cloud_cover: 0.0-1.0
min_resolution_m: number
required_data_types: [sar, optical, dem, weather]
optional_data_types: [...]
priority: critical | high | normal | low
metadata:
created_at: "YYYY-MM-DDTHH:MM:SSZ"
created_by: "string"
tags: [tag1, tag2]# Full stack
docker compose up -d
docker compose down
docker compose logs -f api
# CLI only
docker compose -f docker-compose.minimal.yml run --rm cli flight run ...
# Build images
docker build -f docker/api/Dockerfile -t flight-api .
docker build -f docker/cli/Dockerfile -t flight-cli ../run_tests.py # All tests
./run_tests.py flood # Flood tests
./run_tests.py wildfire # Wildfire tests
./run_tests.py storm # Storm tests
./run_tests.py schemas # Schema validation
./run_tests.py --algorithm sar # SAR algorithm
./run_tests.py --quick # Fast tests only
./run_tests.py --list # List categories"No data found" error:
- Check date range - satellite revisit is typically 6-12 days
- Expand temporal window
- Try different data sources
"Algorithm not applicable" error:
- Check if required data types are available
- Some algorithms need specific bands (e.g., dNBR needs NIR+SWIR)
- Use
flight discoverto see what's available
Memory errors:
- Use appropriate profile for your hardware
- Use
--profile laptopfor machines with <8GB RAM - Large areas automatically use tiling
Docker connection refused:
- Ensure services are running:
docker compose ps - Check logs:
docker compose logs api - Verify ports aren't in use:
lsof -i :8000
flight --help # General help
flight run --help # Command-specific help
flight info # System information
# Test your setup
./run_tests.py schemas # Verify schemas
./run_tests.py --quick # Quick sanity checkLast updated: 2024