Skip to content

Commit 6c92598

Browse files
authored
Move endianness check into AsyncCursor constructor (#13)
* Move endianness check into AsyncCursor constructor * whitespace * shell code
1 parent 8101064 commit 6c92598

File tree

7 files changed

+104
-69
lines changed

7 files changed

+104
-69
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ It additionally exposes geospatial-specific TIFF tag metadata.
1212

1313
Download the following file for use in the tests.
1414

15-
```
15+
```shell
1616
aws s3 cp s3://naip-visualization/ny/2022/60cm/rgb/40073/m_4007307_sw_18_060_20220803.tif ./ --request-payer
1717
```

src/async_reader.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use crate::error::{AiocogeoError, Result};
1111

1212
/// The asynchronous interface used to read COG files
1313
///
14-
/// This was derived from the Parquet `AsyncFileReader`:
15-
/// https://docs.rs/parquet/latest/parquet/arrow/async_reader/trait.AsyncFileReader.html
14+
/// This was derived from the Parquet
15+
/// [`AsyncFileReader`](https://docs.rs/parquet/latest/parquet/arrow/async_reader/trait.AsyncFileReader.html)
1616
///
1717
/// Notes:
1818
///
@@ -143,22 +143,42 @@ macro_rules! impl_read_byteorder {
143143
}
144144

145145
impl AsyncCursor {
146-
pub(crate) fn new(reader: Box<dyn AsyncFileReader>) -> Self {
146+
/// Create a new AsyncCursor from a reader and endianness.
147+
pub(crate) fn new(reader: Box<dyn AsyncFileReader>, endianness: Endianness) -> Self {
147148
Self {
148149
reader,
149150
offset: 0,
150-
endianness: Default::default(),
151+
endianness,
151152
}
152153
}
153154

154-
pub(crate) fn set_endianness(&mut self, endianness: Endianness) {
155-
self.endianness = endianness;
155+
/// Create a new AsyncCursor for a TIFF file, automatically inferring endianness from the first
156+
/// two bytes.
157+
pub(crate) async fn try_open_tiff(reader: Box<dyn AsyncFileReader>) -> Result<Self> {
158+
// Initialize with default endianness and then set later
159+
let mut cursor = Self::new(reader, Default::default());
160+
let magic_bytes = cursor.read(2).await;
161+
162+
// Should be b"II" for little endian or b"MM" for big endian
163+
if magic_bytes == Bytes::from_static(b"II") {
164+
cursor.endianness = Endianness::LittleEndian;
165+
} else if magic_bytes == Bytes::from_static(b"MM") {
166+
cursor.endianness = Endianness::BigEndian;
167+
} else {
168+
return Err(AiocogeoError::General(format!(
169+
"unexpected magic bytes {magic_bytes:?}"
170+
)));
171+
};
172+
173+
Ok(cursor)
156174
}
157175

176+
/// Consume self and return the underlying [`AsyncFileReader`].
158177
pub(crate) fn into_inner(self) -> Box<dyn AsyncFileReader> {
159178
self.reader
160179
}
161180

181+
/// Read the given number of bytes, advancing the internal cursor state by the same amount.
162182
pub(crate) async fn read(&mut self, length: usize) -> Bytes {
163183
let range = self.offset as _..(self.offset + length) as _;
164184
self.offset += length;

src/cog.rs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use bytes::Bytes;
2-
3-
use crate::async_reader::{AsyncCursor, Endianness};
1+
use crate::async_reader::AsyncCursor;
42
use crate::error::Result;
53
use crate::ifd::ImageFileDirectories;
64
use crate::AsyncFileReader;
@@ -13,17 +11,7 @@ pub struct COGReader {
1311

1412
impl COGReader {
1513
pub async fn try_open(reader: Box<dyn AsyncFileReader>) -> Result<Self> {
16-
let mut cursor = AsyncCursor::new(reader);
17-
let magic_bytes = cursor.read(2).await;
18-
// Should be b"II" for little endian or b"MM" for big endian
19-
if magic_bytes == Bytes::from_static(b"II") {
20-
cursor.set_endianness(Endianness::LittleEndian);
21-
} else if magic_bytes == Bytes::from_static(b"MM") {
22-
cursor.set_endianness(Endianness::BigEndian);
23-
} else {
24-
panic!("unexpected magic bytes {magic_bytes:?}");
25-
}
26-
14+
let mut cursor = AsyncCursor::try_open_tiff(reader).await?;
2715
let version = cursor.read_u16().await;
2816

2917
// Assert it's a standard non-big tiff
@@ -72,9 +60,12 @@ mod test {
7260
let path = object_store::path::Path::parse("m_4007307_sw_18_060_20220803.tif").unwrap();
7361
let store = Arc::new(LocalFileSystem::new_with_prefix(folder).unwrap());
7462
let reader = ObjectReader::new(store, path);
63+
7564
let cog_reader = COGReader::try_open(Box::new(reader.clone())).await.unwrap();
76-
let ifd = &cog_reader.ifds.as_ref()[4];
77-
dbg!(ifd.compression);
65+
66+
let ifd = &cog_reader.ifds.as_ref()[1];
67+
// dbg!(ifd.geotransform());
68+
dbg!(ifd);
7869
let tile = ifd.get_tile(0, 0, Box::new(reader)).await.unwrap();
7970
std::fs::write("img.buf", tile).unwrap();
8071
// dbg!(tile.len());

src/geo/affine.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/// Affine transformation values.
12
#[derive(Debug)]
23
pub struct AffineTransform(f64, f64, f64, f64, f64, f64);
34

src/geo/geo_key_directory.rs

Lines changed: 65 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
66
use tiff::decoder::ifd::Value;
77
use tiff::{TiffError, TiffResult};
88

9+
/// Geospatial TIFF tag variants
910
#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive, Eq, Hash)]
1011
#[repr(u16)]
1112
pub enum GeoKeyTag {
@@ -63,60 +64,75 @@ pub enum GeoKeyTag {
6364
VerticalUnits = 4099,
6465
}
6566

66-
/// http://docs.opengeospatial.org/is/19-008r4/19-008r4.html#_requirements_class_geokeydirectorytag
67+
/// Metadata defined by the GeoTIFF standard.
68+
///
69+
/// <http://docs.opengeospatial.org/is/19-008r4/19-008r4.html#_requirements_class_geokeydirectorytag>
6770
#[derive(Debug, Clone)]
6871
pub struct GeoKeyDirectory {
69-
model_type: Option<u16>,
70-
raster_type: Option<u16>,
71-
citation: Option<String>,
72+
pub model_type: Option<u16>,
73+
pub raster_type: Option<u16>,
74+
pub citation: Option<String>,
7275

73-
geographic_type: Option<u16>,
74-
geog_citation: Option<String>,
75-
geog_geodetic_datum: Option<u16>,
76-
geog_prime_meridian: Option<u16>,
77-
geog_linear_units: Option<u16>,
78-
geog_linear_unit_size: Option<f64>,
79-
geog_angular_units: Option<u16>,
80-
geog_angular_unit_size: Option<f64>,
81-
geog_ellipsoid: Option<u16>,
82-
geog_semi_major_axis: Option<f64>,
83-
geog_semi_minor_axis: Option<f64>,
84-
geog_inv_flattening: Option<f64>,
85-
geog_azimuth_units: Option<u16>,
86-
geog_prime_meridian_long: Option<f64>,
76+
pub geographic_type: Option<u16>,
77+
pub geog_citation: Option<String>,
78+
pub geog_geodetic_datum: Option<u16>,
8779

88-
projected_type: Option<u16>,
89-
proj_citation: Option<String>,
90-
projection: Option<u16>,
91-
proj_coord_trans: Option<u16>,
92-
proj_linear_units: Option<u16>,
93-
proj_linear_unit_size: Option<f64>,
94-
proj_std_parallel1: Option<f64>,
95-
proj_std_parallel2: Option<f64>,
96-
proj_nat_origin_long: Option<f64>,
97-
proj_nat_origin_lat: Option<f64>,
98-
proj_false_easting: Option<f64>,
99-
proj_false_northing: Option<f64>,
100-
proj_false_origin_long: Option<f64>,
101-
proj_false_origin_lat: Option<f64>,
102-
proj_false_origin_easting: Option<f64>,
103-
proj_false_origin_northing: Option<f64>,
104-
proj_center_long: Option<f64>,
105-
proj_center_lat: Option<f64>,
106-
proj_center_easting: Option<f64>,
107-
proj_center_northing: Option<f64>,
108-
proj_scale_at_nat_origin: Option<f64>,
109-
proj_scale_at_center: Option<f64>,
110-
proj_azimuth_angle: Option<f64>,
111-
proj_straight_vert_pole_long: Option<f64>,
80+
/// This key is used to specify a Prime Meridian from the GeoTIFF CRS register or to indicate
81+
/// that the Prime Meridian is user-defined. The default is Greenwich, England.
82+
/// <https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_primemeridiangeokey>
83+
pub geog_prime_meridian: Option<u16>,
11284

113-
vertical: Option<u16>,
114-
vertical_citation: Option<String>,
115-
vertical_datum: Option<u16>,
116-
vertical_units: Option<u16>,
85+
pub geog_linear_units: Option<u16>,
86+
pub geog_linear_unit_size: Option<f64>,
87+
pub geog_angular_units: Option<u16>,
88+
pub geog_angular_unit_size: Option<f64>,
89+
90+
/// This key is provided to specify an ellipsoid (or sphere) from the GeoTIFF CRS register or
91+
/// to indicate that the ellipsoid (or sphere) is user-defined.
92+
pub geog_ellipsoid: Option<u16>,
93+
pub geog_semi_major_axis: Option<f64>,
94+
pub geog_semi_minor_axis: Option<f64>,
95+
pub geog_inv_flattening: Option<f64>,
96+
pub geog_azimuth_units: Option<u16>,
97+
98+
/// This key allows definition of a user-defined Prime Meridian, the location of which is
99+
/// defined by its longitude relative to the international reference meridian (for the earth
100+
/// this is Greenwich).
101+
pub geog_prime_meridian_long: Option<f64>,
102+
103+
pub projected_type: Option<u16>,
104+
pub proj_citation: Option<String>,
105+
pub projection: Option<u16>,
106+
pub proj_coord_trans: Option<u16>,
107+
pub proj_linear_units: Option<u16>,
108+
pub proj_linear_unit_size: Option<f64>,
109+
pub proj_std_parallel1: Option<f64>,
110+
pub proj_std_parallel2: Option<f64>,
111+
pub proj_nat_origin_long: Option<f64>,
112+
pub proj_nat_origin_lat: Option<f64>,
113+
pub proj_false_easting: Option<f64>,
114+
pub proj_false_northing: Option<f64>,
115+
pub proj_false_origin_long: Option<f64>,
116+
pub proj_false_origin_lat: Option<f64>,
117+
pub proj_false_origin_easting: Option<f64>,
118+
pub proj_false_origin_northing: Option<f64>,
119+
pub proj_center_long: Option<f64>,
120+
pub proj_center_lat: Option<f64>,
121+
pub proj_center_easting: Option<f64>,
122+
pub proj_center_northing: Option<f64>,
123+
pub proj_scale_at_nat_origin: Option<f64>,
124+
pub proj_scale_at_center: Option<f64>,
125+
pub proj_azimuth_angle: Option<f64>,
126+
pub proj_straight_vert_pole_long: Option<f64>,
127+
128+
pub vertical: Option<u16>,
129+
pub vertical_citation: Option<String>,
130+
pub vertical_datum: Option<u16>,
131+
pub vertical_units: Option<u16>,
117132
}
118133

119134
impl GeoKeyDirectory {
135+
/// Construct a new [`GeoKeyDirectory`] from tag values.
120136
pub(crate) fn from_tags(mut tag_data: HashMap<GeoKeyTag, Value>) -> TiffResult<Self> {
121137
let mut model_type = None;
122138
let mut raster_type = None;
@@ -281,6 +297,9 @@ impl GeoKeyDirectory {
281297
}
282298

283299
/// Return the EPSG code representing the crs of the image
300+
///
301+
/// This will return either [`GeoKeyDirectory::projected_type`] or
302+
/// [`GeoKeyDirectory::geographic_type`].
284303
pub fn epsg_code(&self) -> Option<u16> {
285304
if let Some(projected_type) = self.projected_type {
286305
Some(projected_type)

src/geo/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//! Support for GeoTIFF files.
2+
13
mod affine;
24
mod geo_key_directory;
35
mod partial_reads;

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![doc = include_str!("../README.md")]
2+
13
mod async_reader;
24
mod cog;
35
mod decoder;

0 commit comments

Comments
 (0)