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}