Tutorial Examples: Complete Workflow Progression¶
Welcome to the Hazelbean tutorial series! This comprehensive learning journey takes you through the entire geospatial processing workflow, from initial project setup to professional result organization.
Learning Path Overview¶
Progressive Learning
Each step builds on the previous ones. Complete them in order for the best learning experience.
| Step | Topic | Learning Time | Key Concepts |
|---|---|---|---|
| 1 | Project Setup | 5 minutes | ProjectFlow, Directory Structure |
| 2 | Data Loading | 10 minutes | get_path(), File Discovery, Raster Info |
| 3 | Processing | 15 minutes | Array Operations, Transformations |
| 4 | Analysis | 20 minutes | Spatial Analysis, Multi-raster Operations |
| 5 | Export Results | 15 minutes | Professional Organization, Documentation |
Total Learning Time: ~65 minutes
Step 1: Project Setup¶
Step 1: Project Setup and Directory Management Learning Time: 5 minutes Prerequisites: Basic Python knowledge
Learn how to initialize a Hazelbean ProjectFlow for organized geospatial workflows. This is the foundation for all Hazelbean projects - it creates an organized directory structure and provides intelligent file path resolution.
main()
¶
Demonstrates ProjectFlow initialization and basic project organization.
Source code in examples/step_1_project_setup.py
def main():
"""
Demonstrates ProjectFlow initialization and basic project organization.
"""
# Create a new project with automatic directory structure
# This will create the project directory if it doesn't exist
project_name = 'hazelbean_tutorial'
p = hb.ProjectFlow(project_name)
print("=== Hazelbean Project Setup Demo ===")
print(f"Project name: {project_name}")
print(f"Project directory: {p.project_dir}")
print()
# ProjectFlow automatically creates these standard directories:
print("=== Standard Project Directories ===")
print(f"Input directory: {p.input_dir}")
print(f"Intermediate directory: {p.intermediate_dir}")
print(f"Output directory: {p.output_dir}")
print()
# Show data discovery paths (where ProjectFlow looks for files)
print("=== Data Discovery Hierarchy ===")
print("When you use p.get_path(), Hazelbean searches in this order:")
print(f"1. Base data directory: {p.base_data_dir}")
print(f"2. Model base data directory: {p.model_base_data_dir}")
print(f"3. Project base data directory: {p.project_base_data_dir}")
print(f"4. Current directory: {p.project_dir}")
print()
# Verify directories exist
print("=== Directory Status ===")
for dir_name, dir_path in [
("Input", p.input_dir),
("Intermediate", p.intermediate_dir),
("Output", p.output_dir)
]:
exists = "✓ EXISTS" if os.path.exists(dir_path) else "✗ CREATED"
print(f"{dir_name:12}: {exists}")
print()
print("🎉 Project setup complete!")
print("Next: Run step_2_data_loading.py to learn about intelligent file discovery")
📚 Learning Objectives
By completing this step, you will:
- ✅ Initialize a Hazelbean ProjectFlow for organized geospatial workflows
- ✅ Understand the intelligent directory structure that Hazelbean creates
- ✅ Master the data discovery hierarchy and how get_path() searches for files
- ✅ Build the foundation for all subsequent Hazelbean projects
Prerequisites: Basic Python knowledge
Estimated Time: 5 minutes
Difficulty: 🟢 Beginner
🎯 Key Takeaway
ProjectFlow is the foundation of every Hazelbean workflow. It creates an organized directory structure and provides intelligent file path resolution that makes your analysis reproducible and professional. Think of it as your "project manager" that keeps everything organized automatically.
💡 Real-World Application
In research and professional work, you'll often work with multiple datasets across different projects. ProjectFlow ensures that your analysis remains organized and reproducible, whether you're analyzing land cover changes, environmental impact assessments, or climate modeling.
Step 2: Data Loading¶
Step 2: Data Loading and File Discovery Learning Time: 10 minutes Prerequisites: Completed step_1_project_setup.py
Learn how to use Hazelbean's intelligent file discovery system (get_path) and load geospatial data for analysis.
main()
¶
Demonstrates intelligent file location and basic raster loading.
Source code in examples/step_2_data_loading.py
def main():
"""
Demonstrates intelligent file location and basic raster loading.
"""
# Initialize project (builds on step 1)
p = hb.ProjectFlow('hazelbean_tutorial')
print("=== Hazelbean Data Loading Demo ===")
print()
# Demonstrate get_path() - Hazelbean's intelligent file finder
print("=== Intelligent File Discovery with get_path() ===")
# Try to find a test raster (get_path searches multiple locations)
try:
raster_path = p.get_path('tests/ee_r264_ids_900sec.tif')
# Verify the file actually exists
if hb.path_exists(raster_path):
print(f"✓ Found raster: {raster_path}")
found_raster = True
else:
raise FileNotFoundError("Path found but file doesn't exist")
except:
print("✗ Test raster not found, using alternative...")
# Fallback to any available data
try:
raster_path = p.get_path('pyramids/ha_per_cell_900sec.tif')
if hb.path_exists(raster_path):
print(f"✓ Found alternative raster: {raster_path}")
found_raster = True
else:
raise FileNotFoundError("Alternative path found but file doesn't exist")
except:
print("✗ No sample raster found in data directories")
found_raster = False
if found_raster:
print()
print("=== Loading and Examining Raster Data ===")
# Load raster information without reading full data
raster_info = hb.get_raster_info_hb(raster_path)
print(f"Raster size: {raster_info['raster_size']} (width x height)")
print(f"Pixel size: {raster_info['pixel_size']}")
print(f"Number of bands: {raster_info['n_bands']}")
print(f"Data type: {raster_info.get('datatype', 'Unknown')}")
print(f"NoData value: {raster_info['ndv']}")
# Load raster data as numpy array
print()
print("=== Loading Raster as Array ===")
raster_array = hb.as_array(raster_path)
print(f"Array shape: {raster_array.shape}")
print(f"Array data type: {raster_array.dtype}")
print(f"Min value: {np.nanmin(raster_array):.2f}")
print(f"Max value: {np.nanmax(raster_array):.2f}")
print(f"Mean value: {np.nanmean(raster_array):.2f}")
else:
print()
print("=== Alternative: Create Sample Data ===")
print("When sample data isn't available, you can create test arrays:")
# Create a simple test array
test_array = np.random.rand(100, 100) * 1000
print(f"Created test array shape: {test_array.shape}")
print(f"Test array range: {test_array.min():.1f} to {test_array.max():.1f}")
print()
print("🎉 Data loading complete!")
print("Next: Run step_3_basic_processing.py to learn raster operations")
📚 Learning Objectives
By completing this step, you will:
- ✅ Master Hazelbean's intelligent file discovery system with get_path()
- ✅ Load and examine geospatial raster data efficiently
- ✅ Understand raster metadata and properties for analysis planning
- ✅ Handle missing data scenarios gracefully with fallback patterns
Prerequisites: Completed Step 1 (Project Setup)
Estimated Time: 10 minutes
Difficulty: 🟡 Intermediate
💡 Pro Insight
get_path() is Hazelbean's "smart search engine" for your files. It searches through multiple data directories automatically, following a intelligent hierarchy:
- Project-specific directories first
- Base data directories
- Model-wide data repositories
- Current working directory as fallback
This means your code works whether data is local to your project, shared across projects, or in a centralized data repository.
Step 3: Processing¶
Step 3: Basic Raster Processing Learning Time: 15 minutes Prerequisites: Completed step_1_project_setup.py and step_2_data_loading.py
Learn fundamental raster operations including transformations, resampling, and mathematical operations on geospatial data.
main()
¶
Demonstrates basic raster processing operations.
Source code in examples/step_3_basic_processing.py
def main():
"""
Demonstrates basic raster processing operations.
"""
# Initialize project (builds on previous steps)
p = hb.ProjectFlow('hazelbean_tutorial')
print("=== Hazelbean Basic Processing Demo ===")
print()
# Try to find sample data for processing
try:
input_path = p.get_path('tests/ee_r264_ids_900sec.tif')
if hb.path_exists(input_path):
has_sample_data = True
print(f"✓ Using sample raster: {os.path.basename(input_path)}")
else:
raise FileNotFoundError("Path found but file doesn't exist")
except:
has_sample_data = False
print("✗ Sample data not found, creating synthetic raster...")
if has_sample_data:
# Working with real data
print()
print("=== Basic Raster Information ===")
info = hb.get_raster_info_hb(input_path)
print(f"Original size: {info['raster_size']}")
print(f"Pixel size: {info['pixel_size']}")
# Create output path in intermediate directory
processed_path = os.path.join(p.intermediate_dir, 'processed_raster.tif')
print()
print("=== Resampling Raster ===")
# Resample to a different resolution (make pixels 2x larger)
target_pixel_size = (info['pixel_size'][0] * 2, info['pixel_size'][1] * 2)
try:
hb.warp_raster(
input_path,
target_pixel_size,
processed_path,
resample_method='nearest'
)
# Check the result
new_info = hb.get_raster_info_hb(processed_path)
print(f"✓ Resampled raster created")
print(f"New size: {new_info['raster_size']}")
print(f"New pixel size: {new_info['pixel_size']}")
except Exception as e:
print(f"✗ Resampling failed: {e}")
print("Continuing with array operations...")
# Load and process array data
print()
print("=== Array-based Processing ===")
array = hb.as_array(input_path)
else:
# Create synthetic data for demonstration
print()
print("=== Creating Synthetic Data ===")
array = np.random.rand(50, 50) * 100
processed_path = os.path.join(p.intermediate_dir, 'synthetic_processed.tif')
print(f"Created {array.shape} array with values 0-100")
# Mathematical operations on arrays
print()
print("=== Mathematical Operations ===")
print(f"Original - Min: {np.nanmin(array):.2f}, Max: {np.nanmax(array):.2f}")
# Apply some transformations
# Scale values
scaled_array = array * 2.0
print(f"Scaled x2 - Min: {np.nanmin(scaled_array):.2f}, Max: {np.nanmax(scaled_array):.2f}")
# Apply threshold
threshold_array = np.where(array > np.nanmean(array), 1, 0)
unique_values = np.unique(threshold_array)
print(f"Threshold (mean={np.nanmean(array):.2f}) - Unique values: {unique_values}")
# Calculate statistics
print()
print("=== Statistical Summary ===")
print(f"Mean: {np.nanmean(array):.2f}")
print(f"Standard deviation: {np.nanstd(array):.2f}")
print(f"Non-zero pixels: {np.count_nonzero(array)}")
print(f"Total pixels: {array.size}")
print()
print("🎉 Basic processing complete!")
print("Next: Run step_4_analysis.py to learn spatial analysis workflows")
📚 Learning Objectives
By completing this step, you will:
- ✅ Apply mathematical operations to raster data efficiently
- ✅ Perform coordinate transformations between spatial reference systems
- ✅ Create derived datasets through raster calculations
- ✅ Handle different data types and scaling factors appropriately
Prerequisites: Completed Steps 1-2 (Setup & Data Loading)
Estimated Time: 15 minutes
Difficulty: 🟡 Intermediate
🎯 Critical Concept
Spatial Reference Systems (CRS) are fundamental to geospatial analysis:
- Always verify your coordinate reference systems when combining datasets
- Different CRS can make data appear misaligned even when geographically correct
- Hazelbean helps with transformations, but understanding your data's spatial properties prevents analysis errors
- Rule of thumb: Match all datasets to the same CRS before analysis
🔧 Technical Deep Dive
Raster processing involves working with multi-dimensional arrays where each cell represents a geographic location. Understanding data types (float32 vs int16), no-data values, and scaling factors ensures your mathematical operations produce meaningful results.
Step 4: Analysis¶
Step 4: Spatial Analysis and Multi-Raster Operations Learning Time: 20 minutes Prerequisites: Completed steps 1-3
Learn advanced spatial analysis including combining multiple rasters, spatial calculations, and using Hazelbean's raster calculator for complex geospatial modeling workflows.
main()
¶
Demonstrates spatial analysis and multi-raster operations.
Source code in examples/step_4_analysis.py
def main():
"""
Demonstrates spatial analysis and multi-raster operations.
"""
# Initialize project (builds on previous steps)
p = hb.ProjectFlow('hazelbean_tutorial')
print("=== Hazelbean Spatial Analysis Demo ===")
print()
# Try to find multiple sample rasters for analysis
raster_paths = []
for filename in ['tests/ee_r264_ids_900sec.tif', 'pyramids/ha_per_cell_900sec.tif']:
try:
path = p.get_path(filename)
if hb.path_exists(path):
raster_paths.append(path)
print(f"✓ Found: {os.path.basename(path)}")
else:
print(f"✗ Not found: {filename} (path located but file missing)")
except:
print(f"✗ Not found: {filename}")
if len(raster_paths) >= 2:
print()
print("=== Multi-Raster Analysis ===")
# Load multiple rasters as arrays
array1 = hb.as_array(raster_paths[0])
array2 = hb.as_array(raster_paths[1])
print(f"Raster 1 shape: {array1.shape}")
print(f"Raster 2 shape: {array2.shape}")
# If shapes don't match, work with a subset
if array1.shape != array2.shape:
print("Shapes don't match - using smaller common area")
min_rows = min(array1.shape[0], array2.shape[0])
min_cols = min(array1.shape[1], array2.shape[1])
array1 = array1[:min_rows, :min_cols]
array2 = array2[:min_rows, :min_cols]
print(f"Cropped to shape: {array1.shape}")
# Spatial operations between rasters
print()
print("=== Raster Calculations ===")
# Addition
sum_array = array1 + array2
print(f"Sum - Min: {np.nanmin(sum_array):.2f}, Max: {np.nanmax(sum_array):.2f}")
# Ratio (with division by zero protection)
with np.errstate(divide='ignore', invalid='ignore'):
ratio_array = np.divide(array1, array2,
out=np.zeros_like(array1),
where=(array2 != 0))
print(f"Ratio - Min: {np.nanmin(ratio_array):.2f}, Max: {np.nanmax(ratio_array):.2f}")
# Conditional analysis
overlap_mask = (array1 > 0) & (array2 > 0)
overlap_pixels = np.sum(overlap_mask)
print(f"Overlapping pixels: {overlap_pixels} ({overlap_pixels/array1.size*100:.1f}%)")
else:
print()
print("=== Single Raster Analysis (Synthetic Data) ===")
# Create synthetic landscape for analysis
rows, cols = 100, 100
# Elevation-like surface
x = np.linspace(0, 10, cols)
y = np.linspace(0, 10, rows)
X, Y = np.meshgrid(x, y)
elevation = 100 + 50 * np.sin(X) + 30 * np.cos(Y) + np.random.normal(0, 5, (rows, cols))
# Land cover categories
landcover = np.random.choice([1, 2, 3, 4, 5], size=(rows, cols),
p=[0.3, 0.2, 0.2, 0.2, 0.1])
print(f"Created synthetic landscape: {elevation.shape}")
print(f"Elevation range: {elevation.min():.1f} to {elevation.max():.1f}")
array1, array2 = elevation, landcover
# Spatial analysis calculations
print()
print("=== Spatial Analysis Calculations ===")
# Zone-based statistics
if array2 is not None:
unique_zones = np.unique(array2)
print(f"Analysis zones: {len(unique_zones)} unique values")
for zone in unique_zones[:5]: # Show first 5 zones
mask = array2 == zone
zone_values = array1[mask]
if len(zone_values) > 0:
print(f" Zone {zone}: {len(zone_values)} pixels, "
f"mean={np.mean(zone_values):.2f}")
# Distance and neighborhood analysis (simplified)
print()
print("=== Neighborhood Analysis ===")
# Simple moving window (3x3) mean
from scipy import ndimage
window_mean = ndimage.uniform_filter(array1.astype(float), size=3)
print(f"Original mean: {np.nanmean(array1):.2f}")
print(f"Smoothed mean: {np.nanmean(window_mean):.2f}")
print(f"Smoothing effect: {np.nanmean(np.abs(array1 - window_mean)):.2f}")
# Hot spot identification (values > 2 std dev above mean)
threshold = np.nanmean(array1) + 2 * np.nanstd(array1)
hotspots = array1 > threshold
n_hotspots = np.sum(hotspots)
print()
print("=== Spatial Pattern Analysis ===")
print(f"Hot spot threshold: {threshold:.2f}")
print(f"Hot spot pixels: {n_hotspots} ({n_hotspots/array1.size*100:.1f}%)")
# Save analysis results to intermediate directory
# Ensure intermediate directory exists
os.makedirs(p.intermediate_dir, exist_ok=True)
output_path = os.path.join(p.intermediate_dir, 'analysis_summary.txt')
with open(output_path, 'w') as f:
f.write("Spatial Analysis Summary\n")
f.write("========================\n")
f.write(f"Input shape: {array1.shape}\n")
f.write(f"Value range: {np.nanmin(array1):.2f} to {np.nanmax(array1):.2f}\n")
f.write(f"Mean: {np.nanmean(array1):.2f}\n")
f.write(f"Hot spots: {n_hotspots} pixels\n")
print(f"✓ Analysis summary saved: {output_path}")
print()
print("🎉 Spatial analysis complete!")
print("Next: Run step_5_export_results.py to learn about saving outputs")
📚 Learning Objectives
By completing this step, you will:
- ✅ Combine multiple raster datasets in sophisticated spatial analysis
- ✅ Perform zonal statistics and regional summaries for insights
- ✅ Create classification maps and derived analytical products
- ✅ Handle complex analytical workflows efficiently and reproducibly
Prerequisites: Completed Steps 1-3 (Setup, Data Loading, Basic Processing)
Estimated Time: 20 minutes
Difficulty: 🟠 Advanced
🌍 Real-World Applications
This step demonstrates patterns you'll use for:
- 🌳 Land Cover Analysis: Classify land use types and track changes over time
- 📈 Change Detection: Compare datasets across different time periods
- 🌊 Environmental Impact: Assess how human activities affect natural systems
- 🎯 Regional Planning: Analyze patterns within administrative or ecological boundaries
- 📊 Resource Management: Quantify natural resources within specific regions
🧮 Analysis Philosophy
Multi-raster operations represent the core of spatial analysis. You're not just processing individual datasets, but discovering relationships and patterns that emerge when data sources are combined intelligently.
Step 5: Export Results¶
Step 5: Export Results and Project Organization Learning Time: 15 minutes Prerequisites: Completed steps 1-4
Learn how to properly save geospatial results, organize outputs in the project structure, and create documentation for your analysis workflow.
main()
¶
Demonstrates proper result export and project organization.
Source code in examples/step_5_export_results.py
def main():
"""
Demonstrates proper result export and project organization.
"""
# Initialize project (builds on previous steps)
p = hb.ProjectFlow('hazelbean_tutorial')
print("=== Hazelbean Results Export Demo ===")
print()
# Create some example results to export
print("=== Generating Sample Results ===")
# Create a sample analysis result
rows, cols = 50, 50
result_array = np.random.rand(rows, cols) * 100
# Create a classification result
classification = np.random.choice([1, 2, 3], size=(rows, cols),
p=[0.4, 0.4, 0.2])
print(f"Generated results array: {result_array.shape}")
print(f"Generated classification: {classification.shape}")
# Set up proper output organization
print()
print("=== Organizing Output Structure ===")
# Create timestamped analysis folder
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
analysis_dir = os.path.join(p.output_dir, f'tutorial_analysis_{timestamp}')
# Create subdirectories for different output types
rasters_dir = os.path.join(analysis_dir, 'rasters')
reports_dir = os.path.join(analysis_dir, 'reports')
for directory in [analysis_dir, rasters_dir, reports_dir]:
os.makedirs(directory, exist_ok=True)
print(f"✓ Created: {os.path.relpath(directory, p.project_dir)}")
# Export raster results (simulate proper geospatial export)
print()
print("=== Exporting Raster Results ===")
# In a real workflow, you'd save with proper geospatial metadata
# For this tutorial, we'll save as simple arrays with documentation
results_file = os.path.join(rasters_dir, 'analysis_results.npy')
np.save(results_file, result_array)
print(f"✓ Saved results array: {os.path.basename(results_file)}")
classification_file = os.path.join(rasters_dir, 'classification.npy')
np.save(classification_file, classification)
print(f"✓ Saved classification: {os.path.basename(classification_file)}")
# Create metadata file
metadata_file = os.path.join(rasters_dir, 'metadata.txt')
with open(metadata_file, 'w') as f:
f.write("Raster Metadata\n")
f.write("===============\n")
f.write(f"Analysis date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"Results shape: {result_array.shape}\n")
f.write(f"Results range: {result_array.min():.2f} to {result_array.max():.2f}\n")
f.write(f"Classification classes: {sorted(np.unique(classification))}\n")
f.write("\nClass definitions:\n")
f.write("1 = Low values\n")
f.write("2 = Medium values\n")
f.write("3 = High values\n")
print(f"✓ Created metadata: {os.path.basename(metadata_file)}")
# Generate comprehensive analysis report
print()
print("=== Creating Analysis Report ===")
report_file = os.path.join(reports_dir, 'tutorial_analysis_report.md')
with open(report_file, 'w') as f:
f.write("# Hazelbean Tutorial Analysis Report\n\n")
f.write(f"**Analysis Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"**Project:** {p.project_name}\n\n")
f.write("## Summary\n\n")
f.write("This analysis demonstrates the complete Hazelbean workflow from ")
f.write("project setup through result export.\n\n")
f.write("## Data Processing Steps\n\n")
f.write("1. **Project Setup**: Initialized ProjectFlow with organized directories\n")
f.write("2. **Data Loading**: Used get_path() for intelligent file discovery\n")
f.write("3. **Processing**: Applied transformations and mathematical operations\n")
f.write("4. **Analysis**: Performed spatial analysis and multi-raster operations\n")
f.write("5. **Export**: Organized and documented results\n\n")
f.write("## Results Summary\n\n")
f.write(f"- **Results Array**: {result_array.shape} pixels\n")
f.write(f"- **Value Range**: {result_array.min():.2f} to {result_array.max():.2f}\n")
f.write(f"- **Mean Value**: {result_array.mean():.2f}\n")
f.write(f"- **Standard Deviation**: {result_array.std():.2f}\n\n")
f.write("## Classification Results\n\n")
for class_val in sorted(np.unique(classification)):
count = np.sum(classification == class_val)
percent = (count / classification.size) * 100
f.write(f"- **Class {class_val}**: {count} pixels ({percent:.1f}%)\n")
f.write("\n## Files Generated\n\n")
f.write("- `rasters/analysis_results.npy` - Main analysis results\n")
f.write("- `rasters/classification.npy` - Classification map\n")
f.write("- `rasters/metadata.txt` - Raster metadata\n")
f.write("- `reports/tutorial_analysis_report.md` - This report\n\n")
f.write("## Project Structure\n\n")
f.write("```\n")
f.write(f"{p.project_name}/\n")
f.write("├── inputs/ # Input data\n")
f.write("├── intermediate/ # Processing files\n")
f.write("└── outputs/ # Final results\n")
f.write(f" └── tutorial_analysis_{timestamp}/\n")
f.write(" ├── rasters/ # Geospatial outputs\n")
f.write(" └── reports/ # Documentation\n")
f.write("```\n\n")
f.write("## Next Steps\n\n")
f.write("- Modify parameters for your own analysis\n")
f.write("- Use real geospatial data with coordinate systems\n")
f.write("- Integrate with other Hazelbean functions\n")
f.write("- Add visualization and plotting\n")
print(f"✓ Generated report: {os.path.basename(report_file)}")
# Create quick summary for console
print()
print("=== Project Summary ===")
print(f"Project name: {p.project_name}")
print(f"Project directory: {p.project_dir}")
print(f"Analysis outputs: {os.path.relpath(analysis_dir, p.project_dir)}")
# Count files in each directory
for subdir, name in [(rasters_dir, 'Rasters'), (reports_dir, 'Reports')]:
file_count = len([f for f in os.listdir(subdir) if os.path.isfile(os.path.join(subdir, f))])
print(f"{name}: {file_count} files")
print()
print("🎉 Tutorial complete! You've learned:")
print(" ✓ ProjectFlow setup and directory management")
print(" ✓ Intelligent file discovery with get_path()")
print(" ✓ Basic raster processing and transformations")
print(" ✓ Spatial analysis and multi-raster operations")
print(" ✓ Professional result organization and documentation")
print()
print(f"📁 Check your results in: {analysis_dir}")
print("📖 Read the full report for next steps and customization ideas")
📚 Learning Objectives
By completing this step, you will:
- ✅ Organize analysis outputs in professional directory structures
- ✅ Create comprehensive documentation and metadata for reproducibility
- ✅ Generate detailed analysis reports with clear summaries and insights
- ✅ Establish reproducible workflows for sharing and collaboration
Prerequisites: Completed Steps 1-4 (Complete analysis workflow)
Estimated Time: 15 minutes
Difficulty: 🟢 Beginner (concepts) / 🟠 Advanced (best practices)
💼 Professional Best Practice
Documentation and organization are just as important as the analysis itself. Well-organized results with clear documentation:
- Enable collaboration with colleagues and stakeholders
- Ensure reproducibility for future research and validation
- Facilitate peer review and publication processes
- Save time when you return to a project months later
- Build credibility in professional and academic settings
🎯 Career Impact
Professional result organization distinguishes expert practitioners:
- Junior level: Focuses on getting the analysis to work
- Intermediate level: Produces clean code and basic documentation
- Expert level: Creates comprehensive, self-documenting workflows that others can build upon
This step teaches expert-level practices that make your work valuable beyond the immediate analysis.
Quick Start Guide¶
Ready to Begin?
Prerequisites: - Python environment with Hazelbean installed - Basic familiarity with Python programming - Understanding of geospatial data concepts (helpful but not required)
Setup Instructions:
Learning Support¶
Need Help?
- Got stuck? Each example includes error handling and fallback options
- Want more detail? Check the comprehensive docstrings in each function
- Ready for more? Explore the Test Documentation for advanced patterns
- Have questions? Review the source code directly in the examples/ directory
Next Steps
After completing this tutorial series, you'll be ready to:
- Build your own geospatial analysis workflows
- Integrate Hazelbean with other geospatial libraries
- Contribute to the Hazelbean project
- Explore advanced features in the full documentation
This educational content is automatically extracted from the latest example code, ensuring it stays up-to-date with current best practices.