libisg 0.2.5

Libraly reading/writing ISG 2.0 format
Documentation
// TODO: better impl

use core::ops::{Add, Mul, Neg};
use std::ops::Sub;

use crate::Coord;

impl Neg for Coord {
    type Output = Coord;

    #[inline]
    fn neg(self) -> Self::Output {
        Neg::neg(&self)
    }
}

impl Neg for &Coord {
    type Output = Coord;

    #[inline]
    fn neg(self) -> Self::Output {
        match self {
            Coord::DMS {
                degree,
                minutes,
                second,
            } => Coord::DMS {
                degree: -degree,
                minutes: *minutes,
                second: *second,
            },
            Coord::Dec(f) => Coord::Dec(-f),
        }
    }
}

macro_rules! impl_mul {
    ($type:tt) => {
        impl Mul<$type> for Coord {
            type Output = Coord;

            #[inline]
            fn mul(self, rhs: $type) -> Self::Output {
                Mul::mul(&self, &rhs)
            }
        }

        impl Mul<$type> for &Coord {
            type Output = Coord;

            #[inline]
            fn mul(self, rhs: $type) -> Self::Output {
                Mul::mul(self, &rhs)
            }
        }

        impl Mul<&$type> for Coord {
            type Output = Coord;

            #[inline]
            fn mul(self, rhs: &$type) -> Self::Output {
                Mul::mul(&self, rhs)
            }
        }

        impl Mul<&$type> for &Coord {
            type Output = Coord;

            fn mul(self, rhs: &$type) -> Self::Output {
                if *rhs == 0 {
                    return match self {
                        Coord::DMS { .. } => Coord::DMS {
                            degree: 0,
                            minutes: 0,
                            second: 0,
                        },
                        Coord::Dec(..) => Coord::Dec(0.0),
                    };
                }

                match self {
                    Coord::DMS {
                        degree,
                        minutes,
                        second,
                    } => {
                        let second = *second as u64;
                        let minutes = *minutes as u64;
                        let degree = *degree as i64;
                        let rhs = *rhs as u64;

                        let temp = second * rhs;
                        let (second, carry) = (temp % 60, temp / 60);

                        let temp = minutes * rhs + carry;
                        let (minutes, carry) = (temp % 60, temp / 60);

                        let degree = if !degree.is_negative() {
                            degree * rhs as i64 + carry as i64
                        } else {
                            degree * rhs as i64 - carry as i64
                        };

                        Coord::DMS {
                            degree: degree as i16,
                            minutes: minutes as u8,
                            second: second as u8,
                        }
                    }
                    Coord::Dec(coord) => Coord::Dec(coord * *rhs as f64),
                }
            }
        }
    };
}

impl_mul!(u8);
impl_mul!(u16);
impl_mul!(u32);
impl_mul!(u64);
impl_mul!(usize);

impl Add<Coord> for Coord {
    type Output = Coord;

    #[inline]
    fn add(self, rhs: Coord) -> Self::Output {
        Add::add(&self, &rhs)
    }
}

impl Add<&Coord> for Coord {
    type Output = Coord;

    #[inline]
    fn add(self, rhs: &Coord) -> Self::Output {
        Add::add(&self, rhs)
    }
}

impl Add<Coord> for &Coord {
    type Output = Coord;

    #[inline]
    fn add(self, rhs: Coord) -> Self::Output {
        Add::add(self, &rhs)
    }
}

impl Add<&Coord> for &Coord {
    type Output = Coord;

    fn add(self, rhs: &Coord) -> Self::Output {
        match (self, rhs) {
            (
                Coord::DMS {
                    degree: a_deg,
                    minutes: a_min,
                    second: a_sec,
                },
                Coord::DMS {
                    degree: b_deg,
                    minutes: b_min,
                    second: b_sec,
                },
            ) => {
                let a_sec = *a_sec as u64;
                let b_sec = *b_sec as u64;
                let a_min = *a_min as u64;
                let b_min = *b_min as u64;
                let a_deg = *a_deg as i64;
                let b_deg = *b_deg as i64;

                let temp = a_sec + b_sec;
                let (second, carry) = if 60 <= temp {
                    (temp - 60, 1)
                } else {
                    (temp, 0)
                };

                let temp = a_min + b_min + carry;
                let (minutes, carry) = if 60 <= temp {
                    (temp - 60, 1)
                } else {
                    (temp, 0)
                };

                let degree = a_deg + b_deg + carry as i64;
                Coord::DMS {
                    degree: degree as _,
                    minutes: minutes as _,
                    second: second as _,
                }
            }
            (Coord::Dec(a), Coord::Dec(b)) => Coord::Dec(a + b),
            _ => unimplemented!("not supported ops: `Coord::DMS` + `Coord::Dec`"),
        }
    }
}

impl Sub<Coord> for Coord {
    type Output = Coord;

    #[inline]
    fn sub(self, rhs: Coord) -> Self::Output {
        Sub::sub(&self, &rhs)
    }
}

impl Sub<&Coord> for Coord {
    type Output = Coord;

    #[inline]
    fn sub(self, rhs: &Coord) -> Self::Output {
        Sub::sub(&self, rhs)
    }
}

impl Sub<Coord> for &Coord {
    type Output = Coord;

    #[inline]
    fn sub(self, rhs: Coord) -> Self::Output {
        Sub::sub(self, &rhs)
    }
}

impl Sub<&Coord> for &Coord {
    type Output = Coord;

