Skip to content

Define Python binding for ImageFileDirectory and GeoKeyDirectory #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ bytes = "1.8"
pyo3 = { version = "0.23.0", features = ["macros"] }
pyo3-bytes = "0.1.2"
thiserror = "1"
tiff = "0.9.1"

[profile.release]
lto = true
Expand Down
56 changes: 56 additions & 0 deletions python/python/async_tiff/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from enum import IntEnum


class CompressionMethod(IntEnum):
"""
See [TIFF compression
tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html) for
reference.
"""

Uncompressed = 1
Huffman = 2
Fax3 = 3
Fax4 = 4
LZW = 5
JPEG = 6
# // "Extended JPEG" or "new JPEG" style
ModernJPEG = 7
Deflate = 8
OldDeflate = 0x80B2
PackBits = 0x8005


class PhotometricInterpretation(IntEnum):
WhiteIsZero = 0
BlackIsZero = 1
RGB = 2
RGBPalette = 3
TransparencyMask = 4
CMYK = 5
YCbCr = 6
CIELab = 8


class PlanarConfiguration(IntEnum):
Chunky = 1
Planar = 2


class Predictor(IntEnum):
Unknown = 1
Horizontal = 2
FloatingPoint = 3


class ResolutionUnit(IntEnum):
Unknown = 1
Inch = 2
Centimeter = 3


class SampleFormat(IntEnum):
Uint = 1
Int = 2
IEEEFP = 3
Void = 4
127 changes: 127 additions & 0 deletions python/src/enums.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use pyo3::intern;
use pyo3::prelude::*;
use pyo3::types::{PyString, PyTuple};
use tiff::tags::{
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, ResolutionUnit,
SampleFormat,
};

pub(crate) struct PyCompressionMethod(CompressionMethod);

impl From<CompressionMethod> for PyCompressionMethod {
fn from(value: CompressionMethod) -> Self {
Self(value)
}
}

impl<'py> IntoPyObject<'py> for PyCompressionMethod {
type Target = PyAny;
type Output = Bound<'py, PyAny>;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
to_py_enum_variant(py, intern!(py, "CompressionMethod"), self.0.to_u16())
}
}

pub(crate) struct PyPhotometricInterpretation(PhotometricInterpretation);

impl From<PhotometricInterpretation> for PyPhotometricInterpretation {
fn from(value: PhotometricInterpretation) -> Self {
Self(value)
}
}

impl<'py> IntoPyObject<'py> for PyPhotometricInterpretation {
type Target = PyAny;
type Output = Bound<'py, PyAny>;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
to_py_enum_variant(
py,
intern!(py, "PhotometricInterpretation"),
self.0.to_u16(),
)
}
}

pub(crate) struct PyPlanarConfiguration(PlanarConfiguration);

impl From<PlanarConfiguration> for PyPlanarConfiguration {
fn from(value: PlanarConfiguration) -> Self {
Self(value)
}
}

impl<'py> IntoPyObject<'py> for PyPlanarConfiguration {
type Target = PyAny;
type Output = Bound<'py, PyAny>;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
to_py_enum_variant(py, intern!(py, "PlanarConfiguration"), self.0.to_u16())
}
}

pub(crate) struct PyResolutionUnit(ResolutionUnit);

impl From<ResolutionUnit> for PyResolutionUnit {
fn from(value: ResolutionUnit) -> Self {
Self(value)
}
}

impl<'py> IntoPyObject<'py> for PyResolutionUnit {
type Target = PyAny;
type Output = Bound<'py, PyAny>;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
to_py_enum_variant(py, intern!(py, "ResolutionUnit"), self.0.to_u16())
}
}

pub(crate) struct PyPredictor(Predictor);

impl From<Predictor> for PyPredictor {
fn from(value: Predictor) -> Self {
Self(value)
}
}

impl<'py> IntoPyObject<'py> for PyPredictor {
type Target = PyAny;
type Output = Bound<'py, PyAny>;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
to_py_enum_variant(py, intern!(py, "Predictor"), self.0.to_u16())
}
}

pub(crate) struct PySampleFormat(SampleFormat);

impl From<SampleFormat> for PySampleFormat {
fn from(value: SampleFormat) -> Self {
Self(value)
}
}

