oxigdal_algorithms/tutorials.rs
1//! Algorithm Tutorials and Guides
2//!
3//! This module provides comprehensive tutorials for using OxiGDAL algorithms
4//! for raster and vector processing.
5//!
6//! # Table of Contents
7//!
8//! 1. [Resampling Operations](#resampling-operations)
9//! 2. [Raster Calculations](#raster-calculations)
10//! 3. [Terrain Analysis](#terrain-analysis)
11//! 4. [Vector Operations](#vector-operations)
12//! 5. [SIMD Optimization](#simd-optimization)
13//! 6. [Parallel Processing](#parallel-processing)
14//!
15//! # Resampling Operations
16//!
17//! Resampling is the process of changing the spatial resolution or extent of a raster.
18//! OxiGDAL provides several high-quality resampling methods.
19//!
20//! ## Available Methods
21//!
22//! - **Nearest Neighbor**: Fastest, best for categorical data
23//! - **Bilinear**: Smooth, good for continuous data
24//! - **Bicubic**: Higher quality, slower
25//! - **Lanczos**: Highest quality, most expensive
26//!
27//! ## Example: Basic Resampling
28//!
29//! ```rust
30//! use oxigdal_algorithms::resampling::{ResamplingMethod, Resampler};
31//! use oxigdal_core::buffer::RasterBuffer;
32//! use oxigdal_core::types::RasterDataType;
33//!
34//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
35//! // Create source raster (1000x1000)
36//! let src = RasterBuffer::zeros(1000, 1000, RasterDataType::Float32);
37//!
38//! // Downsample to 500x500 using bilinear interpolation
39//! let resampler = Resampler::new(ResamplingMethod::Bilinear);
40//! let dst = resampler.resample(&src, 500, 500)?;
41//!
42//! assert_eq!(dst.width(), 500);
43//! assert_eq!(dst.height(), 500);
44//! # Ok(())
45//! # }
46//! ```
47//!
48//! ## Example: Comparing Methods
49//!
50//! ```rust,ignore
51//! use oxigdal_algorithms::resampling::{ResamplingMethod, Resampler};
52//! use oxigdal_core::buffer::RasterBuffer;
53//! use oxigdal_core::types::RasterDataType;
54//!
55//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
56//! let src = RasterBuffer::zeros(1000, 1000, RasterDataType::Float32);
57//!
58//! // Try different methods
59//! let methods = vec![
60//! ResamplingMethod::NearestNeighbor,
61//! ResamplingMethod::Bilinear,
62//! ResamplingMethod::Bicubic,
63//! ResamplingMethod::Lanczos,
64//! ];
65//!
66//! for method in methods {
67//! let resampler = Resampler::new(method);
68//! let result = resampler.resample(&src, 500, 500)?;
69//! println!("Resampled with {:?}: {}x{}", method, result.width(), result.height());
70//! }
71//! # Ok(())
72//! # }
73//! ```
74//!
75//! # Raster Calculations
76//!
77//! The raster calculator enables map algebra operations using expressions.
78//!
79//! ## Example: Simple Arithmetic
80//!
81//! ```rust,ignore
82//! use oxigdal_algorithms::raster::calculator::RasterCalculator;
83//! use oxigdal_core::buffer::RasterBuffer;
84//! use oxigdal_core::types::RasterDataType;
85//!
86//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
87//! let mut calc = RasterCalculator::new();
88//!
89//! // Add input rasters
90//! let raster_a = RasterBuffer::zeros(100, 100, RasterDataType::Float32);
91//! let raster_b = RasterBuffer::zeros(100, 100, RasterDataType::Float32);
92//!
93//! calc.add_raster("A", raster_a);
94//! calc.add_raster("B", raster_b);
95//!
96//! // Compute: (A + B) / 2
97//! let result = calc.evaluate("(A + B) / 2")?;
98//! # Ok(())
99//! # }
100//! ```
101//!
102//! ## Example: NDVI Calculation
103//!
104//! ```rust,ignore
105//! use oxigdal_algorithms::raster::calculator::RasterCalculator;
106//! use oxigdal_core::buffer::RasterBuffer;
107//! use oxigdal_core::types::RasterDataType;
108//!
109//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
110//! let mut calc = RasterCalculator::new();
111//!
112//! // Add NIR and Red bands
113//! let nir = RasterBuffer::zeros(512, 512, RasterDataType::Float32);
114//! let red = RasterBuffer::zeros(512, 512, RasterDataType::Float32);
115//!
116//! calc.add_raster("NIR", nir);
117//! calc.add_raster("RED", red);
118//!
119//! // NDVI = (NIR - RED) / (NIR + RED)
120//! let ndvi = calc.evaluate("(NIR - RED) / (NIR + RED)")?;
121//! # Ok(())
122//! # }
123//! ```
124//!
125//! ## Example: Complex Expression
126//!
127//! ```rust,ignore
128//! use oxigdal_algorithms::raster::calculator::RasterCalculator;
129//! use oxigdal_core::buffer::RasterBuffer;
130//! use oxigdal_core::types::RasterDataType;
131//!
132//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
133//! let mut calc = RasterCalculator::new();
134//!
135//! let dem = RasterBuffer::zeros(1000, 1000, RasterDataType::Float32);
136//! calc.add_raster("DEM", dem);
137//!
138//! // Hillshade formula with azimuth and altitude
139//! let expr = "sin(30 * 3.14159 / 180) - cos(30 * 3.14159 / 180) * cos(DEM)";
140//! let result = calc.evaluate(expr)?;
141//! # Ok(())
142//! # }
143//! ```
144//!
145//! # Terrain Analysis
146//!
147//! OxiGDAL provides comprehensive terrain analysis tools for digital elevation models (DEMs).
148//!
149//! ## Hillshade Generation
150//!
151//! ```rust,ignore
152//! use oxigdal_algorithms::raster::hillshade::Hillshade;
153//! use oxigdal_core::buffer::RasterBuffer;
154//! use oxigdal_core::types::RasterDataType;
155//!
156//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
157//! let dem = RasterBuffer::zeros(1000, 1000, RasterDataType::Float32);
158//!
159//! // Generate hillshade with default parameters
160//! // (azimuth=315°, altitude=45°, z-factor=1.0)
161//! let hillshade = Hillshade::new()
162//! .azimuth(315.0)
163//! .altitude(45.0)
164//! .z_factor(1.0)
165//! .compute(&dem, 30.0)?; // 30m cell size
166//! # Ok(())
167//! # }
168//! ```
169//!
170//! ## Slope and Aspect
171//!
172//! ```rust,ignore
173//! use oxigdal_algorithms::raster::slope_aspect::{SlopeAspect, SlopeUnit};
174//! use oxigdal_core::buffer::RasterBuffer;
175//! use oxigdal_core::types::RasterDataType;
176//!
177//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
178//! let dem = RasterBuffer::zeros(1000, 1000, RasterDataType::Float32);
179//!
180//! // Compute slope in degrees
181//! let sa = SlopeAspect::new();
182//! let slope = sa.slope(&dem, 30.0, SlopeUnit::Degrees)?;
183//!
184//! // Compute aspect in degrees (0-360)
185//! let aspect = sa.aspect(&dem, 30.0)?;
186//! # Ok(())
187//! # }
188//! ```
189//!
190//! ## Zonal Statistics
191//!
192//! ```rust,ignore
193//! use oxigdal_algorithms::raster::zonal_stats::{ZonalStats, ZonalStatistic};
194//! use oxigdal_core::buffer::RasterBuffer;
195//! use oxigdal_core::types::RasterDataType;
196//!
197//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
198//! let values = RasterBuffer::zeros(1000, 1000, RasterDataType::Float32);
199//! let zones = RasterBuffer::zeros(1000, 1000, RasterDataType::UInt8);
200//!
201//! let stats = ZonalStats::new()
202//! .add_statistic(ZonalStatistic::Mean)
203//! .add_statistic(ZonalStatistic::StdDev)
204//! .add_statistic(ZonalStatistic::Min)
205//! .add_statistic(ZonalStatistic::Max)
206//! .compute(&values, &zones)?;
207//!
208//! for (zone_id, zone_stats) in stats.iter() {
209//! println!("Zone {}: mean={}", zone_id, zone_stats.mean);
210//! }
211//! # Ok(())
212//! # }
213//! ```
214//!
215//! # Vector Operations
216//!
217//! ## Buffer Generation
218//!
219//! ```rust,ignore
220//! use oxigdal_algorithms::vector::buffer::VectorBuffer;
221//! use oxigdal_core::vector::Geometry;
222//!
223//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
224//! // Create point geometry
225//! let point = Geometry::Point { x: 0.0, y: 0.0 };
226//!
227//! // Create 100-unit buffer around point
228//! let buffer = VectorBuffer::new()
229//! .distance(100.0)
230//! .segments(32) // Number of segments per quadrant
231//! .compute(&point)?;
232//! # Ok(())
233//! # }
234//! ```
235//!
236//! ## Line Simplification
237//!
238//! ```rust,ignore
239//! use oxigdal_algorithms::vector::douglas_peucker::DouglasPeucker;
240//! use oxigdal_core::vector::Geometry;
241//!
242//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
243//! // Create line with many points
244//! let coords = vec![
245//! (0.0, 0.0),
246//! (1.0, 1.0),
247//! (2.0, 0.5),
248//! (3.0, 1.5),
249//! (4.0, 0.0),
250//! ];
251//!
252//! let line = Geometry::LineString {
253//! coords: coords.clone(),
254//! };
255//!
256//! // Simplify with tolerance of 0.5 units
257//! let simplified = DouglasPeucker::new()
258//! .tolerance(0.5)
259//! .simplify(&line)?;
260//! # Ok(())
261//! # }
262//! ```
263//!
264//! ## Geometric Predicates
265//!
266//! ```rust,ignore
267//! use oxigdal_algorithms::vector::intersection::Intersection;
268//! use oxigdal_core::vector::Geometry;
269//!
270//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
271//! let poly1 = Geometry::Polygon {
272//! exterior: vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0), (0.0, 0.0)],
273//! holes: vec![],
274//! };
275//!
276//! let poly2 = Geometry::Polygon {
277//! exterior: vec![(5.0, 5.0), (15.0, 5.0), (15.0, 15.0), (5.0, 15.0), (5.0, 5.0)],
278//! holes: vec![],
279//! };
280//!
281//! // Compute intersection
282//! let result = Intersection::compute(&poly1, &poly2)?;
283//! # Ok(())
284//! # }
285//! ```
286//!
287//! # SIMD Optimization
288//!
289//! Enable SIMD optimizations for maximum performance on modern CPUs.
290//!
291//! ## Enabling SIMD
292//!
293//! Add to your `Cargo.toml`:
294//!
295//! ```toml
296//! oxigdal-algorithms = { version = "0.1", features = ["simd"] }
297//! ```
298//!
299//! ## SIMD-Accelerated Operations
300//!
301//! Many operations automatically use SIMD when enabled:
302//!
303//! ```rust
304//! use oxigdal_algorithms::resampling::{ResamplingMethod, Resampler};
305//! use oxigdal_core::buffer::RasterBuffer;
306//! use oxigdal_core::types::RasterDataType;
307//!
308//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
309//! let src = RasterBuffer::zeros(4096, 4096, RasterDataType::Float32);
310//!
311//! // This will use SIMD instructions when the "simd" feature is enabled
312//! let resampler = Resampler::new(ResamplingMethod::Bilinear);
313//! let dst = resampler.resample(&src, 2048, 2048)?;
314//! # Ok(())
315//! # }
316//! ```
317//!
318//! ## Available SIMD Operations
319//!
320//! - Element-wise arithmetic (add, subtract, multiply, divide)
321//! - Resampling (bilinear, bicubic)
322//! - Convolution filters
323//! - Statistical reductions (min, max, sum, mean)
324//! - Color space transformations
325//!
326//! # Parallel Processing
327//!
328//! Process large rasters efficiently using parallel tile processing.
329//!
330//! ## Parallel Tile Processing
331//!
332//! ```rust,ignore
333//! use oxigdal_algorithms::parallel::tiles::TileProcessor;
334//! use oxigdal_core::buffer::RasterBuffer;
335//! use oxigdal_core::types::RasterDataType;
336//!
337//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
338//! let input = RasterBuffer::zeros(8192, 8192, RasterDataType::Float32);
339//!
340//! // Process in 512x512 tiles using all CPU cores
341//! let processor = TileProcessor::new()
342//! .tile_size(512, 512)
343//! .overlap(32); // 32-pixel overlap to avoid edge artifacts
344//!
345//! let result = processor.process(&input, |tile| {
346//! // Process each tile independently
347//! // This closure runs in parallel
348//! tile.clone()
349//! })?;
350//! # Ok(())
351//! # }
352//! ```
353//!
354//! ## Parallel Batch Processing
355//!
356//! ```rust,ignore
357//! use oxigdal_algorithms::parallel::batch::BatchProcessor;
358//! use oxigdal_core::buffer::RasterBuffer;
359//! use oxigdal_core::types::RasterDataType;
360//!
361//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
362//! // Process multiple rasters in parallel
363//! let rasters: Vec<RasterBuffer> = (0..10)
364//! .map(|_| RasterBuffer::zeros(1000, 1000, RasterDataType::Float32))
365//! .collect();
366//!
367//! let processor = BatchProcessor::new();
368//! let results = processor.process_batch(&rasters, |raster| {
369//! // Process each raster independently
370//! raster.compute_statistics()
371//! })?;
372//! # Ok(())
373//! # }
374//! ```
375//!
376//! ## Controlling Parallelism
377//!
378//! ```rust,ignore
379//! use oxigdal_algorithms::parallel::raster::ParallelRaster;
380//! use rayon::ThreadPoolBuilder;
381//!
382//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
383//! // Limit to 4 threads
384//! let pool = ThreadPoolBuilder::new()
385//! .num_threads(4)
386//! .build()?;
387//!
388//! pool.install(|| {
389//! // All parallel operations inside this closure
390//! // will use at most 4 threads
391//! });
392//! # Ok(())
393//! # }
394//! ```
395//!
396//! # Best Practices
397//!
398//! ## Choose the Right Resampling Method
399//!
400//! - **Categorical data** (land cover, classification): Use NearestNeighbor
401//! - **Continuous data** (elevation, temperature): Use Bilinear or Bicubic
402//! - **High-quality imagery**: Use Lanczos
403//! - **Performance-critical**: Use NearestNeighbor or Bilinear
404//!
405//! ## Optimize Tile Size
406//!
407//! - Too small: High overhead from thread management
408//! - Too large: Poor parallelization and high memory usage
409//! - Sweet spot: 256-1024 pixels per side, depending on data type
410//!
411//! ## Use SIMD Features
412//!
413//! Always enable SIMD for production workloads:
414//!
415//! ```toml
416//! [dependencies]
417//! oxigdal-algorithms = { version = "0.1", features = ["simd", "parallel"] }
418//! ```
419//!
420//! ## Handle NoData Properly
421//!
422//! Always set and respect nodata values:
423//!
424//! ```rust
425//! use oxigdal_core::buffer::RasterBuffer;
426//! use oxigdal_core::types::{RasterDataType, NoDataValue};
427//!
428//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
429//! let nodata = NoDataValue::Float(-9999.0);
430//! let buffer = RasterBuffer::nodata_filled(
431//! 1000,
432//! 1000,
433//! RasterDataType::Float32,
434//! nodata
435//! );
436//!
437//! // Operations automatically handle nodata
438//! let stats = buffer.compute_statistics()?;
439//! println!("Valid pixels: {} / {}", stats.valid_count, buffer.pixel_count());
440//! # Ok(())
441//! # }
442//! ```