gistools/readers/geotiff/
header.rs1use super::constants::GeoTIFFTypes;
3use crate::{
4 parsers::Reader,
5 readers::{GeoKeyDirectoryKeys, GeoStore},
6};
7use alloc::vec::Vec;
8
9#[derive(Debug, Clone, Default, PartialEq)]
11struct KeyValue {
12 key: u16,
13 value: Vec<u8>,
14 field_type: GeoTIFFTypes,
15}
16
17#[derive(Debug, Clone, Copy, Default, PartialEq)]
19pub struct GeoTiePoint {
20 pub i: f64,
22 pub j: f64,
24 pub k: f64,
26 pub x: f64,
28 pub y: f64,
30 pub z: f64,
32}
33
34#[derive(Debug, Clone, Copy, Default, PartialEq)]
36pub struct GeoPixelScale {
37 pub x: f64,
39 pub y: f64,
41 pub z: f64,
43}
44
45#[derive(Debug, Clone, Default, PartialEq)]
47pub struct ImageDirectory {
48 pub geo_key_directory: GeoStore,
50 pub pixel_scale: GeoPixelScale,
52 pub tie_point: GeoTiePoint,
54 pub variables: GeoStore,
56}
57impl ImageDirectory {
58 pub fn insert(&mut self, key: u16, value: Vec<u8>, field_type: GeoTIFFTypes) {
60 self.variables.insert(key, value, field_type);
61 }
62 pub fn len(&self) -> usize {
64 self.variables.len() + self.geo_key_directory.len()
65 }
66 pub fn is_empty(&self) -> bool {
68 self.geo_key_directory.is_empty() && self.variables.is_empty()
69 }
70}
71
72#[derive(Debug, Clone, Default, PartialEq)]
74pub struct GeoTIFFHeaderReader {
75 pub little_endian: bool,
77 pub big_tiff: bool,
79 pub image_directories: Vec<ImageDirectory>,
81}
82impl GeoTIFFHeaderReader {
83 pub fn new<T: Reader>(reader: &T) -> GeoTIFFHeaderReader {
85 let mut tiff_reader = GeoTIFFHeaderReader {
86 little_endian: true,
87 big_tiff: false,
88 image_directories: Vec::new(),
89 };
90 tiff_reader.parse_header(reader);
91
92 tiff_reader
93 }
94
95 fn parse_header<T: Reader>(&mut self, reader: &T) {
97 let bom = reader.uint16_be(Some(0));
99 if bom == 0x4949 {
100 self.little_endian = true;
101 } else if bom == 0x4d4d {
102 self.little_endian = false;
103 } else {
104 panic!("Invalid byte order value.");
105 }
106 let le = self.little_endian;
107
108 let magic_number = reader.uint16(Some(2), Some(le));
109 if magic_number == 42 {
110 self.big_tiff = false;
111 } else if magic_number == 43 {
112 self.big_tiff = true;
113 let offset_byte_size = reader.uint16(Some(4), Some(le));
114 if offset_byte_size != 8 {
115 panic!("Unsupported offset byte-size.");
116 }
117 } else {
118 panic!("Invalid magic number.");
119 }
120
121 let first_ifd_offset = if self.big_tiff {
122 reader.uint64(Some(8), Some(le))
123 } else {
124 reader.uint32(Some(4), Some(le)) as u64
125 };
126
127 self.get_image_metadata(first_ifd_offset, reader);
128 }
129
130 fn get_image_metadata<T: Reader>(&mut self, first_offset: u64, reader: &T) {
138 let GeoTIFFHeaderReader { big_tiff, little_endian, .. } = *self;
139 let entry_size = if big_tiff { 20 } else { 12 };
140 let offset_size = if big_tiff { 8 } else { 2 };
141 let mut offset = first_offset;
142
143 let mut ifd_offset = first_offset;
144 while ifd_offset != 0 {
145 let mut ifd = ImageDirectory::default();
146 let num_dir_entries = self.read_tag(offset, reader);
147
148 let mut i = offset + offset_size;
149 let mut geokey_dir_offset: Option<u64> = None;
150 let mut prev_tag = 0;
151 for _ in 0..num_dir_entries {
152 let field_tag = if little_endian {
153 reader.uint16_le(Some(i))
154 } else {
155 reader.uint16_be(Some(i))
156 };
157 if field_tag < prev_tag {
158 panic!("Invalid IFD, {} < {}", field_tag, prev_tag);
159 }
160 prev_tag = field_tag;
161 if field_tag == 33550 {
162 ifd.pixel_scale = self.get_pixel_scale(i, reader);
164 } else if field_tag == 33922 {
165 ifd.tie_point = self.get_tiepoint(i, reader);
167 } else if field_tag == 34735 {
168 geokey_dir_offset = Some(i);
170 }
171 else {
176 let KeyValue { key, value, field_type } =
177 self.get_key_value(field_tag as u64, i, reader);
178 ifd.insert(key, value, field_type);
179 }
180 i += entry_size;
181 }
182 if let Some(geokey_dir_offset) = geokey_dir_offset {
184 self.get_geo_key_directory(&mut ifd, geokey_dir_offset, reader);
185 } else {
186 }
188 if !ifd.is_empty() {
189 self.image_directories.push(ifd);
190 } else {
191 break;
192 }
193 offset += offset_size + entry_size * num_dir_entries;
195 ifd_offset = self.read_tag(offset, reader);
196 offset += offset_size;
197 }
198 }
199
200 fn read_tag<T: Reader>(&mut self, offset: u64, reader: &T) -> u64 {
208 let Self { big_tiff, little_endian, .. } = self;
209 if *big_tiff {
210 reader.uint64(Some(offset), Some(*little_endian))
211 } else {
212 reader.uint16(Some(offset), Some(*little_endian)) as u64
213 }
214 }
215
216 fn read_offset<T: Reader>(&mut self, offset: u64, reader: &T) -> u64 {
224 let Self { big_tiff, little_endian, .. } = self;
225 if *big_tiff {
226 reader.uint64(Some(offset), Some(*little_endian))
227 } else {
228 reader.uint32(Some(offset), Some(*little_endian)) as u64
229 }
230 }
231
232 fn get_pixel_scale<T: Reader>(&mut self, offset: u64, reader: &T) -> GeoPixelScale {
240 let Self { little_endian, big_tiff, .. } = *self;
241 let field_type = reader.uint16(Some(offset + 2), Some(little_endian));
242 if field_type != 12 {
243 panic!("Invalid GeoKeyDirectory type {}", field_type);
244 }
245 let num_keys = self.read_offset(offset + 4, reader);
246 if num_keys != 3 {
247 panic!("Invalid GeoKeyDirectory num_keys {}", num_keys);
248 }
249 let value_offset = self.read_offset(offset + if big_tiff { 12 } else { 8 }, reader);
250
251 GeoPixelScale {
252 x: reader.f64(Some(value_offset), Some(little_endian)),
253 y: reader.f64(Some(value_offset + 8), Some(little_endian)),
254 z: reader.f64(Some(value_offset + 16), Some(little_endian)),
255 }
256 }
257
258 fn get_geo_key_directory<T: Reader>(
269 &mut self,
270 ifd: &mut ImageDirectory,
271 offset: u64,
272 reader: &T,
273 ) {
274 let file_dir = &mut ifd.variables;
275 let num_keys = self.read_offset(offset + 4, reader);
276 let value_offset = self.read_offset(offset + (if self.big_tiff { 12 } else { 8 }), reader);
277 let raw_geokeys = reader.slice(Some(value_offset), Some(value_offset + num_keys * 2));
278 let raw_geokeys: Vec<u16> = raw_geokeys
279 .chunks_exact(2)
280 .map(|chunk| u16::from_le_bytes(chunk.try_into().unwrap()))
281 .collect();
282 let geo_key_directory = parse_geotiff_raw_geokeys(&raw_geokeys, file_dir);
283 if !geo_key_directory.has(GeoKeyDirectoryKeys::GTModelTypeGeoKey as u16) {
285 panic!("Missing \"GTModelTypeGeoKey\" in GeoKeyDirectory");
286 }
287
288 ifd.geo_key_directory = geo_key_directory;
289 }
290
291 fn get_tiepoint<T: Reader>(&mut self, offset: u64, reader: &T) -> GeoTiePoint {
299 let Self { big_tiff, little_endian, .. } = *self;
300 let field_type = reader.uint16(Some(offset + 2), Some(little_endian));
302 if field_type != 12 {
303 panic!("Invalid TiepointTag type ${field_type}");
304 }
305 let mut tie_point = GeoTiePoint::default();
306 let count = self.read_offset(offset + 4, reader);
308 let value_offset = self.read_offset(offset + if big_tiff { 12 } else { 8 }, reader);
310 for i in 0..count {
311 let val = reader.f64(Some(value_offset + i * 8), Some(little_endian));
312 match i {
313 0 => tie_point.i = val,
314 1 => tie_point.j = val,
315 2 => tie_point.k = val,
316 3 => tie_point.x = val,
317 4 => tie_point.y = val,
318 5 => tie_point.z = val,
319 _ => panic!("Invalid TiepointTag index {}", i),
320 }
321 }
322
323 tie_point
324 }
325
326 fn get_key_value<T: Reader>(&mut self, field_tag: u64, offset: u64, reader: &T) -> KeyValue {
335 let field_type = reader.uint16(Some(offset + 2), Some(self.little_endian));
336 let type_count = self.read_offset(offset + 4, reader);
337 let field_type_length = GeoTIFFTypes::from(field_type).to_size();
338 let value_offset = offset + (if self.big_tiff { 12 } else { 8 });
339 let actual_offset =
340 if (field_type_length as u64) * type_count <= (if self.big_tiff { 8 } else { 4 }) {
341 value_offset
342 } else {
343 self.read_offset(value_offset, reader)
344 };
345 let value = self.get_value(
346 field_tag as usize,
347 field_type.into(),
348 type_count,
349 actual_offset,
350 reader,
351 );
352
353 KeyValue { key: field_tag as u16, value, field_type: field_type.into() }
355 }
356
357 fn get_value<T: Reader>(
368 &self,
369 _field_tag: usize,
370 field_type: GeoTIFFTypes,
371 type_count: u64,
372 value_offset: u64,
373 reader: &T,
374 ) -> Vec<u8> {
375 match field_type {
376 GeoTIFFTypes::ASCII
377 | GeoTIFFTypes::BYTE
378 | GeoTIFFTypes::UNDEFINED
379 | GeoTIFFTypes::SBYTE => {
380 reader.slice(Some(value_offset), Some(value_offset + type_count))
381 }
382 GeoTIFFTypes::SHORT | GeoTIFFTypes::SSHORT => {
383 reader.slice(Some(value_offset), Some(value_offset + type_count * 2))
384 }
385 GeoTIFFTypes::LONG | GeoTIFFTypes::SLONG | GeoTIFFTypes::FLOAT | GeoTIFFTypes::IFD => {
386 reader.slice(Some(value_offset), Some(value_offset + type_count * 4))
387 }
388 GeoTIFFTypes::RATIONAL | GeoTIFFTypes::SRATIONAL | GeoTIFFTypes::DOUBLE => {
389 reader.slice(Some(value_offset), Some(value_offset + type_count * 8))
390 }
391 GeoTIFFTypes::LONG8 | GeoTIFFTypes::SLONG8 | GeoTIFFTypes::IFD8 => {
392 reader.slice(Some(value_offset), Some(value_offset + type_count * 8))
393 }
394 }
395 }
396}
397
398pub fn parse_geotiff_raw_geokeys(raw_geokeys: &[u16], file_dir: &GeoStore) -> GeoStore {
407 let mut geo_key_directory = GeoStore::default();
408 let mut i = 4;
409 let geo_key_count = raw_geokeys[3] as usize * 4;
410 while i <= geo_key_count {
411 let key = raw_geokeys[i];
412 let location = raw_geokeys[i + 1];
413 let count = raw_geokeys[i + 2] as usize;
414 let offset = raw_geokeys[i + 3];
415
416 if location == 0 {
417 geo_key_directory.set_short(key, offset as i16);
418 } else if let Some((value, _)) = file_dir.get(location) {
419 let offset = offset as usize;
420 geo_key_directory.set(
421 key,
422 value[offset..(offset + count)].to_vec(),
423 GeoTIFFTypes::BYTE,
424 );
425 }
426
427 i += 4;
428 }
429
430 geo_key_directory
431}