impl<'py> IntoPyObject<'py> for PySampleFormat {
type Target = PyAny;
type Output = Bound<'py, PyAny>;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
to_py_enum_variant(py, intern!(py, "SampleFormat"), self.0.to_u16())
}
}
fn to_py_enum_variant<'py>(
py: Python<'py>,
enum_name: &Bound<'py, PyString>,
value: u16,
) -> PyResult<Bound<'py, PyAny>> {
let enums_mod = py.import(intern!(py, "async_tiff.enums"))?;
enums_mod.call_method1(enum_name, PyTuple::new(py, vec![value])?)
}
202 changes: 202 additions & 0 deletions python/src/geo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
use async_tiff::geo::GeoKeyDirectory;
use pyo3::prelude::*;

#[pyclass(name = "GeoKeyDirectory")]
pub(crate) struct PyGeoKeyDirectory {
#[pyo3(get)]
model_type: Option<u16>,
#[pyo3(get)]
raster_type: Option<u16>,
#[pyo3(get)]
citation: Option<String>,
#[pyo3(get)]
geographic_type: Option<u16>,
#[pyo3(get)]
geog_citation: Option<String>,
#[pyo3(get)]
geog_geodetic_datum: Option<u16>,
#[pyo3(get)]
geog_prime_meridian: Option<u16>,
#[pyo3(get)]
geog_linear_units: Option<u16>,
#[pyo3(get)]
geog_linear_unit_size: Option<f64>,
#[pyo3(get)]
geog_angular_units: Option<u16>,
#[pyo3(get)]
geog_angular_unit_size: Option<f64>,
#[pyo3(get)]
geog_ellipsoid: Option<u16>,
#[pyo3(get)]
geog_semi_major_axis: Option<f64>,
#[pyo3(get)]
geog_semi_minor_axis: Option<f64>,
#[pyo3(get)]
geog_inv_flattening: Option<f64>,
#[pyo3(get)]
geog_azimuth_units: Option<u16>,
#[pyo3(get)]
geog_prime_meridian_long: Option<f64>,

#[pyo3(get)]
projected_type: Option<u16>,
#[pyo3(get)]
proj_citation: Option<String>,
#[pyo3(get)]
projection: Option<u16>,
#[pyo3(get)]
proj_coord_trans: Option<u16>,
#[pyo3(get)]
proj_linear_units: Option<u16>,
#[pyo3(get)]
proj_linear_unit_size: Option<f64>,
#[pyo3(get)]
proj_std_parallel1: Option<f64>,
#[pyo3(get)]
proj_std_parallel2: Option<f64>,
#[pyo3(get)]
proj_nat_origin_long: Option<f64>,
#[pyo3(get)]
proj_nat_origin_lat: Option<f64>,
#[pyo3(get)]
proj_false_easting: Option<f64>,
#[pyo3(get)]
proj_false_northing: Option<f64>,
#[pyo3(get)]
proj_false_origin_long: Option<f64>,
#[pyo3(get)]
proj_false_origin_lat: Option<f64>,
#[pyo3(get)]
proj_false_origin_easting: Option<f64>,
#[pyo3(get)]
proj_false_origin_northing: Option<f64>,
#[pyo3(get)]
proj_center_long: Option<f64>,
#[pyo3(get)]
proj_center_lat: Option<f64>,
#[pyo3(get)]
proj_center_easting: Option<f64>,
#[pyo3(get)]
proj_center_northing: Option<f64>,
#[pyo3(get)]
proj_scale_at_nat_origin: Option<f64>,
#[pyo3(get)]
proj_scale_at_center: Option<f64>,
#[pyo3(get)]
proj_azimuth_angle: Option<f64>,
#[pyo3(get)]
proj_straight_vert_pole_long: Option<f64>,

#[pyo3(get)]
vertical: Option<u16>,
#[pyo3(get)]
vertical_citation: Option<String>,
#[pyo3(get)]
vertical_datum: Option<u16>,
#[pyo3(get)]
vertical_units: Option<u16>,
}

