<p align="center">
<img src="https://github.com/Mirjan-Ali-Sha/open-geodata-api/blob/main/icon.png" alt="Open Geodata API Icon" width="150" height="150" />
</p>
# Open Geodata API - Complete User Guide
## Table of Contents
1. [Introduction](README.md#introduction)
2. [Open Geodata API - Complete Tool Summary](README.md#open-geodata-api---complete-tool-summary)
3. [Installation](README.md#installation)
4. [Quick Start](README.md#quick-start)
5. [Core Concepts](README.md#core-concepts)
6. [API Reference](README.md#api-reference)
7. [Usage Examples](README.md#usage-examples)
8. [Best Practices](README.md#best-practices)
9. [Troubleshooting](README.md#troubleshooting)
10. [Advanced Usage](README.md#advanced-usage)
11. [Utility Functions](README.md#utils-functions)
12. [CLI Usage](README.md#cli-usage)
13. [FAQ](README.md#faq)
## Introduction
### What is Open Geodata API?
**Open Geodata API** is a unified Python client library that provides seamless access to multiple open geospatial data APIs. It focuses on **API access, search, and URL management** while maintaining maximum flexibility for data reading and processing.
## Open Geodata API - Complete Tool Summary
**š°ļø Unified Python Client for Satellite Data Access**
#### **What It Does**
ā
**One Interface** - Access Microsoft Planetary Computer \& AWS EarthSearch APIs<br>
ā
**Smart URLs** - Automatic signing, validation, and expiration handling<br>
ā
**Your Choice** - Use any raster package (rioxarray, rasterio, GDAL)<br>
ā
**Complete Workflow** - Search ā Filter ā Download ā Analyze
#### **Key Capabilities**
ā
**Python API** - Programmatic access with full flexibility<br>
ā
**Command Line** - `ogapi` CLI for all operations with help<br>
ā
**Smart Downloads** - Progress tracking, resume, batch processing<br>
ā
**Data Filtering** - Cloud cover, temporal, quality-based filtering<br>
ā
**URL Management** - Export, validate, and refresh URLs automatically
### Supported APIs
| API | Provider | Authentication | URL Handling |
| :-- | :-- | :-- | :-- |
| **Planetary Computer** | Microsoft | API Key + Signing | Automatic signing |
| **EarthSearch** | Element84/AWS | None required | URL validation |
### Philosophy
šÆ **Core Focus**: We provide URLs - you choose how to read them!<br>
š¦ **Use Any Package**: rioxarray, rasterio, GDAL, or any package you prefer<br>
š **Maximum Flexibility**: Zero restrictions on your workflow
## Installation
### Basic Installation
```bash
# Install core package
pip install open-geodata-api
```
### Optional Dependencies
```bash
# For spatial analysis (shapely, geopandas)
pip install open-geodata-api[spatial]
# For raster reading suggestions (rioxarray,rasterio, xarray)
pip install open-geodata-api[io] # rioxarray + xarray
# For complete examples (shapely, geopandas, rioxarray, rasterio, xarray)
pip install open-geodata-api[complete]
# Development dependencies
pip install open-geodata-api[dev]
```
### Verify Installation
```python
import open_geodata_api as ogapi
ogapi.info()
```
## Quick Start
### 30-Second Example
```python
import open_geodata_api as ogapi
# Get clients for both APIs
clients = ogapi.get_clients(pc_auto_sign=True)
pc = clients['planetary_computer']
es = clients['earth_search']
# Search for Sentinel-2 data
results = pc.search(
collections=["sentinel-2-l2a"],
bbox=[-122.5, 47.5, -122.0, 48.0],
datetime="2024-01-01/2024-03-31"
)
# Get items and URLs
items = results.get_all_items()
item = items[0]
# Get ready-to-use URLs
blue_url = item.get_asset_url('B02') # Automatically signed!
all_urls = item.get_all_asset_urls() # All assets
# Use with ANY raster package
import rioxarray
data = rioxarray.open_rasterio(blue_url)
# Or use with rasterio
import rasterio
with rasterio.open(blue_url) as src:
data = src.read(1)
```
### 5-Minute Tutorial
```python
# 1. Import and setup
import open_geodata_api as ogapi
# 2. Create clients
pc = ogapi.planetary_computer(auto_sign=True)
es = ogapi.earth_search()
# 3. Search for data
search_params = {
'collections': ['sentinel-2-l2a'],
'bbox': [-122.5, 47.5, -122.0, 48.0],
'datetime': '2024-01-01/2024-03-31',
'query': {'eo:cloud_cover': {'lt': 30}}
}
pc_results = pc.search(**search_params, limit=10)
es_results = es.search(**search_params, limit=10)
# 4. Work with results
pc_items = pc_results.get_all_items()
es_items = es_results.get_all_items()
print(f"Found: PC={len(pc_items)}, ES={len(es_items)} items")
# 5. Get URLs and use with your preferred package
item = pc_items[0]
item.print_assets_info()
# Get specific bands
rgb_urls = item.get_band_urls(['B04', 'B03', 'B02']) # Red, Green, Blue
print(f"RGB URLs: {rgb_urls}")
# Use URLs with any package you want!
```
## Core Concepts
### STAC (SpatioTemporal Asset Catalog)
Open Geodata API works with STAC-compliant APIs. Key STAC concepts:
- **Collections**: Groups of related datasets (e.g., "sentinel-2-l2a")
- **Items**: Individual products/scenes with metadata
- **Assets**: Individual files (bands, thumbnails, metadata)
### Package Architecture
```
open-geodata-api/
āāā Core Classes (Universal)
ā āāā STACItem # Individual products
ā āāā STACItemCollection # Groups of products
ā āāā STACAsset # Individual files
ā āāā STACSearch # Search results
āāā API Clients
ā āāā PlanetaryComputerCollections
ā āāā EarthSearchCollections
āāā Utilities
āāā URL signing (PC)
āāā URL validation (ES)
āāā Filtering functions
```
### Provider-Specific Handling
| Feature | Planetary Computer | EarthSearch |
| :-- | :-- | :-- |
| **Authentication** | Automatic via planetary-computer package | None required |
| **URL Signing** | Automatic (auto_sign=True) | Not applicable |
| **Asset Naming** | B01, B02, B03... | coastal, blue, green... |
| **Cloud Cover** | eo:cloud_cover | eo:cloud_cover |
## API Reference
### Factory Functions
#### `planetary_computer(auto_sign=False)`
Creates a Planetary Computer client.
**Parameters:**
- `auto_sign` (bool): Automatically sign URLs for immediate use
**Returns:** `PlanetaryComputerCollections` instance
#### `earth_search(auto_validate=False)`
Creates an EarthSearch client.
**Parameters:**
- `auto_validate` (bool): Validate URLs (currently placeholder)
**Returns:** `EarthSearchCollections` instance
#### `get_clients(pc_auto_sign=False, es_auto_validate=False)`
Creates both clients simultaneously.
**Returns:** Dictionary with 'planetary_computer' and 'earth_search' keys
### Client Methods
#### `search(collections, bbox=None, datetime=None, query=None, limit=100)`
Search for STAC items.
**Parameters:**
- `collections` (list): Collection IDs to search
- `bbox` (list): Bounding box [west, south, east, north]
- `datetime` (str): Date range "YYYY-MM-DD/YYYY-MM-DD"
- `query` (dict): Additional filters like `{"eo:cloud_cover": {"lt": 30}}`
- `limit` (int): Maximum results to return
**Returns:** `STACSearch` instance
#### `list_collections()`
Get list of available collection names.
**Returns:** List of collection ID strings
#### `get_collection_info(collection_name)`
Get detailed information about a specific collection.
**Returns:** Collection metadata dictionary
### STACItem Methods
#### `get_asset_url(asset_key, signed=None)`
Get ready-to-use URL for a specific asset.
**Parameters:**
- `asset_key` (str): Asset name (e.g., 'B02', 'blue', 'red')
- `signed` (bool): Override automatic signing behavior
**Returns:** URL string ready for any raster package
#### `get_all_asset_urls(signed=None)`
Get URLs for all available assets.
**Returns:** Dictionary `{asset_key: url}`
#### `get_band_urls(bands, signed=None)`
Get URLs for specific bands/assets.
**Parameters:**
- `bands` (list): List of asset names
**Returns:** Dictionary `{asset_key: url}`
#### `list_assets()`
Get list of available asset names.
**Returns:** List of asset key strings
#### `print_assets_info()`
Print detailed information about all assets.
### STACItemCollection Methods
#### `get_all_urls(asset_keys=None, signed=None)`
Get URLs from all items in the collection.
**Parameters:**
- `asset_keys` (list, optional): Specific assets to get URLs for
- `signed` (bool, optional): Override signing behavior
**Returns:** Dictionary `{item_id: {asset_key: url}}`
#### `to_dataframe(include_geometry=True)`
Convert collection to pandas/geopandas DataFrame.
**Parameters:**
- `include_geometry` (bool): Include spatial geometry (requires geopandas)
**Returns:** DataFrame with item metadata
#### `export_urls_json(filename, asset_keys=None)`
Export all URLs to JSON file for external processing.
## Usage Examples
### Example 1: Simple Data Discovery
```python
import open_geodata_api as ogapi
# Setup
pc = ogapi.planetary_computer(auto_sign=True)
# Find available collections
collections = pc.list_collections()
sentinel_collections = [c for c in collections if 'sentinel' in c.lower()]
print(f"Sentinel collections: {sentinel_collections}")
# Get collection details
s2_info = pc.get_collection_info('sentinel-2-l2a')
print(f"Sentinel-2 L2A: {s2_info['title']}")
print(f"Description: {s2_info['description'][:100]}...")
```
### Example 2: Geographic Search
```python
# Search around San Francisco Bay Area
bbox = [-122.5, 37.5, -122.0, 38.0]
results = pc.search(
collections=['sentinel-2-l2a'],
bbox=bbox,
datetime='2024-06-01/2024-08-31',
query={'eo:cloud_cover': {'lt': 20}}, # Less than 20% clouds
limit=20
)
items = results.get_all_items()
print(f"Found {len(items)} items with <20% cloud cover")
# Convert to DataFrame for analysis
df = items.to_dataframe()
print(f"Date range: {df['datetime'].min()} to {df['datetime'].max()}")
print(f"Cloud cover range: {df['eo:cloud_cover'].min():.1f}% to {df['eo:cloud_cover'].max():.1f}%")
```
### Example 3: Multi-Provider Comparison
```python
# Compare results from both providers
bbox = [-122.2, 47.6, -122.1, 47.7] # Seattle area
pc_results = pc.search(
collections=['sentinel-2-l2a'],
bbox=bbox,
datetime='2024-01-01/2024-03-31'
)
es_results = es.search(
collections=['sentinel-2-l2a'],
bbox=bbox,
datetime='2024-01-01T00:00:00Z/2024-03-31T23:59:59Z'
)
pc_items = pc_results.get_all_items()
es_items = es_results.get_all_items()
print(f"Planetary Computer: {len(pc_items)} items")
print(f"EarthSearch: {len(es_items)} items")
# Compare asset availability
if pc_items and es_items:
pc_assets = pc_items[0].list_assets()
es_assets = es_items[0].list_assets()
print(f"PC assets: {pc_assets[:5]}")
print(f"ES assets: {es_assets[:5]}")
```
### Example 4: URL Export for External Processing
```python
# Get URLs for specific bands across multiple items
items = pc_results.get_all_items()
# Export RGB band URLs
rgb_urls = items.get_all_urls(['B04', 'B03', 'B02']) # Red, Green, Blue
# Save to JSON for external processing
items.export_urls_json('sentinel2_rgb_urls.json', ['B04', 'B03', 'B02'])
# Use the URLs with any package
first_item_urls = rgb_urls[list(rgb_urls.keys())[0]]
print(f"Red band URL: {first_item_urls['B04']}")
# Example with different raster packages
import rioxarray
import rasterio
from osgeo import gdal
red_url = first_item_urls['B04']
# Option 1: rioxarray
red_data_xr = rioxarray.open_rasterio(red_url)
# Option 2: rasterio
with rasterio.open(red_url) as src:
red_data_rio = src.read(1)
# Option 3: GDAL
red_ds = gdal.Open(red_url)
red_data_gdal = red_ds.ReadAsArray()
print(f"Data shapes - XR: {red_data_xr.shape}, RIO: {red_data_rio.shape}, GDAL: {red_data_gdal.shape}")
```
### Example 5: Batch Processing Setup
```python
# Setup for batch processing
import json
# Search for monthly data
results = pc.search(
collections=['sentinel-2-l2a'],
bbox=[-120.0, 35.0, -119.0, 36.0],
datetime='2024-01-01/2024-12-31',
query={'eo:cloud_cover': {'lt': 15}},
limit=100
)
items = results.get_all_items()
print(f"Found {len(items)} low-cloud scenes")
# Group by month
df = items.to_dataframe()
df['month'] = df['datetime'].str[:7] # YYYY-MM
monthly_counts = df.groupby('month').size()
print("Monthly data availability:")
print(monthly_counts)
# Export all URLs for batch processing
all_urls = items.get_all_urls(['B04', 'B03', 'B02', 'B08']) # RGB + NIR
# Save configuration for external processing
config = {
'search_params': {
'bbox': [-120.0, 35.0, -119.0, 36.0],
'datetime': '2024-01-01/2024-12-31',
'collections': ['sentinel-2-l2a']
},
'items_found': len(items),
'urls': all_urls
}
with open('batch_processing_config.json', 'w') as f:
json.dump(config, f, indent=2)
print("Batch processing configuration saved!")
```
### Example 6: EarthSearch Specific Features
```python
# EarthSearch uses different asset names
es = ogapi.earth_search()
es_results = es.search(
collections=['sentinel-2-l2a'],
bbox=[-122.5, 47.5, -122.0, 48.0],
datetime='2024-06-01T00:00:00Z/2024-08-31T23:59:59Z',
limit=5
)
es_items = es_results.get_all_items()
item = es_items[0]
# EarthSearch asset names
item.print_assets_info()
# Get URLs using EarthSearch naming
rgb_urls = item.get_band_urls(['red', 'green', 'blue'])
nir_url = item.get_asset_url('nir')
print(f"RGB URLs: {list(rgb_urls.keys())}")
print(f"NIR URL ready: {nir_url[:50]}...")
# All URLs (no signing needed for EarthSearch)
all_urls = item.get_all_asset_urls()
print(f"Total assets available: {len(all_urls)}")
```
## Best Practices
### 1. Client Configuration
```python
# Recommended setup
import open_geodata_api as ogapi
# Auto-sign PC URLs for immediate use
pc = ogapi.planetary_computer(auto_sign=True)
es = ogapi.earth_search()
# Or get both at once
clients = ogapi.get_clients(pc_auto_sign=True)
```
### 2. Search Strategy
```python
# Start with broad search, then refine
results = pc.search(
collections=['sentinel-2-l2a'],
bbox=your_bbox,
datetime='2024-01-01/2024-12-31',
query={'eo:cloud_cover': {'lt': 50}}, # Start broad
limit=100
)
# Filter further based on your needs
df = results.get_all_items().to_dataframe()
filtered_df = df[df['eo:cloud_cover'] < 20] # Refine cloud cover
```
### 3. URL Management
```python
# Let the package handle URL signing automatically
item = items[0]
# This automatically handles signing based on provider
blue_url = item.get_asset_url('B02') # PC: signed, ES: validated
# Override if needed
unsigned_url = item.get_asset_url('B02', signed=False)
```
### 4. Asset Name Handling
```python
# Handle different naming conventions gracefully
def get_rgb_urls(item):
"""Get RGB URLs regardless of provider naming."""
assets = item.list_assets()
# Try Planetary Computer naming
if all(band in assets for band in ['B04', 'B03', 'B02']):
return item.get_band_urls(['B04', 'B03', 'B02'])
# Try EarthSearch naming
elif all(band in assets for band in ['red', 'green', 'blue']):
return item.get_band_urls(['red', 'green', 'blue'])
else:
print(f"Available assets: {assets}")
return {}
# Use the function
rgb_urls = get_rgb_urls(item)
```
### 5. Error Handling
```python
# Robust search with error handling
def safe_search(client, **kwargs):
"""Search with comprehensive error handling."""
try:
results = client.search(**kwargs)
items = results.get_all_items()
if len(items) == 0:
print("No items found. Try adjusting search parameters.")
return None
print(f"Found {len(items)} items")
return items
except Exception as e:
print(f"Search failed: {e}")
return None
# Use robust search
items = safe_search(
pc,
collections=['sentinel-2-l2a'],
bbox=your_bbox,
datetime='2024-01-01/2024-03-31'
)
```
### 6. Memory Management
```python
# For large datasets, process in batches
def process_in_batches(items, batch_size=10):
"""Process items in batches to manage memory."""
for i in range(0, len(items), batch_size):
batch = items[i:i+batch_size]
# Get URLs for this batch
batch_urls = {}
for item in batch:
try:
batch_urls[item.id] = item.get_band_urls(['B04', 'B03', 'B02'])
except Exception as e:
print(f"Failed to get URLs for {item.id}: {e}")
# Process batch_urls as needed
yield batch_urls
# Use batch processing
for batch_urls in process_in_batches(items):
print(f"Processing batch with {len(batch_urls)} items")
# Your processing logic here
```
## Troubleshooting
### Common Issues and Solutions
#### Issue: "planetary-computer package not found"
**Problem:** PC URL signing fails
```python
# Error: planetary-computer package not found, returning unsigned URL
```
**Solution:**
```bash
pip install planetary-computer
```
#### Issue: No items found
**Problem:** Search returns empty results
**Solutions:**
```python
# 1. Check collection names
available_collections = pc.list_collections()
print("Available collections:", available_collections)
# 2. Expand search area
bbox = [-123.0, 47.0, -121.0, 48.0] # Larger area
# 3. Expand date range
datetime = '2023-01-01/2024-12-31' # Larger time window
# 4. Relax cloud cover
query = {'eo:cloud_cover': {'lt': 80}} # More permissive
```
#### Issue: Asset not found
**Problem:** `KeyError: Asset 'B02' not found`
**Solutions:**
```python
# 1. Check available assets
item.print_assets_info()
# 2. Use correct naming for provider
# PC: B01, B02, B03...
# ES: coastal, blue, green...
# 3. Handle gracefully
try:
url = item.get_asset_url('B02')
except KeyError:
# Try alternative naming
url = item.get_asset_url('blue')
```
#### Issue: EarthSearch datetime format
**Problem:** EarthSearch requires RFC3339 format
**Solution:**
```python
# Use proper format for EarthSearch
datetime_es = '2024-01-01T00:00:00Z/2024-03-31T23:59:59Z'
# Package handles this automatically in most cases
```
#### Issue: Large data downloads
**Problem:** Memory issues with large datasets
**Solutions:**
```python
# 1. Use overview levels (if your raster package supports it)
import rioxarray
data = rioxarray.open_rasterio(url, overview_level=2)
# 2. Use chunking
data = rioxarray.open_rasterio(url, chunks={'x': 512, 'y': 512})
# 3. Read windows
import rasterio
with rasterio.open(url) as src:
window = rasterio.windows.Window(0, 0, 1024, 1024)
data = src.read(1, window=window)
```
### Debug Mode
```python
# Enable debug information
import logging
logging.basicConfig(level=logging.DEBUG)
# Check what URLs are being generated
item = items[0]
print(f"Item ID: {item.id}")
print(f"Provider: {item.provider}")
all_urls = item.get_all_asset_urls()
for asset, url in all_urls.items():
print(f"{asset}: {url[:50]}...")
```
### Validation Steps
```python
# Validate your setup
def validate_setup():
"""Validate package installation and API access."""
try:
import open_geodata_api as ogapi
print("ā
Package imported successfully")
# Test client creation
pc = ogapi.planetary_computer()
es = ogapi.earth_search()
print("ā
Clients created successfully")
# Test collection listing
pc_collections = pc.list_collections()
print(f"ā
PC collections: {len(pc_collections)} available")
# Test simple search
test_results = pc.search(
collections=['sentinel-2-l2a'],
bbox=[-122.0, 47.0, -121.0, 48.0],
limit=1
)
test_items = test_results.get_all_items()
print(f"ā
Test search: {len(test_items)} items found")
return True
except Exception as e:
print(f"ā Validation failed: {e}")
return False
# Run validation
validate_setup()
```
## Advanced Usage
### Custom Processing Workflows
```python
# Example: Multi-temporal analysis setup
def setup_temporal_analysis(bbox, date_ranges, max_cloud_cover=20):
"""Setup data for temporal analysis."""
all_data = {}
for period_name, date_range in date_ranges.items():
print(f"Searching for {period_name}...")
results = pc.search(
collections=['sentinel-2-l2a'],
bbox=bbox,
datetime=date_range,
query={'eo:cloud_cover': {'lt': max_cloud_cover}},
limit=50
)
items = results.get_all_items()
urls = items.get_all_urls(['B04', 'B03', 'B02', 'B08']) # RGB + NIR
all_data[period_name] = {
'count': len(items),
'date_range': date_range,
'urls': urls
}
print(f" Found {len(items)} items")
return all_data
# Use for seasonal analysis
seasonal_data = setup_temporal_analysis(
bbox=[-120.0, 35.0, -119.0, 36.0],
date_ranges={
'spring_2024': '2024-03-01/2024-05-31',
'summer_2024': '2024-06-01/2024-08-31',
'fall_2024': '2024-09-01/2024-11-30'
}
)
```
### Integration with Other Libraries
##### Install Required Packages
```python
pip install stackstac pystac
```
##### The Custom Functions
```python
# Example: Integration with STAC-tools
def integrate_with_stac_tools(items):
"""Convert to format compatible with other STAC tools."""
# Export as standard STAC format
stac_collection = items.to_dict() # GeoJSON FeatureCollection
# Use with pystac
try:
import pystac
# Convert items for pystac
pystac_items = []
for item_data in items.to_list():
pystac_item = pystac.Item.from_dict(item_data)
pystac_items.append(pystac_item)
print(f"Converted {len(pystac_items)} items to pystac format")
return pystac_items
except ImportError:
print("pystac not available")
return stac_collection
# Example: Integration with stackstac
def prepare_for_stackstac(items, bands=['B04', 'B03', 'B02']):
"""Prepare data for stackstac processing."""
try:
import stackstac
# Get STAC items in proper format
stac_items = [item.to_dict() for item in items]
# Note: URLs need to be properly signed
# The package handles this automatically
print(f"Prepared {len(stac_items)} items for stackstac")
print(f"Bands: {bands}")
return stac_items
except ImportError:
print("stackstac not available")
return None
if __name__ == "__main__":
# Use the functions
stac_items = integrate_with_stac_tools(items)
stackstac_items = prepare_for_stackstac(items)
print(f"STAC items: {stac_items} \nStackSTAC items: {stackstac_items}")
print(f"STAC items: {len(stac_items)} \nStackSTAC items: {len(stackstac_items)}")
print("Integration and preparation complete!")
```
### Custom URL Processing
```python
# Example: Custom URL validation and processing
def process_urls_custom(items, custom_processor=None):
"""Process URLs with custom logic."""
def default_processor(url):
"""Default URL processor."""
# Add custom headers, caching, etc.
return url
processor = custom_processor or default_processor
processed_urls = {}
for item in items:
item_urls = item.get_all_asset_urls()
processed_item_urls = {}
for asset, url in item_urls.items():
processed_url = processor(url)
processed_item_urls[asset] = processed_url
processed_urls[item.id] = processed_item_urls
return processed_urls
# Example custom processor
def add_caching_headers(url):
"""Add caching parameters to URL."""
if '?' in url:
return f"{url}&cache=3600"
else:
return f"{url}?cache=3600"
# Use custom processing
cached_urls = process_urls_custom(items, add_caching_headers)
print(f"Cached URLs: {cached_urls}")
```
## Utils Functions
### Utils Functions - Usage Examples
```python
import open_geodata_api as ogapi
from open_geodata_api.utils import (
filter_by_cloud_cover,
download_datasets,
download_url,
download_from_json,
download_seasonal,
download_single_file,
download_url_dict,
download_items,
download_seasonal_data,
create_download_summary,
is_url_expired,
is_signed_url,
re_sign_url_if_needed
)
# Setup clients
pc = ogapi.planetary_computer(auto_sign=True)
es = ogapi.earth_search()
```
#### Example 1: Complete Workflow - Search and Filter by Cloud Cover
```python
print("š Searching for Sentinel-2 data...")
results = pc.search(
collections=["sentinel-2-l2a"],
bbox=[-122.5, 47.5, -122.0, 48.0], # Seattle area
datetime="2024-06-01/2024-08-31",
limit=20
)
items = results.get_all_items()
print(f"Found {len(items)} items")
# Filter by cloud cover using utils
clear_items = filter_by_cloud_cover(items, max_cloud_cover=15)
print(f"After filtering: {len(clear_items)} clear items (<15% clouds)")
```
#### Example 2: Download Single Asset from Search Results
```python
print("\nš„ Downloading single asset...")
first_item = clear_items[^0]
first_item.print_assets_info()
# Get a single band URL and download
red_url = first_item.get_asset_url('B04') # Red band, auto-signed
downloaded_file = download_single_file(
red_url,
destination="./data/red_band.tif",
provider="planetary_computer"
)
print(f"Downloaded: {downloaded_file}")
```
#### Example 3: Download RGB Bands from Multiple Items
```python
print("\nšØ Downloading RGB bands from multiple items...")
rgb_downloads = download_items(
clear_items[:3], # First 3 clear items
base_destination="./rgb_data/",
asset_keys=['B04', 'B03', 'B02'], # Red, Green, Blue
create_product_folders=True
)
print(f"Downloaded RGB data for {len(rgb_downloads)} items")
```
#### Example 4: Multi-Provider Data Collection and Download
```python
print("\nš Comparing data from multiple providers...")
# Search both providers
search_params = {
'collections': ['sentinel-2-l2a'],
'bbox': [-120.0, 35.0, -119.0, 36.0], # California
'datetime': '2024-07-01/2024-07-31',
'limit': 5
}
pc_results = pc.search(**search_params)
es_results = es.search(**search_params)
pc_items = pc_results.get_all_items()
es_items = es_results.get_all_items()
print(f"PC found: {len(pc_items)} items")
print(f"ES found: {len(es_items)} items")
# Filter both collections
pc_clear = filter_by_cloud_cover(pc_items, max_cloud_cover=20)
es_clear = filter_by_cloud_cover(es_items, max_cloud_cover=20)
# Download from both providers
print("š¦ Downloading from Planetary Computer...")
pc_downloads = download_items(
pc_clear[:2],
base_destination="./pc_data/",
asset_keys=['B08', 'B04'], # NIR, Red for NDVI
)
print("š¦ Downloading from EarthSearch...")
es_downloads = download_items(
es_clear[:2],
base_destination="./es_data/",
asset_keys=['nir', 'red'], # ES naming convention
)
```
#### Example 5: Seasonal Analysis Workflow
```python
print("\nš± Setting up seasonal analysis...")
def collect_seasonal_data(bbox, year):
"""Collect data for seasonal analysis."""
seasons = {
'spring': f'{year}-03-01/{year}-05-31',
'summer': f'{year}-06-01/{year}-08-31',
'fall': f'{year}-09-01/{year}-11-30',
'winter': f'{year}-12-01/{year+1}-02-28'
}
seasonal_data = {}
for season, date_range in seasons.items():
print(f"š Searching {season} {year} data...")
results = pc.search(
collections=['sentinel-2-l2a'],
bbox=bbox,
datetime=date_range,
query={'eo:cloud_cover': {'lt': 25}},
limit=10
)
items = results.get_all_items()
filtered_items = filter_by_cloud_cover(items, max_cloud_cover=20)
# Get URLs for NDVI calculation
urls = filtered_items.get_all_urls(['B08', 'B04']) # NIR, Red
seasonal_data[season] = {
'count': len(filtered_items),
'date_range': date_range,
'urls': urls
}
print(f" Found {len(filtered_items)} clear scenes")
return seasonal_data
# Collect seasonal data
bbox = [-121.0, 38.0, -120.5, 38.5] # Northern California
seasonal_data = collect_seasonal_data(bbox, 2024)
# Download seasonal data using utils
seasonal_downloads = download_seasonal_data(
seasonal_data,
base_destination="./seasonal_analysis/",
seasons=['spring', 'summer'], # Only spring and summer
asset_keys=['B08', 'B04'] # NIR and Red bands
)
```
#### Example 6: URL Management and Re-signing
```python
print("\nš URL management example...")
# Get some URLs from items
item = pc_items[^0] if pc_items else clear_items[^0]
all_urls = item.get_all_asset_urls()
# Check URL status
for asset, url in list(all_urls.items())[:3]:
print(f"\nš Asset: {asset}")
print(f" Signed: {is_signed_url(url)}")
print(f" Expired: {is_url_expired(url)}")
# Re-sign if needed
fresh_url = re_sign_url_if_needed(url, provider="planetary_computer")
if fresh_url != url:
print(f" ā
URL was re-signed")
```
#### Example 7: Batch Processing with URL Dictionary
```python
print("\nš Batch processing workflow...")
# Create a custom URL dictionary from search results
custom_urls = {}
for i, item in enumerate(clear_items[:3]):
item_id = f"sentinel2_{item.id[-8:]}" # Shortened ID
# Get specific bands for analysis
item_urls = item.get_band_urls(['B02', 'B03', 'B04', 'B08'])
custom_urls[item_id] = item_urls
print(f"Created custom URL dictionary with {len(custom_urls)} items")
# Download using URL dictionary
batch_downloads = download_url_dict(
{k: v for k, v in list(custom_urls.items())[^0].items()}, # First item only
base_destination="./batch_data/",
provider="planetary_computer",
create_subfolders=True
)
```
#### Example 8: Export and Import Workflow
```python
print("\nš¾ Export/Import workflow...")
# Export URLs to JSON for later processing
import json
with open('./data_urls.json', 'w') as f:
json.dump(custom_urls, f, indent=2)
print("š¤ URLs exported to data_urls.json")
# Download from JSON file using utils
json_downloads = download_from_json(
'./data_urls.json',
destination="./from_json/",
asset_keys=['B04', 'B08'], # Only specific bands
create_folders=True
)
```
#### Example 9: Download Summary and Reporting
```python
print("\nš Creating download summary...")
# Combine all download results
all_downloads = {
'rgb_downloads': rgb_downloads,
'pc_downloads': pc_downloads,
'es_downloads': es_downloads,
'seasonal_downloads': seasonal_downloads,
'batch_downloads': batch_downloads,
'json_downloads': json_downloads
}
# Create comprehensive summary
summary = create_download_summary(
all_downloads,
output_file="./download_report.json"
)
print(f"š Download Summary:")
print(f" Total files: {summary['total_files']}")
print(f" Successful: {summary['successful_downloads']}")
print(f" Failed: {summary['failed_downloads']}")
print(f" Success rate: {summary['success_rate']}")
```
#### Example 10: Advanced Filtering and Processing
```python
print("\nš¬ Advanced processing workflow...")
# Multi-step filtering
def advanced_processing_workflow(bbox, max_cloud=10):
"""Advanced workflow with multiple filtering steps."""
# Step 1: Search with broader criteria
results = pc.search(
collections=['sentinel-2-l2a'],
bbox=bbox,
datetime='2024-06-01/2024-09-30',
limit=50
)
items = results.get_all_items()
print(f"Step 1: Found {len(items)} total items")
# Step 2: Filter by cloud cover
clear_items = filter_by_cloud_cover(items, max_cloud_cover=max_cloud)
print(f"Step 2: {len(clear_items)} items with <{max_cloud}% clouds")
# Step 3: Convert to DataFrame for advanced filtering
df = clear_items.to_dataframe(include_geometry=False)
# Step 4: Filter by date (summer months only)
summer_mask = df['datetime'].str.contains('2024-0[^678]') # June, July, August
summer_items_ids = df[summer_mask]['id'].tolist()
# Step 5: Get items for summer period
summer_items = [item for item in clear_items if item.id in summer_items_ids]
print(f"Step 3: {len(summer_items)} summer items")
# Step 6: Download analysis-ready data
analysis_downloads = download_items(
summer_items[:5], # Top 5 summer items
base_destination="./analysis_ready/",
asset_keys=['B02', 'B03', 'B04', 'B08', 'B11', 'B12'], # Multi-spectral
create_product_folders=True
)
return analysis_downloads, summer_items
# Run advanced workflow
analysis_results, summer_items = advanced_processing_workflow(
bbox=[-122.0, 37.0, -121.5, 37.5], # San Francisco Bay
max_cloud=5
)
print(f"ā
Analysis-ready data downloaded for {len(analysis_results)} items")
```
#### Example 11: Error Handling and Resilient Downloads
```python
print("\nš”ļø Resilient download example...")
def resilient_download(items, max_retries=3):
"""Download with retry logic and error handling."""
successful_downloads = {}
failed_downloads = {}
for item in items[:2]: # Process first 2 items
item_id = item.id
retries = 0
while retries < max_retries:
try:
# Try to download key bands
downloads = download_items(
[item],
base_destination=f"./resilient_data/attempt_{retries+1}/",
asset_keys=['B04', 'B08'],
create_product_folders=True
)
successful_downloads[item_id] = downloads
print(f"ā
Successfully downloaded {item_id}")
break
except Exception as e:
retries += 1
print(f"ā Attempt {retries} failed for {item_id}: {e}")
if retries >= max_retries:
failed_downloads[item_id] = str(e)
print(f"š Gave up on {item_id} after {max_retries} attempts")
return successful_downloads, failed_downloads
# Run resilient download
successful, failed = resilient_download(clear_items)
print(f"Resilient download completed: {len(successful)} successful, {len(failed)} failed")
print("\nš All utils function examples completed!")
print(f"Check your './data/' directory for downloaded files")
```
## CLI Usage
### Command Line Interface (CLI) Usage
Open Geodata API provides a comprehensive CLI for satellite data discovery, filtering, and downloading. After installation, use the `ogapi` command to access all functionality.
#### Show package information
```bash
ogapi info
```
#### Get help for any command
```bash
ogapi --help
```
```bash
ogapi collections --help
```
```bash
ogapi search items --help
```
### Collections Management
#### List all collections from both providers
```bash
ogapi collections list
```
#### List from specific provider
```bash
ogapi collections list --provider pc
```
```bash
ogapi collections list --provider es
```
#### Filter collections by keyword
```bash
ogapi collections list --filter sentinel
```
#### Save results to file
```bash
ogapi collections list --output collections.json
```
### Search Collections
#### Find collections by keyword
```bash
ogapi collections search sentinel
```
```bash
ogapi collections search landsat --provider pc
```
```bash
ogapi collections search modis --provider both
```
### Get Collection Information
#### Get detailed collection info
```bash
ogapi collections info sentinel-2-l2a
```
```bash
ogapi collections info sentinel-2-l2a --provider es
```
```bash
ogapi collections info landsat-c2-l2 --output collection_info.json
```
### Data Search
#### Search for Sentinel-2 data in Seattle area
```bash
ogapi search items --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --datetime "2024-06-01/2024-08-31" --limit 10
```
#### Search with cloud cover filter
```bash
ogapi search items --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --cloud-cover 20 --output search_results.json
```
#### Search multiple collections
```bash
ogapi search items --collections "sentinel-2-l2a,landsat-c2-l2" --bbox "-120.0,35.0,-119.0,36.0" --datetime "2024-01-01/2024-12-31"
```
#### Advanced Search with JSON Query
```bash
ogapi search items --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --query '{"eo:cloud_cover":{"lt":15},"platform":{"eq":"sentinel-2a"}}' --output advanced_search.json
```
#### Find recent clear data (last 30 days)
```bash
ogapi search quick sentinel-2-l2a "-122.5,47.5,-122.0,48.0"
```
#### Customize time range and cloud threshold
```bash
ogapi search quick sentinel-2-l2a "-122.5,47.5,-122.0,48.0" --days 7 --cloud-cover 10 --limit 5
```
#### Save results for later processing
```bash
ogapi search quick landsat-c2-l2 "-120.0,35.0,-119.0,36.0" --output recent_landsat.json
```
### Compare Data Availability Between Providers
#### Compare Sentinel-2 availability
```bash
ogapi search compare --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --datetime "2024-06-01/2024-08-31"
```
#### Compare with cloud filtering
```bash
ogapi search compare --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --cloud-cover 25 --output comparison_results.json
```
### Working with Items
#### Show info for first item in search results
```bash
ogapi items info search_results.json
```
#### Show info for specific item by index
```bash
ogapi items info search_results.json --item-index 2
```
#### Show detailed metadata
```bash
ogapi items info search_results.json --show-all --output item_details.json
```
#### List all assets for an item
```bash
ogapi items assets search_results.json
```
#### Filter assets by pattern
```bash
ogapi items assets search_results.json --type "image/tiff"
```
#### Show URLs for assets
```bash
ogapi items assets search_results.json --show-urls
```
#### Get URLs for RGB bands
```bash
ogapi items urls search_results.json --assets "B04,B03,B02"
```
#### Get all asset URLs
```bash
ogapi items urls search_results.json --output all_urls.json
```
#### Get URLs by pattern
```bash
ogapi items urls search_results.json --pattern "B0" --output optical_bands.json
```
#### Get unsigned URLs
```bash
ogapi items urls search_results.json --unsigned
```
### Compare Items by Quality
#### Compare items by cloud cover (find clearest)
```bash
ogapi items compare search_results.json
```
#### Compare by date (find most recent)
```bash
ogapi items compare search_results.json --metric date
```
#### Compare asset availability
```bash
ogapi items compare search_results.json --metric assets --max-items 10
```
### Data Download
#### Download all assets from search results
```bash
ogapi download search-results search_results.json
```
#### Download specific bands only
```bash
ogapi download search-results search_results.json --assets "B04,B03,B02" --destination "./rgb_data/"
```
#### Download with additional filtering
```bash
ogapi download search-results search_results.json --cloud-cover 15 --max-items 5 --assets "B08,B04"
```
#### Create flat file structure
```bash
ogapi download search-results search_results.json --flat-structure --destination "./satellite_data/
```
#### Download single file
```bash
ogapi download url "https://example.com/sentinel2_B04.tif"
```
#### Download to specific location
```bash
ogapi download url "https://example.com/B04.tif" --destination "./data/red_band.tif"
```
#### Download with provider specification (it will handdle url validation)
```bash
ogapi download url "https://pc.example.com/B04.tif" --provider pc
```
#### Download from exported URLs
```bash
ogapi download urls-json exported_urls.json
```
#### Custom destination
```bash
ogapi download urls-json urls.json --destination "./downloads/" --flat-structure
```
#### Download all seasons from seasonal JSON
```bash
ogapi download seasonal seasonal_data.json
```
#### Download specific seasons and assets
```bash
ogapi download seasonal seasonal_data.json --seasons "spring,summer" --assets "B08,B04" --destination "./time_series/"
```
### Filter by Cloud Cover
#### Filter search results by cloud cover
```bash
ogapi utils filter-clouds search_results.json --max-cloud-cover 20
```
#### Filter and save results
```bash
ogapi utils filter-clouds search_results.json --max-cloud-cover 15 --output clear_results.json --show-stats
```
### Export URLs
#### Export all URLs from search results
```bash
ogapi utils export-urls search_results.json --output all_urls.json
```
#### Export specific assets
```bash
ogapi utils export-urls search_results.json --output rgb_urls.json --assets "B04,B03,B02"
```
#### Export in simple format
```bash
ogapi utils export-urls search_results.json --output simple_urls.json --format simple
```
#### Export unsigned URLs
```bash
ogapi utils export-urls search_results.json --output unsigned_urls.json --unsigned
```
### Validate URLs
#### Basic URL validation
```bash
ogapi utils validate-urls urls.json
```
#### Check accessibility (HTTP requests)
```bash
ogapi utils validate-urls urls.json --check-access
```
#### Fix expired URLs
```bash
ogapi utils validate-urls urls.json --fix-expired --output fixed_urls.json
```
#### Skip expiry check for speed
```bash
ogapi utils validate-urls urls.json --no-check-expiry
```
### Analyze Search Results
#### Analyze cloud cover distribution
```bash
ogapi utils analyze search_results.json
```
#### Temporal analysis
```bash
ogapi utils analyze search_results.json --metric temporal
```
#### Asset availability analysis
```bash
ogapi utils analyze search_results.json --metric assets --output analysis_report.json
```
### Create Download Summaries
#### Create detailed download summary
```bash
ogapi utils download-summary download_results.json
```
#### Brief summary
```bash
ogapi utils download-summary results.json --format brief
```
#### Save summary report
```bash
ogapi utils download-summary results.json --output report.json
```
### Complete Workflow Examples
#### 1.1: Search for data (Basic RGB Download Workflow)
```bash
ogapi search items --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --datetime "2024-06-01/2024-08-31" --cloud-cover 20 --output search_results.json
```
#### 1.2: Filter for very clear imagery
```bash
ogapi utils filter-clouds search_results.json --max-cloud-cover 10 --output clear_results.json
```
#### 1.3: Download RGB bands
```bash
ogapi download search-results clear_results.json --assets "B04,B03,B02" --destination "./rgb_analysis/"
```
#### 1.4: Create summary
```bash
ogapi utils download-summary download_results.json
```
#### 2.1: Search for data (NDVI Analysis Workflow)
```bash
ogapi search items --collections sentinel-2-l2a --bbox "-120.0,35.0,-119.0,36.0" --datetime "2024-01-01/2024-12-31" --cloud-cover 25 --output yearly_search.json
```
#### 2.2: Filter by seasons and export URLs
```bash
ogapi utils filter-clouds yearly_search.json --max-cloud-cover 15 --output clear_yearly.json
```
```bash
ogapi utils export-urls clear_yearly.json --assets "B08,B04" --output ndvi_urls.json
```
#### 2.3: Download NDVI bands
```bash
ogapi download urls-json ndvi_urls.json --destination "./ndvi_analysis/"
```
#### 3.1 Compare data availability (Multi-Provider Comparison)
```bash
ogapi search compare --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --datetime "2024-06-01/2024-08-31" --output comparison.json
```
#### 3.2 Search both providers separately
```bash
ogapi search items --provider pc --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --cloud-cover 20 --output pc_results.json
```
```bash
ogapi search items --provider es --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --cloud-cover 20 --output es_results.json
```
#### 3.3 Download from best provider
```bash
ogapi download search-results pc_results.json --max-items 3 --destination "./pc_data/"
```
#### 4.1 Search and analyze (Quality Assessment Workflow)
```bash
ogapi search items --collections sentinel-2-l2a --bbox "-122.5,47.5,-122.0,48.0" --limit 50 --output large_search.json
```
#### 4.2 Analyze data quality
```bash
ogapi utils analyze large_search.json --metric cloud_cover
```
```bash
ogapi utils analyze large_search.json --metric temporal
```
```bash
ogapi utils analyze large_search.json --metric assets
```
#### 4.3 Compare individual items
```bash
ogapi items compare large_search.json --metric cloud_cover
```
#### 4.4 Download best items only
```bash
ogapi utils filter-clouds large_search.json --max-cloud-cover 10 --output best_quality.json
```
```bash
ogapi download search-results best_quality.json --max-items 5
```
### Global Options
**All commands support these global options:**
#### Enable verbose output for debugging
```bash
ogapi --verbose [command]
```
#### Show version
```bash
ogapi --version
```
#### Get help for any command
```bash
ogapi [command] --help
```
```bash
ogapi [command] [subcommand] --help
```
### Tips and Best Practices
1. **Start Small**: Use `--limit` and `--max-items` to test workflows before large downloads
2. **Save Results**: Always use `--output` to save search results for reprocessing
3. **Filter Early**: Use cloud cover filters to reduce data volume
4. **Check Status**: Use validation commands before large downloads
5. **Resume Downloads**: Most download commands support resuming interrupted transfers
6. **Use Verbose Mode**: Add `--verbose` for detailed debugging information
### Environment Variables
#### Optional: Set default provider
```bash
export OGAPI_DEFAULT_PROVIDER=pc
```
#### Optional: Set default destination
```bash
export OGAPI_DEFAULT_DEST=./satellite_data/
```
## FAQ
### General Questions
**Q: What makes this package different from using APIs directly?**
A: Key advantages:
- Unified interface across multiple APIs
- Automatic URL signing/validation
- Consistent error handling
- No lock-in to specific data reading packages
- Built-in best practices
**Q: Can I use this with my existing geospatial workflow?**
A: Absolutely! The package provides URLs that work with any raster reading library:
```python
url = item.get_asset_url('red')
# Use with your existing tools
import rioxarray; data = rioxarray.open_rasterio(url)
import rasterio; data = rasterio.open(url)
from osgeo import gdal; data = gdal.Open(url)
```
**Q: Do I need API keys?**
A: Only for Planetary Computer. EarthSearch is completely open.
### Technical Questions
**Q: How does automatic URL signing work?**
A: When `auto_sign=True`, the package:
1. Detects the provider (PC vs ES)
2. For PC: Uses the planetary-computer package to sign URLs
3. For ES: Returns URLs as-is (no signing needed)
4. You can override with `signed=False/True`
**Q: What about rate limiting?**
A: Both APIs have rate limits:
- **Planetary Computer**: Generous limits for signed URLs
- **EarthSearch**: Standard HTTP rate limits
The package doesn't implement rate limiting - use your own if needed.
**Q: Can I cache results?**
A: Yes, several approaches:
```python
# 1. Export URLs to JSON
items.export_urls_json('cache.json')
# 2. Save DataFrames
df = items.to_dataframe()
df.to_parquet('metadata_cache.parquet')
# 3. Use your own caching layer
```
**Q: How do I handle different projections?**
A: The package provides URLs - projection handling is up to your raster library:
```python
import rioxarray
data = rioxarray.open_rasterio(url)
data_reprojected = data.rio.reproject('EPSG:4326')
```
### Troubleshooting Questions
**Q: Why am I getting "Asset not found" errors?**
A: Different providers use different asset names:
- **PC**: B01, B02, B03, B04...
- **EarthSearch**: coastal, blue, green, red...
Use `item.print_assets_info()` to see available assets.
**Q: Search returns no results but data should exist**
A: Common issues:
1. **Bbox order**: Use [west, south, east, north]
2. **Date format**: PC accepts "YYYY-MM-DD", ES prefers RFC3339
3. **Collection names**: Use `client.list_collections()` to verify
4. **Cloud cover**: Try relaxing the threshold
**Q: URLs work but data loading is slow**
A: Optimization strategies:
1. Use overview levels: `rioxarray.open_rasterio(url, overview_level=2)`
2. Enable chunking: `rioxarray.open_rasterio(url, chunks=True)`
3. Read smaller windows with rasterio
4. Consider geographic proximity to data
### Integration Questions
**Q: Can I use this with Jupyter notebooks?**
A: Yes! The package works great in Jupyter:
```python
# Display asset info
item.print_assets_info()
# Show DataFrames
df = items.to_dataframe()
display(df)
# Plot with matplotlib/cartopy
import matplotlib.pyplot as plt
data = rioxarray.open_rasterio(url)
data.plot()
```
**Q: How do I integrate with QGIS/ArcGIS?**
A: Export URLs and use them directly:
```python
# Get URLs
urls = item.get_all_asset_urls()
# In QGIS: Add Raster Layer -> use the URL directly
# In ArcGIS: Add Data -> Raster Dataset -> paste URL
```
**Q: Can I use this in production systems?**
A: Yes! The package is designed for production use:
- Robust error handling
- No forced dependencies
- Clean separation of concerns
- Comprehensive logging support
**Q: How do I contribute or report issues?**
A: Visit the GitHub repository:
- Report issues: GitHub Issues
- Contribute: Pull Requests welcome
- Documentation: Help improve this guide
---
This completes the comprehensive user guide for Open Geodata API. The package provides a clean, flexible foundation for accessing open geospatial data while letting you maintain full control over data processing and analysis workflows.
--- End ---
[^1]: https://developers.arcgis.com/python/latest/guide/tutorials/import-data/
[^2]: https://github.com/geopython/pygeoapi/blob/master/pygeoapi/openapi.py
[^3]: https://opencagedata.com/api
[^4]: https://opencagedata.com/tutorials/geocode-in-python
[^5]: https://guides.library.columbia.edu/geotools/Python
[^6]: https://pygeoapi.io
[^7]: https://live.osgeo.org/en/quickstart/pygeoapi_quickstart.html
[^8]: https://packaging.python.org
[^9]: [http://r-project.ro/conference2018/presentations/Tutorial_Spatial_Analysis_in_R_with_Open_Geodata_-_uRos2018.pdf](https://github.com/Mirjan-Ali-Sha/open-geodata-api/blob/main/Open%20Geodata%20API%20-%20Complete%20User%20Guide.pdf)
[^10]: https://geodata.readthedocs.io
Raw data
{
"_id": null,
"home_page": null,
"name": "open-geodata-api",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "api-client, cli, earth-observation, earthsearch, geodata, geospatial, open-data, planetary-computer, remote-sensing, satellite, stac",
"author": null,
"author_email": "Mirjan Ali Sha <mastools.help@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/88/4a/f6e353404bb9fc0cd12fdaf5bb4bc2efaf70c35820a30462f699d7e562eb/open_geodata_api-0.3.0.tar.gz",
"platform": null,
"description": "<p align=\"center\">\n <img src=\"https://github.com/Mirjan-Ali-Sha/open-geodata-api/blob/main/icon.png\" alt=\"Open Geodata API Icon\" width=\"150\" height=\"150\" />\n</p>\n\n# Open Geodata API - Complete User Guide\n\n## Table of Contents\n\n1. [Introduction](README.md#introduction)\n2. [Open Geodata API - Complete Tool Summary](README.md#open-geodata-api---complete-tool-summary)\n3. [Installation](README.md#installation)\n4. [Quick Start](README.md#quick-start)\n5. [Core Concepts](README.md#core-concepts)\n6. [API Reference](README.md#api-reference)\n7. [Usage Examples](README.md#usage-examples)\n8. [Best Practices](README.md#best-practices)\n9. [Troubleshooting](README.md#troubleshooting)\n10. [Advanced Usage](README.md#advanced-usage)\n11. [Utility Functions](README.md#utils-functions)\n12. [CLI Usage](README.md#cli-usage)\n13. [FAQ](README.md#faq)\n\n## Introduction\n\n### What is Open Geodata API?\n\n**Open Geodata API** is a unified Python client library that provides seamless access to multiple open geospatial data APIs. It focuses on **API access, search, and URL management** while maintaining maximum flexibility for data reading and processing.\n\n## Open Geodata API - Complete Tool Summary\n\n**\ud83d\udef0\ufe0f Unified Python Client for Satellite Data Access**\n\n#### **What It Does**\n\n\u2705 **One Interface** - Access Microsoft Planetary Computer \\& AWS EarthSearch APIs<br>\n\u2705 **Smart URLs** - Automatic signing, validation, and expiration handling<br>\n\u2705 **Your Choice** - Use any raster package (rioxarray, rasterio, GDAL)<br>\n\u2705 **Complete Workflow** - Search \u2192 Filter \u2192 Download \u2192 Analyze\n\n#### **Key Capabilities**\n\n\u2705 **Python API** - Programmatic access with full flexibility<br>\n\u2705 **Command Line** - `ogapi` CLI for all operations with help<br>\n\u2705 **Smart Downloads** - Progress tracking, resume, batch processing<br>\n\u2705 **Data Filtering** - Cloud cover, temporal, quality-based filtering<br>\n\u2705 **URL Management** - Export, validate, and refresh URLs automatically\n\n\n### Supported APIs\n\n| API | Provider | Authentication | URL Handling |\n| :-- | :-- | :-- | :-- |\n| **Planetary Computer** | Microsoft | API Key + Signing | Automatic signing |\n| **EarthSearch** | Element84/AWS | None required | URL validation |\n\n### Philosophy\n\n\ud83c\udfaf **Core Focus**: We provide URLs - you choose how to read them!<br>\n\ud83d\udce6 **Use Any Package**: rioxarray, rasterio, GDAL, or any package you prefer<br>\n\ud83d\ude80 **Maximum Flexibility**: Zero restrictions on your workflow\n\n## Installation\n\n### Basic Installation\n\n```bash\n# Install core package\npip install open-geodata-api\n```\n\n\n### Optional Dependencies\n\n```bash\n# For spatial analysis (shapely, geopandas)\npip install open-geodata-api[spatial]\n\n# For raster reading suggestions (rioxarray,rasterio, xarray)\npip install open-geodata-api[io] # rioxarray + xarray\n\n# For complete examples (shapely, geopandas, rioxarray, rasterio, xarray)\npip install open-geodata-api[complete]\n\n# Development dependencies\npip install open-geodata-api[dev]\n```\n\n\n### Verify Installation\n\n```python\nimport open_geodata_api as ogapi\nogapi.info()\n```\n\n\n## Quick Start\n\n### 30-Second Example\n\n```python\nimport open_geodata_api as ogapi\n\n# Get clients for both APIs\nclients = ogapi.get_clients(pc_auto_sign=True)\npc = clients['planetary_computer']\nes = clients['earth_search']\n\n# Search for Sentinel-2 data\nresults = pc.search(\n collections=[\"sentinel-2-l2a\"],\n bbox=[-122.5, 47.5, -122.0, 48.0],\n datetime=\"2024-01-01/2024-03-31\"\n)\n\n# Get items and URLs\nitems = results.get_all_items()\nitem = items[0]\n\n# Get ready-to-use URLs\nblue_url = item.get_asset_url('B02') # Automatically signed!\nall_urls = item.get_all_asset_urls() # All assets\n\n# Use with ANY raster package\nimport rioxarray\ndata = rioxarray.open_rasterio(blue_url)\n\n# Or use with rasterio\nimport rasterio\nwith rasterio.open(blue_url) as src:\n data = src.read(1)\n```\n\n\n### 5-Minute Tutorial\n\n```python\n# 1. Import and setup\nimport open_geodata_api as ogapi\n\n# 2. Create clients\npc = ogapi.planetary_computer(auto_sign=True)\nes = ogapi.earth_search()\n\n# 3. Search for data\nsearch_params = {\n 'collections': ['sentinel-2-l2a'],\n 'bbox': [-122.5, 47.5, -122.0, 48.0],\n 'datetime': '2024-01-01/2024-03-31',\n 'query': {'eo:cloud_cover': {'lt': 30}}\n}\n\npc_results = pc.search(**search_params, limit=10)\nes_results = es.search(**search_params, limit=10)\n\n# 4. Work with results\npc_items = pc_results.get_all_items()\nes_items = es_results.get_all_items()\n\nprint(f\"Found: PC={len(pc_items)}, ES={len(es_items)} items\")\n\n# 5. Get URLs and use with your preferred package\nitem = pc_items[0]\nitem.print_assets_info()\n\n# Get specific bands\nrgb_urls = item.get_band_urls(['B04', 'B03', 'B02']) # Red, Green, Blue\nprint(f\"RGB URLs: {rgb_urls}\")\n\n# Use URLs with any package you want!\n```\n\n\n## Core Concepts\n\n### STAC (SpatioTemporal Asset Catalog)\n\nOpen Geodata API works with STAC-compliant APIs. Key STAC concepts:\n\n- **Collections**: Groups of related datasets (e.g., \"sentinel-2-l2a\")\n- **Items**: Individual products/scenes with metadata\n- **Assets**: Individual files (bands, thumbnails, metadata)\n\n\n### Package Architecture\n\n```\nopen-geodata-api/\n\u251c\u2500\u2500 Core Classes (Universal)\n\u2502 \u251c\u2500\u2500 STACItem # Individual products\n\u2502 \u251c\u2500\u2500 STACItemCollection # Groups of products \n\u2502 \u251c\u2500\u2500 STACAsset # Individual files\n\u2502 \u2514\u2500\u2500 STACSearch # Search results\n\u251c\u2500\u2500 API Clients\n\u2502 \u251c\u2500\u2500 PlanetaryComputerCollections\n\u2502 \u2514\u2500\u2500 EarthSearchCollections\n\u2514\u2500\u2500 Utilities\n \u251c\u2500\u2500 URL signing (PC)\n \u251c\u2500\u2500 URL validation (ES)\n \u2514\u2500\u2500 Filtering functions\n```\n\n\n### Provider-Specific Handling\n\n| Feature | Planetary Computer | EarthSearch |\n| :-- | :-- | :-- |\n| **Authentication** | Automatic via planetary-computer package | None required |\n| **URL Signing** | Automatic (auto_sign=True) | Not applicable |\n| **Asset Naming** | B01, B02, B03... | coastal, blue, green... |\n| **Cloud Cover** | eo:cloud_cover | eo:cloud_cover |\n\n## API Reference\n\n### Factory Functions\n\n#### `planetary_computer(auto_sign=False)`\n\nCreates a Planetary Computer client.\n\n**Parameters:**\n\n- `auto_sign` (bool): Automatically sign URLs for immediate use\n\n**Returns:** `PlanetaryComputerCollections` instance\n\n#### `earth_search(auto_validate=False)`\n\nCreates an EarthSearch client.\n\n**Parameters:**\n\n- `auto_validate` (bool): Validate URLs (currently placeholder)\n\n**Returns:** `EarthSearchCollections` instance\n\n#### `get_clients(pc_auto_sign=False, es_auto_validate=False)`\n\nCreates both clients simultaneously.\n\n**Returns:** Dictionary with 'planetary_computer' and 'earth_search' keys\n\n### Client Methods\n\n#### `search(collections, bbox=None, datetime=None, query=None, limit=100)`\n\nSearch for STAC items.\n\n**Parameters:**\n\n- `collections` (list): Collection IDs to search\n- `bbox` (list): Bounding box [west, south, east, north]\n- `datetime` (str): Date range \"YYYY-MM-DD/YYYY-MM-DD\"\n- `query` (dict): Additional filters like `{\"eo:cloud_cover\": {\"lt\": 30}}`\n- `limit` (int): Maximum results to return\n\n**Returns:** `STACSearch` instance\n\n#### `list_collections()`\n\nGet list of available collection names.\n\n**Returns:** List of collection ID strings\n\n#### `get_collection_info(collection_name)`\n\nGet detailed information about a specific collection.\n\n**Returns:** Collection metadata dictionary\n\n### STACItem Methods\n\n#### `get_asset_url(asset_key, signed=None)`\n\nGet ready-to-use URL for a specific asset.\n\n**Parameters:**\n\n- `asset_key` (str): Asset name (e.g., 'B02', 'blue', 'red')\n- `signed` (bool): Override automatic signing behavior\n\n**Returns:** URL string ready for any raster package\n\n#### `get_all_asset_urls(signed=None)`\n\nGet URLs for all available assets.\n\n**Returns:** Dictionary `{asset_key: url}`\n\n#### `get_band_urls(bands, signed=None)`\n\nGet URLs for specific bands/assets.\n\n**Parameters:**\n\n- `bands` (list): List of asset names\n\n**Returns:** Dictionary `{asset_key: url}`\n\n#### `list_assets()`\n\nGet list of available asset names.\n\n**Returns:** List of asset key strings\n\n#### `print_assets_info()`\n\nPrint detailed information about all assets.\n\n### STACItemCollection Methods\n\n#### `get_all_urls(asset_keys=None, signed=None)`\n\nGet URLs from all items in the collection.\n\n**Parameters:**\n\n- `asset_keys` (list, optional): Specific assets to get URLs for\n- `signed` (bool, optional): Override signing behavior\n\n**Returns:** Dictionary `{item_id: {asset_key: url}}`\n\n#### `to_dataframe(include_geometry=True)`\n\nConvert collection to pandas/geopandas DataFrame.\n\n**Parameters:**\n\n- `include_geometry` (bool): Include spatial geometry (requires geopandas)\n\n**Returns:** DataFrame with item metadata\n\n#### `export_urls_json(filename, asset_keys=None)`\n\nExport all URLs to JSON file for external processing.\n\n## Usage Examples\n\n### Example 1: Simple Data Discovery\n\n```python\nimport open_geodata_api as ogapi\n\n# Setup\npc = ogapi.planetary_computer(auto_sign=True)\n\n# Find available collections\ncollections = pc.list_collections()\nsentinel_collections = [c for c in collections if 'sentinel' in c.lower()]\nprint(f\"Sentinel collections: {sentinel_collections}\")\n\n# Get collection details\ns2_info = pc.get_collection_info('sentinel-2-l2a')\nprint(f\"Sentinel-2 L2A: {s2_info['title']}\")\nprint(f\"Description: {s2_info['description'][:100]}...\")\n```\n\n\n### Example 2: Geographic Search\n\n```python\n# Search around San Francisco Bay Area\nbbox = [-122.5, 37.5, -122.0, 38.0]\n\nresults = pc.search(\n collections=['sentinel-2-l2a'],\n bbox=bbox,\n datetime='2024-06-01/2024-08-31',\n query={'eo:cloud_cover': {'lt': 20}}, # Less than 20% clouds\n limit=20\n)\n\nitems = results.get_all_items()\nprint(f\"Found {len(items)} items with <20% cloud cover\")\n\n# Convert to DataFrame for analysis\ndf = items.to_dataframe()\nprint(f\"Date range: {df['datetime'].min()} to {df['datetime'].max()}\")\nprint(f\"Cloud cover range: {df['eo:cloud_cover'].min():.1f}% to {df['eo:cloud_cover'].max():.1f}%\")\n```\n\n\n### Example 3: Multi-Provider Comparison\n\n```python\n# Compare results from both providers\nbbox = [-122.2, 47.6, -122.1, 47.7] # Seattle area\n\npc_results = pc.search(\n collections=['sentinel-2-l2a'],\n bbox=bbox,\n datetime='2024-01-01/2024-03-31'\n)\n\nes_results = es.search(\n collections=['sentinel-2-l2a'], \n bbox=bbox,\n datetime='2024-01-01T00:00:00Z/2024-03-31T23:59:59Z'\n)\n\npc_items = pc_results.get_all_items()\nes_items = es_results.get_all_items()\n\nprint(f\"Planetary Computer: {len(pc_items)} items\")\nprint(f\"EarthSearch: {len(es_items)} items\")\n\n# Compare asset availability\nif pc_items and es_items:\n pc_assets = pc_items[0].list_assets()\n es_assets = es_items[0].list_assets()\n \n print(f\"PC assets: {pc_assets[:5]}\")\n print(f\"ES assets: {es_assets[:5]}\")\n```\n\n\n### Example 4: URL Export for External Processing\n\n```python\n# Get URLs for specific bands across multiple items\nitems = pc_results.get_all_items()\n\n# Export RGB band URLs\nrgb_urls = items.get_all_urls(['B04', 'B03', 'B02']) # Red, Green, Blue\n\n# Save to JSON for external processing\nitems.export_urls_json('sentinel2_rgb_urls.json', ['B04', 'B03', 'B02'])\n\n# Use the URLs with any package\nfirst_item_urls = rgb_urls[list(rgb_urls.keys())[0]]\nprint(f\"Red band URL: {first_item_urls['B04']}\")\n\n# Example with different raster packages\nimport rioxarray\nimport rasterio\nfrom osgeo import gdal\n\nred_url = first_item_urls['B04']\n\n# Option 1: rioxarray\nred_data_xr = rioxarray.open_rasterio(red_url)\n\n# Option 2: rasterio\nwith rasterio.open(red_url) as src:\n red_data_rio = src.read(1)\n\n# Option 3: GDAL\nred_ds = gdal.Open(red_url)\nred_data_gdal = red_ds.ReadAsArray()\n\nprint(f\"Data shapes - XR: {red_data_xr.shape}, RIO: {red_data_rio.shape}, GDAL: {red_data_gdal.shape}\")\n```\n\n\n### Example 5: Batch Processing Setup\n\n```python\n# Setup for batch processing\nimport json\n\n# Search for monthly data\nresults = pc.search(\n collections=['sentinel-2-l2a'],\n bbox=[-120.0, 35.0, -119.0, 36.0],\n datetime='2024-01-01/2024-12-31',\n query={'eo:cloud_cover': {'lt': 15}},\n limit=100\n)\n\nitems = results.get_all_items()\nprint(f\"Found {len(items)} low-cloud scenes\")\n\n# Group by month\ndf = items.to_dataframe()\ndf['month'] = df['datetime'].str[:7] # YYYY-MM\nmonthly_counts = df.groupby('month').size()\nprint(\"Monthly data availability:\")\nprint(monthly_counts)\n\n# Export all URLs for batch processing\nall_urls = items.get_all_urls(['B04', 'B03', 'B02', 'B08']) # RGB + NIR\n\n# Save configuration for external processing\nconfig = {\n 'search_params': {\n 'bbox': [-120.0, 35.0, -119.0, 36.0],\n 'datetime': '2024-01-01/2024-12-31',\n 'collections': ['sentinel-2-l2a']\n },\n 'items_found': len(items),\n 'urls': all_urls\n}\n\nwith open('batch_processing_config.json', 'w') as f:\n json.dump(config, f, indent=2)\n\nprint(\"Batch processing configuration saved!\")\n```\n\n\n### Example 6: EarthSearch Specific Features\n\n```python\n# EarthSearch uses different asset names\nes = ogapi.earth_search()\n\nes_results = es.search(\n collections=['sentinel-2-l2a'],\n bbox=[-122.5, 47.5, -122.0, 48.0],\n datetime='2024-06-01T00:00:00Z/2024-08-31T23:59:59Z',\n limit=5\n)\n\nes_items = es_results.get_all_items()\nitem = es_items[0]\n\n# EarthSearch asset names\nitem.print_assets_info()\n\n# Get URLs using EarthSearch naming\nrgb_urls = item.get_band_urls(['red', 'green', 'blue'])\nnir_url = item.get_asset_url('nir')\n\nprint(f\"RGB URLs: {list(rgb_urls.keys())}\")\nprint(f\"NIR URL ready: {nir_url[:50]}...\")\n\n# All URLs (no signing needed for EarthSearch)\nall_urls = item.get_all_asset_urls()\nprint(f\"Total assets available: {len(all_urls)}\")\n```\n\n\n## Best Practices\n\n### 1. Client Configuration\n\n```python\n# Recommended setup\nimport open_geodata_api as ogapi\n\n# Auto-sign PC URLs for immediate use\npc = ogapi.planetary_computer(auto_sign=True)\nes = ogapi.earth_search()\n\n# Or get both at once\nclients = ogapi.get_clients(pc_auto_sign=True)\n```\n\n\n### 2. Search Strategy\n\n```python\n# Start with broad search, then refine\nresults = pc.search(\n collections=['sentinel-2-l2a'],\n bbox=your_bbox,\n datetime='2024-01-01/2024-12-31',\n query={'eo:cloud_cover': {'lt': 50}}, # Start broad\n limit=100\n)\n\n# Filter further based on your needs\ndf = results.get_all_items().to_dataframe()\nfiltered_df = df[df['eo:cloud_cover'] < 20] # Refine cloud cover\n```\n\n\n### 3. URL Management\n\n```python\n# Let the package handle URL signing automatically\nitem = items[0]\n\n# This automatically handles signing based on provider\nblue_url = item.get_asset_url('B02') # PC: signed, ES: validated\n\n# Override if needed\nunsigned_url = item.get_asset_url('B02', signed=False)\n```\n\n\n### 4. Asset Name Handling\n\n```python\n# Handle different naming conventions gracefully\ndef get_rgb_urls(item):\n \"\"\"Get RGB URLs regardless of provider naming.\"\"\"\n assets = item.list_assets()\n \n # Try Planetary Computer naming\n if all(band in assets for band in ['B04', 'B03', 'B02']):\n return item.get_band_urls(['B04', 'B03', 'B02'])\n \n # Try EarthSearch naming \n elif all(band in assets for band in ['red', 'green', 'blue']):\n return item.get_band_urls(['red', 'green', 'blue'])\n \n else:\n print(f\"Available assets: {assets}\")\n return {}\n\n# Use the function\nrgb_urls = get_rgb_urls(item)\n```\n\n\n### 5. Error Handling\n\n```python\n# Robust search with error handling\ndef safe_search(client, **kwargs):\n \"\"\"Search with comprehensive error handling.\"\"\"\n try:\n results = client.search(**kwargs)\n items = results.get_all_items()\n \n if len(items) == 0:\n print(\"No items found. Try adjusting search parameters.\")\n return None\n \n print(f\"Found {len(items)} items\")\n return items\n \n except Exception as e:\n print(f\"Search failed: {e}\")\n return None\n\n# Use robust search\nitems = safe_search(\n pc,\n collections=['sentinel-2-l2a'],\n bbox=your_bbox,\n datetime='2024-01-01/2024-03-31'\n)\n```\n\n\n### 6. Memory Management\n\n```python\n# For large datasets, process in batches\ndef process_in_batches(items, batch_size=10):\n \"\"\"Process items in batches to manage memory.\"\"\"\n for i in range(0, len(items), batch_size):\n batch = items[i:i+batch_size]\n \n # Get URLs for this batch\n batch_urls = {}\n for item in batch:\n try:\n batch_urls[item.id] = item.get_band_urls(['B04', 'B03', 'B02'])\n except Exception as e:\n print(f\"Failed to get URLs for {item.id}: {e}\")\n \n # Process batch_urls as needed\n yield batch_urls\n\n# Use batch processing\nfor batch_urls in process_in_batches(items):\n print(f\"Processing batch with {len(batch_urls)} items\")\n # Your processing logic here\n```\n\n\n## Troubleshooting\n\n### Common Issues and Solutions\n\n#### Issue: \"planetary-computer package not found\"\n\n**Problem:** PC URL signing fails\n\n```python\n# Error: planetary-computer package not found, returning unsigned URL\n```\n\n**Solution:**\n\n```bash\npip install planetary-computer\n```\n\n\n#### Issue: No items found\n\n**Problem:** Search returns empty results\n\n**Solutions:**\n\n```python\n# 1. Check collection names\navailable_collections = pc.list_collections()\nprint(\"Available collections:\", available_collections)\n\n# 2. Expand search area\nbbox = [-123.0, 47.0, -121.0, 48.0] # Larger area\n\n# 3. Expand date range\ndatetime = '2023-01-01/2024-12-31' # Larger time window\n\n# 4. Relax cloud cover\nquery = {'eo:cloud_cover': {'lt': 80}} # More permissive\n```\n\n\n#### Issue: Asset not found\n\n**Problem:** `KeyError: Asset 'B02' not found`\n\n**Solutions:**\n\n```python\n# 1. Check available assets\nitem.print_assets_info()\n\n# 2. Use correct naming for provider\n# PC: B01, B02, B03...\n# ES: coastal, blue, green...\n\n# 3. Handle gracefully\ntry:\n url = item.get_asset_url('B02')\nexcept KeyError:\n # Try alternative naming\n url = item.get_asset_url('blue')\n```\n\n\n#### Issue: EarthSearch datetime format\n\n**Problem:** EarthSearch requires RFC3339 format\n\n**Solution:**\n\n```python\n# Use proper format for EarthSearch\ndatetime_es = '2024-01-01T00:00:00Z/2024-03-31T23:59:59Z'\n\n# Package handles this automatically in most cases\n```\n\n\n#### Issue: Large data downloads\n\n**Problem:** Memory issues with large datasets\n\n**Solutions:**\n\n```python\n# 1. Use overview levels (if your raster package supports it)\nimport rioxarray\ndata = rioxarray.open_rasterio(url, overview_level=2)\n\n# 2. Use chunking\ndata = rioxarray.open_rasterio(url, chunks={'x': 512, 'y': 512})\n\n# 3. Read windows\nimport rasterio\nwith rasterio.open(url) as src:\n window = rasterio.windows.Window(0, 0, 1024, 1024)\n data = src.read(1, window=window)\n```\n\n\n### Debug Mode\n\n```python\n# Enable debug information\nimport logging\nlogging.basicConfig(level=logging.DEBUG)\n\n# Check what URLs are being generated\nitem = items[0]\nprint(f\"Item ID: {item.id}\")\nprint(f\"Provider: {item.provider}\")\n\nall_urls = item.get_all_asset_urls()\nfor asset, url in all_urls.items():\n print(f\"{asset}: {url[:50]}...\")\n```\n\n\n### Validation Steps\n\n```python\n# Validate your setup\ndef validate_setup():\n \"\"\"Validate package installation and API access.\"\"\"\n try:\n import open_geodata_api as ogapi\n print(\"\u2705 Package imported successfully\")\n \n # Test client creation\n pc = ogapi.planetary_computer()\n es = ogapi.earth_search()\n print(\"\u2705 Clients created successfully\")\n \n # Test collection listing\n pc_collections = pc.list_collections()\n print(f\"\u2705 PC collections: {len(pc_collections)} available\")\n \n # Test simple search\n test_results = pc.search(\n collections=['sentinel-2-l2a'],\n bbox=[-122.0, 47.0, -121.0, 48.0],\n limit=1\n )\n test_items = test_results.get_all_items()\n print(f\"\u2705 Test search: {len(test_items)} items found\")\n \n return True\n \n except Exception as e:\n print(f\"\u274c Validation failed: {e}\")\n return False\n\n# Run validation\nvalidate_setup()\n```\n\n\n## Advanced Usage\n\n### Custom Processing Workflows\n\n```python\n# Example: Multi-temporal analysis setup\ndef setup_temporal_analysis(bbox, date_ranges, max_cloud_cover=20):\n \"\"\"Setup data for temporal analysis.\"\"\"\n \n all_data = {}\n \n for period_name, date_range in date_ranges.items():\n print(f\"Searching for {period_name}...\")\n \n results = pc.search(\n collections=['sentinel-2-l2a'],\n bbox=bbox,\n datetime=date_range,\n query={'eo:cloud_cover': {'lt': max_cloud_cover}},\n limit=50\n )\n \n items = results.get_all_items()\n urls = items.get_all_urls(['B04', 'B03', 'B02', 'B08']) # RGB + NIR\n \n all_data[period_name] = {\n 'count': len(items),\n 'date_range': date_range,\n 'urls': urls\n }\n \n print(f\" Found {len(items)} items\")\n \n return all_data\n\n# Use for seasonal analysis\nseasonal_data = setup_temporal_analysis(\n bbox=[-120.0, 35.0, -119.0, 36.0],\n date_ranges={\n 'spring_2024': '2024-03-01/2024-05-31',\n 'summer_2024': '2024-06-01/2024-08-31',\n 'fall_2024': '2024-09-01/2024-11-30'\n }\n)\n```\n\n\n### Integration with Other Libraries\n##### Install Required Packages\n```python\npip install stackstac pystac\n```\n##### The Custom Functions\n```python\n# Example: Integration with STAC-tools\ndef integrate_with_stac_tools(items):\n \"\"\"Convert to format compatible with other STAC tools.\"\"\"\n \n # Export as standard STAC format\n stac_collection = items.to_dict() # GeoJSON FeatureCollection\n \n # Use with pystac\n try:\n import pystac\n \n # Convert items for pystac\n pystac_items = []\n for item_data in items.to_list():\n pystac_item = pystac.Item.from_dict(item_data)\n pystac_items.append(pystac_item)\n \n print(f\"Converted {len(pystac_items)} items to pystac format\")\n return pystac_items\n \n except ImportError:\n print(\"pystac not available\")\n return stac_collection\n\n# Example: Integration with stackstac\ndef prepare_for_stackstac(items, bands=['B04', 'B03', 'B02']):\n \"\"\"Prepare data for stackstac processing.\"\"\"\n \n try:\n import stackstac\n \n # Get STAC items in proper format\n stac_items = [item.to_dict() for item in items]\n \n # Note: URLs need to be properly signed\n # The package handles this automatically\n \n print(f\"Prepared {len(stac_items)} items for stackstac\")\n print(f\"Bands: {bands}\")\n \n return stac_items\n \n except ImportError:\n print(\"stackstac not available\")\n return None\n\nif __name__ == \"__main__\":\n # Use the functions\n stac_items = integrate_with_stac_tools(items)\n stackstac_items = prepare_for_stackstac(items)\n print(f\"STAC items: {stac_items} \\nStackSTAC items: {stackstac_items}\")\n print(f\"STAC items: {len(stac_items)} \\nStackSTAC items: {len(stackstac_items)}\")\n print(\"Integration and preparation complete!\")\n```\n\n\n### Custom URL Processing\n\n```python\n# Example: Custom URL validation and processing\ndef process_urls_custom(items, custom_processor=None):\n \"\"\"Process URLs with custom logic.\"\"\"\n \n def default_processor(url):\n \"\"\"Default URL processor.\"\"\"\n # Add custom headers, caching, etc.\n return url\n \n processor = custom_processor or default_processor\n \n processed_urls = {}\n \n for item in items:\n item_urls = item.get_all_asset_urls()\n processed_item_urls = {}\n \n for asset, url in item_urls.items():\n processed_url = processor(url)\n processed_item_urls[asset] = processed_url\n \n processed_urls[item.id] = processed_item_urls\n \n return processed_urls\n\n# Example custom processor\ndef add_caching_headers(url):\n \"\"\"Add caching parameters to URL.\"\"\"\n if '?' in url:\n return f\"{url}&cache=3600\"\n else:\n return f\"{url}?cache=3600\"\n\n# Use custom processing\ncached_urls = process_urls_custom(items, add_caching_headers)\nprint(f\"Cached URLs: {cached_urls}\")\n```\n## Utils Functions\n\n### Utils Functions - Usage Examples\n\n```python\nimport open_geodata_api as ogapi\nfrom open_geodata_api.utils import (\n filter_by_cloud_cover,\n download_datasets,\n download_url,\n download_from_json,\n download_seasonal,\n download_single_file,\n download_url_dict,\n download_items,\n download_seasonal_data,\n create_download_summary,\n is_url_expired,\n is_signed_url,\n re_sign_url_if_needed\n)\n\n# Setup clients\npc = ogapi.planetary_computer(auto_sign=True)\nes = ogapi.earth_search()\n```\n\n#### Example 1: Complete Workflow - Search and Filter by Cloud Cover\n```python\nprint(\"\ud83d\udd0d Searching for Sentinel-2 data...\")\nresults = pc.search(\n collections=[\"sentinel-2-l2a\"],\n bbox=[-122.5, 47.5, -122.0, 48.0], # Seattle area\n datetime=\"2024-06-01/2024-08-31\",\n limit=20\n)\n\nitems = results.get_all_items()\nprint(f\"Found {len(items)} items\")\n\n# Filter by cloud cover using utils\nclear_items = filter_by_cloud_cover(items, max_cloud_cover=15)\nprint(f\"After filtering: {len(clear_items)} clear items (<15% clouds)\")\n```\n\n#### Example 2: Download Single Asset from Search Results\n```python\nprint(\"\\n\ud83d\udce5 Downloading single asset...\")\nfirst_item = clear_items[^0]\nfirst_item.print_assets_info()\n\n# Get a single band URL and download\nred_url = first_item.get_asset_url('B04') # Red band, auto-signed\ndownloaded_file = download_single_file(\n red_url, \n destination=\"./data/red_band.tif\",\n provider=\"planetary_computer\"\n)\nprint(f\"Downloaded: {downloaded_file}\")\n```\n\n#### Example 3: Download RGB Bands from Multiple Items\n```python\nprint(\"\\n\ud83c\udfa8 Downloading RGB bands from multiple items...\")\nrgb_downloads = download_items(\n clear_items[:3], # First 3 clear items\n base_destination=\"./rgb_data/\",\n asset_keys=['B04', 'B03', 'B02'], # Red, Green, Blue\n create_product_folders=True\n)\n\nprint(f\"Downloaded RGB data for {len(rgb_downloads)} items\")\n```\n\n#### Example 4: Multi-Provider Data Collection and Download\n```python\nprint(\"\\n\ud83c\udf0d Comparing data from multiple providers...\")\n\n# Search both providers\nsearch_params = {\n 'collections': ['sentinel-2-l2a'],\n 'bbox': [-120.0, 35.0, -119.0, 36.0], # California\n 'datetime': '2024-07-01/2024-07-31',\n 'limit': 5\n}\n\npc_results = pc.search(**search_params)\nes_results = es.search(**search_params)\n\npc_items = pc_results.get_all_items()\nes_items = es_results.get_all_items()\n\nprint(f\"PC found: {len(pc_items)} items\")\nprint(f\"ES found: {len(es_items)} items\")\n\n# Filter both collections\npc_clear = filter_by_cloud_cover(pc_items, max_cloud_cover=20)\nes_clear = filter_by_cloud_cover(es_items, max_cloud_cover=20)\n\n# Download from both providers\nprint(\"\ud83d\udce6 Downloading from Planetary Computer...\")\npc_downloads = download_items(\n pc_clear[:2], \n base_destination=\"./pc_data/\",\n asset_keys=['B08', 'B04'], # NIR, Red for NDVI\n)\n\nprint(\"\ud83d\udce6 Downloading from EarthSearch...\")\nes_downloads = download_items(\n es_clear[:2], \n base_destination=\"./es_data/\",\n asset_keys=['nir', 'red'], # ES naming convention\n)\n```\n\n#### Example 5: Seasonal Analysis Workflow\n```python\nprint(\"\\n\ud83c\udf31 Setting up seasonal analysis...\")\n\ndef collect_seasonal_data(bbox, year):\n \"\"\"Collect data for seasonal analysis.\"\"\"\n seasons = {\n 'spring': f'{year}-03-01/{year}-05-31',\n 'summer': f'{year}-06-01/{year}-08-31', \n 'fall': f'{year}-09-01/{year}-11-30',\n 'winter': f'{year}-12-01/{year+1}-02-28'\n }\n \n seasonal_data = {}\n \n for season, date_range in seasons.items():\n print(f\"\ud83d\udd0d Searching {season} {year} data...\")\n \n results = pc.search(\n collections=['sentinel-2-l2a'],\n bbox=bbox,\n datetime=date_range,\n query={'eo:cloud_cover': {'lt': 25}},\n limit=10\n )\n \n items = results.get_all_items()\n filtered_items = filter_by_cloud_cover(items, max_cloud_cover=20)\n \n # Get URLs for NDVI calculation\n urls = filtered_items.get_all_urls(['B08', 'B04']) # NIR, Red\n \n seasonal_data[season] = {\n 'count': len(filtered_items),\n 'date_range': date_range,\n 'urls': urls\n }\n \n print(f\" Found {len(filtered_items)} clear scenes\")\n \n return seasonal_data\n\n# Collect seasonal data\nbbox = [-121.0, 38.0, -120.5, 38.5] # Northern California\nseasonal_data = collect_seasonal_data(bbox, 2024)\n\n# Download seasonal data using utils\nseasonal_downloads = download_seasonal_data(\n seasonal_data,\n base_destination=\"./seasonal_analysis/\",\n seasons=['spring', 'summer'], # Only spring and summer\n asset_keys=['B08', 'B04'] # NIR and Red bands\n)\n```\n\n#### Example 6: URL Management and Re-signing\n```python\nprint(\"\\n\ud83d\udd10 URL management example...\")\n\n# Get some URLs from items\nitem = pc_items[^0] if pc_items else clear_items[^0]\nall_urls = item.get_all_asset_urls()\n\n# Check URL status\nfor asset, url in list(all_urls.items())[:3]:\n print(f\"\\n\ud83d\udd17 Asset: {asset}\")\n print(f\" Signed: {is_signed_url(url)}\")\n print(f\" Expired: {is_url_expired(url)}\")\n \n # Re-sign if needed\n fresh_url = re_sign_url_if_needed(url, provider=\"planetary_computer\")\n if fresh_url != url:\n print(f\" \u2705 URL was re-signed\")\n```\n\n#### Example 7: Batch Processing with URL Dictionary\n```python\nprint(\"\\n\ud83d\udcca Batch processing workflow...\")\n\n# Create a custom URL dictionary from search results\ncustom_urls = {}\nfor i, item in enumerate(clear_items[:3]):\n item_id = f\"sentinel2_{item.id[-8:]}\" # Shortened ID\n # Get specific bands for analysis\n item_urls = item.get_band_urls(['B02', 'B03', 'B04', 'B08'])\n custom_urls[item_id] = item_urls\n\nprint(f\"Created custom URL dictionary with {len(custom_urls)} items\")\n\n# Download using URL dictionary\nbatch_downloads = download_url_dict(\n {k: v for k, v in list(custom_urls.items())[^0].items()}, # First item only\n base_destination=\"./batch_data/\",\n provider=\"planetary_computer\",\n create_subfolders=True\n)\n```\n#### Example 8: Export and Import Workflow\n```python\nprint(\"\\n\ud83d\udcbe Export/Import workflow...\")\n\n# Export URLs to JSON for later processing\nimport json\nwith open('./data_urls.json', 'w') as f:\n json.dump(custom_urls, f, indent=2)\n\nprint(\"\ud83d\udce4 URLs exported to data_urls.json\")\n\n# Download from JSON file using utils\njson_downloads = download_from_json(\n './data_urls.json',\n destination=\"./from_json/\",\n asset_keys=['B04', 'B08'], # Only specific bands\n create_folders=True\n)\n```\n\n#### Example 9: Download Summary and Reporting\n```python\nprint(\"\\n\ud83d\udccb Creating download summary...\")\n\n# Combine all download results\nall_downloads = {\n 'rgb_downloads': rgb_downloads,\n 'pc_downloads': pc_downloads,\n 'es_downloads': es_downloads,\n 'seasonal_downloads': seasonal_downloads,\n 'batch_downloads': batch_downloads,\n 'json_downloads': json_downloads\n}\n\n# Create comprehensive summary\nsummary = create_download_summary(\n all_downloads, \n output_file=\"./download_report.json\"\n)\n\nprint(f\"\ud83d\udcca Download Summary:\")\nprint(f\" Total files: {summary['total_files']}\")\nprint(f\" Successful: {summary['successful_downloads']}\")\nprint(f\" Failed: {summary['failed_downloads']}\")\nprint(f\" Success rate: {summary['success_rate']}\")\n```\n\n#### Example 10: Advanced Filtering and Processing\n```python\nprint(\"\\n\ud83d\udd2c Advanced processing workflow...\")\n\n# Multi-step filtering\ndef advanced_processing_workflow(bbox, max_cloud=10):\n \"\"\"Advanced workflow with multiple filtering steps.\"\"\"\n \n # Step 1: Search with broader criteria\n results = pc.search(\n collections=['sentinel-2-l2a'],\n bbox=bbox,\n datetime='2024-06-01/2024-09-30',\n limit=50\n )\n \n items = results.get_all_items()\n print(f\"Step 1: Found {len(items)} total items\")\n \n # Step 2: Filter by cloud cover\n clear_items = filter_by_cloud_cover(items, max_cloud_cover=max_cloud)\n print(f\"Step 2: {len(clear_items)} items with <{max_cloud}% clouds\")\n \n # Step 3: Convert to DataFrame for advanced filtering\n df = clear_items.to_dataframe(include_geometry=False)\n \n # Step 4: Filter by date (summer months only)\n summer_mask = df['datetime'].str.contains('2024-0[^678]') # June, July, August\n summer_items_ids = df[summer_mask]['id'].tolist()\n \n # Step 5: Get items for summer period\n summer_items = [item for item in clear_items if item.id in summer_items_ids]\n print(f\"Step 3: {len(summer_items)} summer items\")\n \n # Step 6: Download analysis-ready data\n analysis_downloads = download_items(\n summer_items[:5], # Top 5 summer items\n base_destination=\"./analysis_ready/\",\n asset_keys=['B02', 'B03', 'B04', 'B08', 'B11', 'B12'], # Multi-spectral\n create_product_folders=True\n )\n \n return analysis_downloads, summer_items\n\n# Run advanced workflow\nanalysis_results, summer_items = advanced_processing_workflow(\n bbox=[-122.0, 37.0, -121.5, 37.5], # San Francisco Bay\n max_cloud=5\n)\n\nprint(f\"\u2705 Analysis-ready data downloaded for {len(analysis_results)} items\")\n```\n\n#### Example 11: Error Handling and Resilient Downloads\n```python\nprint(\"\\n\ud83d\udee1\ufe0f Resilient download example...\")\n\ndef resilient_download(items, max_retries=3):\n \"\"\"Download with retry logic and error handling.\"\"\"\n \n successful_downloads = {}\n failed_downloads = {}\n \n for item in items[:2]: # Process first 2 items\n item_id = item.id\n retries = 0\n \n while retries < max_retries:\n try:\n # Try to download key bands\n downloads = download_items(\n [item],\n base_destination=f\"./resilient_data/attempt_{retries+1}/\",\n asset_keys=['B04', 'B08'],\n create_product_folders=True\n )\n \n successful_downloads[item_id] = downloads\n print(f\"\u2705 Successfully downloaded {item_id}\")\n break\n \n except Exception as e:\n retries += 1\n print(f\"\u274c Attempt {retries} failed for {item_id}: {e}\")\n \n if retries >= max_retries:\n failed_downloads[item_id] = str(e)\n print(f\"\ud83d\udc80 Gave up on {item_id} after {max_retries} attempts\")\n \n return successful_downloads, failed_downloads\n\n# Run resilient download\nsuccessful, failed = resilient_download(clear_items)\nprint(f\"Resilient download completed: {len(successful)} successful, {len(failed)} failed\")\n\nprint(\"\\n\ud83c\udf89 All utils function examples completed!\")\nprint(f\"Check your './data/' directory for downloaded files\")\n```\n\n## CLI Usage\n\n### Command Line Interface (CLI) Usage\nOpen Geodata API provides a comprehensive CLI for satellite data discovery, filtering, and downloading. After installation, use the `ogapi` command to access all functionality.\n\n#### Show package information\n```bash\nogapi info\n```\n\n#### Get help for any command\n```bash\nogapi --help\n```\n```bash\nogapi collections --help\n```\n```bash\nogapi search items --help\n```\n### Collections Management\n#### List all collections from both providers\n```bash\nogapi collections list\n```\n#### List from specific provider\n```bash\nogapi collections list --provider pc\n```\n```bash\nogapi collections list --provider es\n```\n\n#### Filter collections by keyword\n```bash\nogapi collections list --filter sentinel\n```\n\n#### Save results to file\n```bash\nogapi collections list --output collections.json\n```\n\n### Search Collections\n#### Find collections by keyword\n```bash\nogapi collections search sentinel\n```\n```bash\nogapi collections search landsat --provider pc\n```\n```bash\nogapi collections search modis --provider both\n```\n\n### Get Collection Information\n#### Get detailed collection info\n```bash\nogapi collections info sentinel-2-l2a\n```\n```bash\nogapi collections info sentinel-2-l2a --provider es\n```\n```bash\nogapi collections info landsat-c2-l2 --output collection_info.json\n```\n\n### Data Search\n#### Search for Sentinel-2 data in Seattle area\n```bash\nogapi search items --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --datetime \"2024-06-01/2024-08-31\" --limit 10\n```\n\n#### Search with cloud cover filter\n```bash\nogapi search items --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --cloud-cover 20 --output search_results.json\n```\n\n#### Search multiple collections\n```bash\nogapi search items --collections \"sentinel-2-l2a,landsat-c2-l2\" --bbox \"-120.0,35.0,-119.0,36.0\" --datetime \"2024-01-01/2024-12-31\"\n```\n#### Advanced Search with JSON Query\n```bash\nogapi search items --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --query '{\"eo:cloud_cover\":{\"lt\":15},\"platform\":{\"eq\":\"sentinel-2a\"}}' --output advanced_search.json\n```\n\n#### Find recent clear data (last 30 days)\n```bash\nogapi search quick sentinel-2-l2a \"-122.5,47.5,-122.0,48.0\"\n```\n\n#### Customize time range and cloud threshold\n```bash\nogapi search quick sentinel-2-l2a \"-122.5,47.5,-122.0,48.0\" --days 7 --cloud-cover 10 --limit 5\n```\n\n#### Save results for later processing\n```bash\nogapi search quick landsat-c2-l2 \"-120.0,35.0,-119.0,36.0\" --output recent_landsat.json\n```\n\n### Compare Data Availability Between Providers\n#### Compare Sentinel-2 availability\n```bash\nogapi search compare --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --datetime \"2024-06-01/2024-08-31\"\n```\n#### Compare with cloud filtering\n```bash\nogapi search compare --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --cloud-cover 25 --output comparison_results.json\n```\n\n### Working with Items\n#### Show info for first item in search results\n```bash\nogapi items info search_results.json\n```\n#### Show info for specific item by index\n```bash\nogapi items info search_results.json --item-index 2\n```\n#### Show detailed metadata\n```bash\nogapi items info search_results.json --show-all --output item_details.json\n```\n#### List all assets for an item\n```bash\nogapi items assets search_results.json\n```\n#### Filter assets by pattern\n```bash\nogapi items assets search_results.json --type \"image/tiff\"\n```\n#### Show URLs for assets\n```bash\nogapi items assets search_results.json --show-urls\n```\n#### Get URLs for RGB bands\n```bash\nogapi items urls search_results.json --assets \"B04,B03,B02\"\n```\n#### Get all asset URLs\n```bash\nogapi items urls search_results.json --output all_urls.json\n```\n#### Get URLs by pattern\n```bash\nogapi items urls search_results.json --pattern \"B0\" --output optical_bands.json\n```\n#### Get unsigned URLs\n```bash\nogapi items urls search_results.json --unsigned\n```\n### Compare Items by Quality\n#### Compare items by cloud cover (find clearest)\n```bash\nogapi items compare search_results.json\n```\n#### Compare by date (find most recent)\n```bash\nogapi items compare search_results.json --metric date\n```\n#### Compare asset availability\n```bash\nogapi items compare search_results.json --metric assets --max-items 10\n```\n### Data Download\n#### Download all assets from search results\n```bash\nogapi download search-results search_results.json\n```\n#### Download specific bands only\n```bash\nogapi download search-results search_results.json --assets \"B04,B03,B02\" --destination \"./rgb_data/\"\n```\n#### Download with additional filtering\n```bash\nogapi download search-results search_results.json --cloud-cover 15 --max-items 5 --assets \"B08,B04\"\n```\n#### Create flat file structure\n```bash\nogapi download search-results search_results.json --flat-structure --destination \"./satellite_data/\n```\n#### Download single file\n```bash\nogapi download url \"https://example.com/sentinel2_B04.tif\"\n```\n#### Download to specific location\n```bash\nogapi download url \"https://example.com/B04.tif\" --destination \"./data/red_band.tif\"\n```\n#### Download with provider specification (it will handdle url validation)\n```bash\nogapi download url \"https://pc.example.com/B04.tif\" --provider pc\n```\n#### Download from exported URLs\n```bash\nogapi download urls-json exported_urls.json\n```\n#### Custom destination\n```bash\nogapi download urls-json urls.json --destination \"./downloads/\" --flat-structure\n```\n#### Download all seasons from seasonal JSON\n```bash\nogapi download seasonal seasonal_data.json\n```\n#### Download specific seasons and assets\n```bash\nogapi download seasonal seasonal_data.json --seasons \"spring,summer\" --assets \"B08,B04\" --destination \"./time_series/\"\n```\n### Filter by Cloud Cover\n#### Filter search results by cloud cover\n```bash\nogapi utils filter-clouds search_results.json --max-cloud-cover 20\n```\n#### Filter and save results\n```bash\nogapi utils filter-clouds search_results.json --max-cloud-cover 15 --output clear_results.json --show-stats\n```\n### Export URLs\n#### Export all URLs from search results\n```bash\nogapi utils export-urls search_results.json --output all_urls.json\n```\n#### Export specific assets\n```bash\nogapi utils export-urls search_results.json --output rgb_urls.json --assets \"B04,B03,B02\"\n```\n#### Export in simple format\n```bash\nogapi utils export-urls search_results.json --output simple_urls.json --format simple\n```\n#### Export unsigned URLs\n```bash\nogapi utils export-urls search_results.json --output unsigned_urls.json --unsigned\n```\n### Validate URLs\n#### Basic URL validation\n```bash\nogapi utils validate-urls urls.json\n```\n#### Check accessibility (HTTP requests)\n```bash\nogapi utils validate-urls urls.json --check-access\n```\n#### Fix expired URLs\n```bash\nogapi utils validate-urls urls.json --fix-expired --output fixed_urls.json\n```\n#### Skip expiry check for speed\n```bash\nogapi utils validate-urls urls.json --no-check-expiry\n```\n### Analyze Search Results\n#### Analyze cloud cover distribution\n```bash\nogapi utils analyze search_results.json\n```\n#### Temporal analysis\n```bash\nogapi utils analyze search_results.json --metric temporal\n```\n#### Asset availability analysis\n```bash\nogapi utils analyze search_results.json --metric assets --output analysis_report.json\n```\n### Create Download Summaries\n#### Create detailed download summary\n```bash\nogapi utils download-summary download_results.json\n```\n#### Brief summary\n```bash\nogapi utils download-summary results.json --format brief\n```\n#### Save summary report\n```bash\nogapi utils download-summary results.json --output report.json\n```\n### Complete Workflow Examples\n#### 1.1: Search for data (Basic RGB Download Workflow)\n```bash\nogapi search items --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --datetime \"2024-06-01/2024-08-31\" --cloud-cover 20 --output search_results.json\n```\n#### 1.2: Filter for very clear imagery\n```bash\nogapi utils filter-clouds search_results.json --max-cloud-cover 10 --output clear_results.json\n```\n#### 1.3: Download RGB bands\n```bash\nogapi download search-results clear_results.json --assets \"B04,B03,B02\" --destination \"./rgb_analysis/\"\n```\n#### 1.4: Create summary\n```bash\nogapi utils download-summary download_results.json\n```\n#### 2.1: Search for data (NDVI Analysis Workflow)\n```bash\nogapi search items --collections sentinel-2-l2a --bbox \"-120.0,35.0,-119.0,36.0\" --datetime \"2024-01-01/2024-12-31\" --cloud-cover 25 --output yearly_search.json\n```\n#### 2.2: Filter by seasons and export URLs\n```bash\nogapi utils filter-clouds yearly_search.json --max-cloud-cover 15 --output clear_yearly.json\n```\n```bash\nogapi utils export-urls clear_yearly.json --assets \"B08,B04\" --output ndvi_urls.json\n```\n#### 2.3: Download NDVI bands\n```bash\nogapi download urls-json ndvi_urls.json --destination \"./ndvi_analysis/\"\n```\n#### 3.1 Compare data availability (Multi-Provider Comparison)\n```bash\nogapi search compare --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --datetime \"2024-06-01/2024-08-31\" --output comparison.json\n```\n#### 3.2 Search both providers separately\n```bash\nogapi search items --provider pc --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --cloud-cover 20 --output pc_results.json\n```\n```bash\nogapi search items --provider es --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --cloud-cover 20 --output es_results.json\n```\n#### 3.3 Download from best provider\n```bash\nogapi download search-results pc_results.json --max-items 3 --destination \"./pc_data/\"\n```\n#### 4.1 Search and analyze (Quality Assessment Workflow) \n```bash\nogapi search items --collections sentinel-2-l2a --bbox \"-122.5,47.5,-122.0,48.0\" --limit 50 --output large_search.json\n```\n#### 4.2 Analyze data quality \n```bash\nogapi utils analyze large_search.json --metric cloud_cover\n```\n```bash\nogapi utils analyze large_search.json --metric temporal\n```\n```bash\nogapi utils analyze large_search.json --metric assets\n```\n#### 4.3 Compare individual items\n```bash\nogapi items compare large_search.json --metric cloud_cover\n```\n#### 4.4 Download best items only\n```bash\nogapi utils filter-clouds large_search.json --max-cloud-cover 10 --output best_quality.json\n```\n```bash\nogapi download search-results best_quality.json --max-items 5\n```\n### Global Options\n**All commands support these global options:**\n#### Enable verbose output for debugging\n```bash\nogapi --verbose [command]\n```\n#### Show version\n```bash\nogapi --version\n```\n#### Get help for any command\n```bash\nogapi [command] --help\n```\n```bash\nogapi [command] [subcommand] --help\n```\n\n### Tips and Best Practices\n\n1. **Start Small**: Use `--limit` and `--max-items` to test workflows before large downloads\n2. **Save Results**: Always use `--output` to save search results for reprocessing\n3. **Filter Early**: Use cloud cover filters to reduce data volume\n4. **Check Status**: Use validation commands before large downloads\n5. **Resume Downloads**: Most download commands support resuming interrupted transfers\n6. **Use Verbose Mode**: Add `--verbose` for detailed debugging information\n\n### Environment Variables\n#### Optional: Set default provider\n```bash\nexport OGAPI_DEFAULT_PROVIDER=pc\n```\n#### Optional: Set default destination\n```bash\nexport OGAPI_DEFAULT_DEST=./satellite_data/\n```\n\n## FAQ\n\n### General Questions\n\n**Q: What makes this package different from using APIs directly?**\n\nA: Key advantages:\n\n- Unified interface across multiple APIs\n- Automatic URL signing/validation\n- Consistent error handling\n- No lock-in to specific data reading packages\n- Built-in best practices\n\n**Q: Can I use this with my existing geospatial workflow?**\n\nA: Absolutely! The package provides URLs that work with any raster reading library:\n\n```python\nurl = item.get_asset_url('red')\n\n# Use with your existing tools\nimport rioxarray; data = rioxarray.open_rasterio(url)\nimport rasterio; data = rasterio.open(url)\nfrom osgeo import gdal; data = gdal.Open(url)\n```\n\n**Q: Do I need API keys?**\n\nA: Only for Planetary Computer. EarthSearch is completely open.\n\n### Technical Questions\n\n**Q: How does automatic URL signing work?**\n\nA: When `auto_sign=True`, the package:\n\n1. Detects the provider (PC vs ES)\n2. For PC: Uses the planetary-computer package to sign URLs\n3. For ES: Returns URLs as-is (no signing needed)\n4. You can override with `signed=False/True`\n\n**Q: What about rate limiting?**\n\nA: Both APIs have rate limits:\n\n- **Planetary Computer**: Generous limits for signed URLs\n- **EarthSearch**: Standard HTTP rate limits\n\nThe package doesn't implement rate limiting - use your own if needed.\n\n**Q: Can I cache results?**\n\nA: Yes, several approaches:\n\n```python\n# 1. Export URLs to JSON\nitems.export_urls_json('cache.json')\n\n# 2. Save DataFrames\ndf = items.to_dataframe()\ndf.to_parquet('metadata_cache.parquet')\n\n# 3. Use your own caching layer\n```\n\n**Q: How do I handle different projections?**\n\nA: The package provides URLs - projection handling is up to your raster library:\n\n```python\nimport rioxarray\ndata = rioxarray.open_rasterio(url)\ndata_reprojected = data.rio.reproject('EPSG:4326')\n```\n\n\n### Troubleshooting Questions\n\n**Q: Why am I getting \"Asset not found\" errors?**\n\nA: Different providers use different asset names:\n\n- **PC**: B01, B02, B03, B04...\n- **EarthSearch**: coastal, blue, green, red...\n\nUse `item.print_assets_info()` to see available assets.\n\n**Q: Search returns no results but data should exist**\n\nA: Common issues:\n\n1. **Bbox order**: Use [west, south, east, north]\n2. **Date format**: PC accepts \"YYYY-MM-DD\", ES prefers RFC3339\n3. **Collection names**: Use `client.list_collections()` to verify\n4. **Cloud cover**: Try relaxing the threshold\n\n**Q: URLs work but data loading is slow**\n\nA: Optimization strategies:\n\n1. Use overview levels: `rioxarray.open_rasterio(url, overview_level=2)`\n2. Enable chunking: `rioxarray.open_rasterio(url, chunks=True)`\n3. Read smaller windows with rasterio\n4. Consider geographic proximity to data\n\n### Integration Questions\n\n**Q: Can I use this with Jupyter notebooks?**\n\nA: Yes! The package works great in Jupyter:\n\n```python\n# Display asset info\nitem.print_assets_info()\n\n# Show DataFrames\ndf = items.to_dataframe()\ndisplay(df)\n\n# Plot with matplotlib/cartopy\nimport matplotlib.pyplot as plt\ndata = rioxarray.open_rasterio(url)\ndata.plot()\n```\n\n**Q: How do I integrate with QGIS/ArcGIS?**\n\nA: Export URLs and use them directly:\n\n```python\n# Get URLs\nurls = item.get_all_asset_urls()\n\n# In QGIS: Add Raster Layer -> use the URL directly\n# In ArcGIS: Add Data -> Raster Dataset -> paste URL\n```\n\n**Q: Can I use this in production systems?**\n\nA: Yes! The package is designed for production use:\n\n- Robust error handling\n- No forced dependencies\n- Clean separation of concerns\n- Comprehensive logging support\n\n**Q: How do I contribute or report issues?**\n\nA: Visit the GitHub repository:\n\n- Report issues: GitHub Issues\n- Contribute: Pull Requests welcome\n- Documentation: Help improve this guide\n\n---\n\nThis completes the comprehensive user guide for Open Geodata API. The package provides a clean, flexible foundation for accessing open geospatial data while letting you maintain full control over data processing and analysis workflows.\n\n--- End ---\n\n[^1]: https://developers.arcgis.com/python/latest/guide/tutorials/import-data/\n\n[^2]: https://github.com/geopython/pygeoapi/blob/master/pygeoapi/openapi.py\n\n[^3]: https://opencagedata.com/api\n\n[^4]: https://opencagedata.com/tutorials/geocode-in-python\n\n[^5]: https://guides.library.columbia.edu/geotools/Python\n\n[^6]: https://pygeoapi.io\n\n[^7]: https://live.osgeo.org/en/quickstart/pygeoapi_quickstart.html\n\n[^8]: https://packaging.python.org\n\n[^9]: [http://r-project.ro/conference2018/presentations/Tutorial_Spatial_Analysis_in_R_with_Open_Geodata_-_uRos2018.pdf](https://github.com/Mirjan-Ali-Sha/open-geodata-api/blob/main/Open%20Geodata%20API%20-%20Complete%20User%20Guide.pdf)\n\n[^10]: https://geodata.readthedocs.io\n\n",
"bugtrack_url": null,
"license": null,
"summary": "Unified Python client for open geospatial data APIs: Planetary Computer, EarthSearch, and more",
"version": "0.3.0",
"project_urls": {
"Bug Reports": "https://github.com/Mirjan-Ali-Sha/open-geodata-api/issues",
"Documentation": "https://open-geodata-api.readthedocs.io",
"Homepage": "https://github.com/Mirjan-Ali-Sha/open-geodata-api",
"License": "https://github.com/Mirjan-Ali-Sha/open-geodata-api/blob/main/LICENSE",
"Repository": "https://github.com/Mirjan-Ali-Sha/open-geodata-api",
"Third-Party Notices": "https://github.com/Mirjan-Ali-Sha/open-geodata-api/blob/main/THIRD_PARTY_NOTICES.md"
},
"split_keywords": [
"api-client",
" cli",
" earth-observation",
" earthsearch",
" geodata",
" geospatial",
" open-data",
" planetary-computer",
" remote-sensing",
" satellite",
" stac"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a193c5eefdd139eba1e1614532b643e545474c5e7cdaf2209172c9c2cce2e1e8",
"md5": "5c36ec099a54a68a18acaf235c6f05d9",
"sha256": "fc202c1f107172f75b61ac878a7efb4b7d04e029858450bd4c0d76dd25dfd08c"
},
"downloads": -1,
"filename": "open_geodata_api-0.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5c36ec099a54a68a18acaf235c6f05d9",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 97542,
"upload_time": "2025-07-13T17:58:09",
"upload_time_iso_8601": "2025-07-13T17:58:09.871339Z",
"url": "https://files.pythonhosted.org/packages/a1/93/c5eefdd139eba1e1614532b643e545474c5e7cdaf2209172c9c2cce2e1e8/open_geodata_api-0.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "884af6e353404bb9fc0cd12fdaf5bb4bc2efaf70c35820a30462f699d7e562eb",
"md5": "c4df9441b22c4f99ee2457d69790cad8",
"sha256": "e07305393bbf120122fc820946a417644cfa8a9316483e9ce085312622c4ce97"
},
"downloads": -1,
"filename": "open_geodata_api-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "c4df9441b22c4f99ee2457d69790cad8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 1343828,
"upload_time": "2025-07-13T17:58:11",
"upload_time_iso_8601": "2025-07-13T17:58:11.887924Z",
"url": "https://files.pythonhosted.org/packages/88/4a/f6e353404bb9fc0cd12fdaf5bb4bc2efaf70c35820a30462f699d7e562eb/open_geodata_api-0.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-13 17:58:11",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Mirjan-Ali-Sha",
"github_project": "open-geodata-api",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "requests",
"specs": [
[
">=",
"2.25.0"
]
]
},
{
"name": "pandas",
"specs": [
[
">=",
"1.3.0"
]
]
},
{
"name": "sphinx-copybutton",
"specs": []
}
],
"lcname": "open-geodata-api"
}