    fn sub(self, rhs: &Coord) -> Self::Output {
        match (self, rhs) {
            (
                Coord::DMS {
                    degree: a_deg,
                    minutes: a_min,
                    second: a_sec,
                },
                Coord::DMS {
                    degree: b_deg,
                    minutes: b_min,
                    second: b_sec,
                },
            ) => {
                let a_sec = *a_sec as i64;
                let b_sec = *b_sec as i64;
                let a_min = *a_min as i64;
                let b_min = *b_min as i64;
                let a_deg = *a_deg as i64;
                let b_deg = *b_deg as i64;

                let (second, carry) = if a_sec >= b_sec {
                    (a_sec - b_sec, 0)
                } else {
                    (60 + a_sec - b_sec, 1)
                };

                let (minutes, carry) = if a_min >= b_min + carry {
                    (a_min - b_min - carry, 0)
                } else {
                    (60 + a_min - b_min - carry, 1)
                };

                let degree = a_deg - b_deg - carry;
                Coord::DMS {
                    degree: degree as _,
                    minutes: minutes as _,
                    second: second as _,
                }
            }
            (Coord::Dec(a), Coord::Dec(b)) => Coord::Dec(a - b),
            _ => unimplemented!(
                "not supported ops: `Coord::DMS` - `Coord::Dec` or `Coord::Dec` - `Coord::DMS`"
            ),
        }
    }
}

#[cfg(test)]
mod test {
    use std::fs;

    use crate::{from_str, Data, DataBounds};

    use super::*;

    #[test]
    fn test() {
        let s = fs::read_to_string("rsc/isg/example.1.isg").unwrap();
        let isg = from_str(&s).unwrap();

        let (a_max, b_max, delta_a, delta_b) = match isg.header.data_bounds {
            DataBounds::GridGeodetic {
                lat_max,
                lon_max,
                delta_lat,
                delta_lon,
                ..
            } => (lat_max, lon_max, delta_lat, delta_lon),
            DataBounds::GridProjected {
                north_max,
                east_max,
                delta_north,
                delta_east,
                ..
            } => (north_max, east_max, delta_north, delta_east),
            DataBounds::SparseGeodetic {
                lat_max, lon_max, ..
            } => (lat_max, lon_max, Coord::Dec(0.0), Coord::Dec(0.0)),
            DataBounds::SparseProjected {
                north_max,
                east_max,
                ..
            } => (north_max, east_max, Coord::Dec(0.0), Coord::Dec(0.0)),
        };

        let pos: Vec<Vec<_>> = match &isg.data {
            Data::Grid(data) => data
                .iter()
                .enumerate()
                .map(|(nrow, row)| {
                    row.iter()
                        .enumerate()
                        .map(|(ncol, _)| (a_max - delta_a * nrow, b_max - delta_b * ncol))
                        .collect()
                })
                .collect(),
            Data::Sparse(_) => {
                panic!()
            }
        };

        assert_eq!(
            pos,
            vec![
                vec![
                    (Coord::with_dms(41, 10, 0), Coord::with_dms(121, 50, 0)),
                    (Coord::with_dms(41, 10, 0), Coord::with_dms(121, 30, 0)),
                    (Coord::with_dms(41, 10, 0), Coord::with_dms(121, 10, 0)),
                    (Coord::with_dms(41, 10, 0), Coord::with_dms(120, 50, 0)),
                    (Coord::with_dms(41, 10, 0), Coord::with_dms(120, 30, 0)),
                    (Coord::with_dms(41, 10, 0), Coord::with_dms(120, 10, 0)),
                ],
                vec![
                    (Coord::with_dms(40, 50, 0), Coord::with_dms(121, 50, 0)),
                    (Coord::with_dms(40, 50, 0), Coord::with_dms(121, 30, 0)),
                    (Coord::with_dms(40, 50, 0), Coord::with_dms(121, 10, 0)),
                    (Coord::with_dms(40, 50, 0), Coord::with_dms(120, 50, 0)),
                    (Coord::with_dms(40, 50, 0), Coord::with_dms(120, 30, 0)),
                    (Coord::with_dms(40, 50, 0), Coord::with_dms(120, 10, 0)),
                ],
                vec![
                    (Coord::with_dms(40, 30, 0), Coord::with_dms(121, 50, 0)),
                    (Coord::with_dms(40, 30, 0), Coord::with_dms(121, 30, 0)),
                    (Coord::with_dms(40, 30, 0), Coord::with_dms(121, 10, 0)),
                    (Coord::with_dms(40, 30, 0), Coord::with_dms(120, 50, 0)),
                    (Coord::with_dms(40, 30, 0), Coord::with_dms(120, 30, 0)),
                    (Coord::with_dms(40, 30, 0), Coord::with_dms(120, 10, 0)),
                ],
                vec![
                    (Coord::with_dms(40, 10, 0), Coord::with_dms(121, 50, 0)),
                    (Coord::with_dms(40, 10, 0), Coord::with_dms(121, 30, 0)),
                    (Coord::with_dms(40, 10, 0), Coord::with_dms(121, 10, 0)),
                    (Coord::with_dms(40, 10, 0), Coord::with_dms(120, 50, 0)),
                    (Coord::with_dms(40, 10, 0), Coord::with_dms(120, 30, 0)),
                    (Coord::with_dms(40, 10, 0), Coord::with_dms(120, 10, 0)),
                ],
            ]
        )
    }
}