impl From<PyGeoKeyDirectory> for GeoKeyDirectory {
fn from(value: PyGeoKeyDirectory) -> Self {
Self {
model_type: value.model_type,
raster_type: value.raster_type,
citation: value.citation,
geographic_type: value.geographic_type,
geog_citation: value.geog_citation,
geog_geodetic_datum: value.geog_geodetic_datum,
geog_prime_meridian: value.geog_prime_meridian,
geog_linear_units: value.geog_linear_units,
geog_linear_unit_size: value.geog_linear_unit_size,
geog_angular_units: value.geog_angular_units,
geog_angular_unit_size: value.geog_angular_unit_size,
geog_ellipsoid: value.geog_ellipsoid,
geog_semi_major_axis: value.geog_semi_major_axis,
geog_semi_minor_axis: value.geog_semi_minor_axis,
geog_inv_flattening: value.geog_inv_flattening,
geog_azimuth_units: value.geog_azimuth_units,
geog_prime_meridian_long: value.geog_prime_meridian_long,
projected_type: value.projected_type,
proj_citation: value.proj_citation,
projection: value.projection,
proj_coord_trans: value.proj_coord_trans,
proj_linear_units: value.proj_linear_units,
proj_linear_unit_size: value.proj_linear_unit_size,
proj_std_parallel1: value.proj_std_parallel1,
proj_std_parallel2: value.proj_std_parallel2,
proj_nat_origin_long: value.proj_nat_origin_long,
proj_nat_origin_lat: value.proj_nat_origin_lat,
proj_false_easting: value.proj_false_easting,
proj_false_northing: value.proj_false_northing,
proj_false_origin_long: value.proj_false_origin_long,
proj_false_origin_lat: value.proj_false_origin_lat,
proj_false_origin_easting: value.proj_false_origin_easting,
proj_false_origin_northing: value.proj_false_origin_northing,
proj_center_long: value.proj_center_long,
proj_center_lat: value.proj_center_lat,
proj_center_easting: value.proj_center_easting,
proj_center_northing: value.proj_center_northing,
proj_scale_at_nat_origin: value.proj_scale_at_nat_origin,
proj_scale_at_center: value.proj_scale_at_center,
proj_azimuth_angle: value.proj_azimuth_angle,
proj_straight_vert_pole_long: value.proj_straight_vert_pole_long,
vertical: value.vertical,
vertical_citation: value.vertical_citation,
vertical_datum: value.vertical_datum,
vertical_units: value.vertical_units,
}
}
}

impl From<GeoKeyDirectory> for PyGeoKeyDirectory {
fn from(value: GeoKeyDirectory) -> Self {
Self {
model_type: value.model_type,
raster_type: value.raster_type,
citation: value.citation,
geographic_type: value.geographic_type,
geog_citation: value.geog_citation,
geog_geodetic_datum: value.geog_geodetic_datum,
geog_prime_meridian: value.geog_prime_meridian,
geog_linear_units: value.geog_linear_units,
geog_linear_unit_size: value.geog_linear_unit_size,
geog_angular_units: value.geog_angular_units,
geog_angular_unit_size: value.geog_angular_unit_size,
geog_ellipsoid: value.geog_ellipsoid,
geog_semi_major_axis: value.geog_semi_major_axis,
geog_semi_minor_axis: value.geog_semi_minor_axis,
geog_inv_flattening: value.geog_inv_flattening,
geog_azimuth_units: value.geog_azimuth_units,
geog_prime_meridian_long: value.geog_prime_meridian_long,
projected_type: value.projected_type,
proj_citation: value.proj_citation,
projection: value.projection,
proj_coord_trans: value.proj_coord_trans,
proj_linear_units: value.proj_linear_units,
proj_linear_unit_size: value.proj_linear_unit_size,
proj_std_parallel1: value.proj_std_parallel1,
proj_std_parallel2: value.proj_std_parallel2,
proj_nat_origin_long: value.proj_nat_origin_long,
proj_nat_origin_lat: value.proj_nat_origin_lat,
proj_false_easting: value.proj_false_easting,
proj_false_northing: value.proj_false_northing,
proj_false_origin_long: value.proj_false_origin_long,
proj_false_origin_lat: value.proj_false_origin_lat,
proj_false_origin_easting: value.proj_false_origin_easting,
proj_false_origin_northing: value.proj_false_origin_northing,
proj_center_long: value.proj_center_long,
proj_center_lat: value.proj_center_lat,
proj_center_easting: value.proj_center_easting,
proj_center_northing: value.proj_center_northing,
proj_scale_at_nat_origin: value.proj_scale_at_nat_origin,
proj_scale_at_center: value.proj_scale_at_center,
proj_azimuth_angle: value.proj_azimuth_angle,
proj_straight_vert_pole_long: value.proj_straight_vert_pole_long,
vertical: value.vertical,
vertical_citation: value.vertical_citation,
vertical_datum: value.vertical_datum,
vertical_units: value.vertical_units,
}
}
}
Loading