gistools/readers/geotiff/
mod.rs

1/// GeoTIFF Color tools
2mod color;
3/// GeoTIFF Constants
4mod constants;
5/// Decoding tools
6mod decoder;
7/// Header tools
8mod header;
9/// GeoTIFF Image tools
10mod image;
11/// GeoTIFF Image utility tools
12mod image_util;
13/// Predictors
14mod predictor;
15/// Projection Params
16mod proj_params;
17
18use crate::{
19    parsers::{FeatureReader, RGBA, Reader},
20    proj::Transformer,
21};
22use alloc::{collections::BTreeMap, rc::Rc, string::String};
23pub use color::*;
24pub use constants::*;
25use core::cell::RefCell;
26pub use header::*;
27pub use image::*;
28pub use image_util::*;
29pub use predictor::*;
30pub use proj_params::*;
31use s2json::{Properties, VectorFeature};
32
33/// An GeoTIFF Shaped Vector Feature
34pub type GeoTIFFVectorFeature = VectorFeature<GeoTIFFMetadata, Properties, RGBA>;
35
36/// Options for the GeoTIFF Reader
37#[derive(Debug, Default, Clone)]
38pub struct GeoTIFFOptions {
39    /// List of EPSG codes to utilize e.g. `{ "4326": "WKT_STRING" }``
40    pub epsg_codes: BTreeMap<String, String>,
41}
42
43/// # GeoTIFF Reader
44///
45/// ## Description
46/// This class reads a GeoTIFF file and returns a list of GeoTIFF images.
47///
48/// Implements the [`FeatureReader`] trait
49///
50/// ## Usage
51///
52/// The methods you have access to:
53/// - [`GeoTIFFReader::new`]: Create a new GeoTIFFReader
54/// - [`GeoTIFFReader::len`]: Get the length (number of features)
55/// - [`GeoTIFFReader::is_empty`]: Check if the reader is empty
56/// - [`GeoTIFFReader::get_image`]: Get an image at index
57/// - [`GeoTIFFReader::iter`]: Iterate over the features
58/// - [`GeoTIFFReader::par_iter`]: Iterate over the features
59///
60/// ### File Reader
61/// ```rust
62/// use gistools::{
63///     parsers::{FeatureReader, FileReader},
64///     readers::{GeoTIFFReader, GeoTIFFOptions},
65/// };
66/// use std::path::PathBuf;
67///
68/// let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
69/// path.push("tests/readers/geotiff/fixtures/ycbcr.tif");
70/// let geotiff = GeoTIFFReader::new(FileReader::from(path), None);
71///
72/// {
73///     // you can pull in the image directly
74///     let mut image = geotiff.get_image(None).unwrap();
75///     // read the raster data as any data format
76///     let raster = image.raster_data(None);
77///     // or read in the RGB data as RGBA u8 linear encoded
78///     let rgb = image.get_rgba();
79/// }
80///
81/// // Or you can just pull in the all the grids
82/// let grids: Vec<_> = geotiff.iter().collect();
83/// assert_eq!(grids.len(), 1);
84/// ```
85///
86/// ### Buffer Reader
87/// ```rust
88/// use gistools::{
89///     parsers::{FeatureReader, BufferReader},
90///     readers::{GeoTIFFReader, GeoTIFFOptions},
91/// };
92///
93/// // Ignore this, used to setup example
94/// use std::path::PathBuf;
95/// let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
96/// path.push("tests/readers/geotiff/fixtures/ycbcr.tif");
97/// let bytes: Vec<u8> = std::fs::read(path).unwrap();
98///
99/// // read in the data and build the grid
100/// let geotiff = GeoTIFFReader::new(BufferReader::from(bytes), None);
101/// let grid: Vec<_> = geotiff.iter().collect();
102/// assert_eq!(grid.len(), 1);
103/// ```
104///
105/// ## Links
106/// - <https://www.ogc.org/publications/standard/geotiff/>
107/// - <https://download.osgeo.org/geotiff/spec/tiff6.pdf>
108/// - <https://geospatialworld.net/article/geotiff-a-standard-image-file-format-for-gis-applications/>
109/// - <https://docs.ogc.org/is/19-008r4/19-008r4.html>
110/// - <https://gitlab.com/libtiff/libtiff>
111#[derive(Debug, Clone, Default, PartialEq)]
112pub struct GeoTIFFReader<T: Reader> {
113    reader: Rc<RefCell<T>>,
114    /// The GeoTIFF header
115    pub header: GeoTIFFHeaderReader,
116    transform: Rc<RefCell<Transformer>>,
117}
118impl<T: Reader> GeoTIFFReader<T> {
119    /// Create a new GeoTIFFReader
120    pub fn new(reader: T, options: Option<GeoTIFFOptions>) -> GeoTIFFReader<T> {
121        let header = GeoTIFFHeaderReader::new(&reader);
122        let options = options.unwrap_or_default();
123        let mut transform = Transformer::new();
124        for (epsg_code, wkt) in options.epsg_codes.iter() {
125            transform.insert_epsg_code(epsg_code.clone(), wkt.clone());
126        }
127        GeoTIFFReader {
128            reader: Rc::new(RefCell::new(reader)),
129            header,
130            transform: Rc::new(RefCell::new(transform)),
131        }
132    }
133
134    /// Check if the reader is empty
135    pub fn is_empty(&self) -> bool {
136        self.header.image_directories.is_empty()
137    }
138
139    /// Get the length of internal subfiles
140    pub fn len(&self) -> usize {
141        self.header.image_directories.len()
142    }
143
144    /// Get the nth internal subfile of an image. By default, the first is returned.
145    ///
146    /// ## Parameters
147    /// - `index`: the index of the image to get [Default=0]
148    ///
149    /// ## Returns
150    /// The image at the given index
151    pub fn get_image(&self, index: Option<usize>) -> Option<GeoTIFFImage<T>> {
152        let index = index.unwrap_or(0);
153        if index >= self.len() {
154            None
155        } else {
156            Some(GeoTIFFImage::new(
157                self.reader.clone(),
158                self.header.image_directories[index].clone(),
159                self.transform.clone(),
160                self.header.little_endian,
161            ))
162        }
163    }
164}
165
166/// The GeoTIFF Iterator tool
167#[derive(Debug)]
168pub struct GeoTIFFIterator<'a, T: Reader> {
169    reader: &'a GeoTIFFReader<T>,
170    index: usize,
171    end: usize,
172}
173impl<T: Reader> Iterator for GeoTIFFIterator<'_, T> {
174    type Item = GeoTIFFVectorFeature;
175
176    fn next(&mut self) -> Option<Self::Item> {
177        if self.index >= self.end {
178            None
179        } else if let Some(mut point) = self.reader.get_image(Some(self.index)) {
180            self.index += 1;
181            Some(point.get_multi_point_vector())
182        } else {
183            None
184        }
185    }
186}
187/// A feature reader trait with a callback-based approach
188impl<T: Reader> FeatureReader<GeoTIFFMetadata, Properties, RGBA> for GeoTIFFReader<T> {
189    type FeatureIterator<'a>
190        = GeoTIFFIterator<'a, T>
191    where
192        T: 'a;
193
194    fn iter(&self) -> Self::FeatureIterator<'_> {
195        GeoTIFFIterator { reader: self, index: 0, end: self.len() }
196    }
197
198    fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
199        let start = self.len() * thread_id / pool_size;
200        let end = self.len() * (thread_id + 1) / pool_size;
201        GeoTIFFIterator { reader: self, index: start, end }
202    }
203}