Skip to main content

qtty_core/units/
area.rs

1//! Area units.
2//!
3//! The canonical scaling unit for this dimension is the **square metre** (`SquareMeter::RATIO == 1.0`).
4//! All other area units are expressed as exact ratios to square metres.
5//!
6//! This module provides:
7//!
8//! - **Metric squares**: square millimetre, square centimetre, square metre, square kilometre.
9//! - **Land measurement**: hectare, are.
10//! - **Imperial/US**: square inch, square foot, square yard, square mile, acre.
11//!
12//! Area units can also arise *automatically* from multiplying two length quantities:
13//!
14//! ```rust
15//! use qtty_core::length::{Meter, Meters};
16//! use qtty_core::area::{SquareMeters, SquareMeter};
17//! use qtty_core::Prod;
18//!
19//! let side = Meters::new(5.0);
20//! let area_prod = side * side;                     // Quantity<Prod<Meter, Meter>>
21//! let area: SquareMeters = area_prod.to();          // Convert to named area unit
22//! assert!((area.value() - 25.0).abs() < 1e-12);
23//! ```
24//!
25//! ## All area units
26//!
27//! ```rust
28//! use qtty_core::area::*;
29//!
30//! macro_rules! touch {
31//!     ($T:ty, $v:expr) => {{ let q = <$T>::new($v); let _c = q; assert!(q == q); }};
32//! }
33//!
34//! touch!(SquareMeters, 1.0);     touch!(SquareKilometers, 1.0);
35//! touch!(SquareCentimeters, 1.0);touch!(SquareMillimeters, 1.0);
36//! touch!(Hectares, 1.0);         touch!(Ares, 1.0);
37//! touch!(SquareInches, 1.0);     touch!(SquareFeet, 1.0);
38//! touch!(SquareYards, 1.0);      touch!(SquareMiles, 1.0);
39//! touch!(Acres, 1.0);
40//! ```
41
42use crate::{Quantity, Unit};
43use qtty_derive::Unit;
44
45/// Re-export the area dimension from the dimension module.
46pub use crate::dimension::Area;
47
48/// Marker trait for any [`Unit`] whose dimension is [`Area`].
49pub trait AreaUnit: Unit<Dim = Area> {}
50impl<T: Unit<Dim = Area>> AreaUnit for T {}
51
52// ─────────────────────────────────────────────────────────────────────────────
53// SI / metric area units
54// ─────────────────────────────────────────────────────────────────────────────
55
56/// Square metre (SI derived unit of area).
57#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
58#[unit(symbol = "m²", dimension = Area, ratio = 1.0)]
59pub struct SquareMeter;
60/// A quantity measured in square metres.
61pub type SquareMeters = Quantity<SquareMeter>;
62
63/// Square kilometre (`1e6 m²`).
64#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
65#[unit(symbol = "km²", dimension = Area, ratio = 1e6)]
66pub struct SquareKilometer;
67/// A quantity measured in square kilometres.
68pub type SquareKilometers = Quantity<SquareKilometer>;
69
70/// Square centimetre (`1e-4 m²`).
71#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
72#[unit(symbol = "cm²", dimension = Area, ratio = 1e-4)]
73pub struct SquareCentimeter;
74/// A quantity measured in square centimetres.
75pub type SquareCentimeters = Quantity<SquareCentimeter>;
76
77/// Square millimetre (`1e-6 m²`).
78#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
79#[unit(symbol = "mm²", dimension = Area, ratio = 1e-6)]
80pub struct SquareMillimeter;
81/// A quantity measured in square millimetres.
82pub type SquareMillimeters = Quantity<SquareMillimeter>;
83
84// ─────────────────────────────────────────────────────────────────────────────
85// Land measurement
86// ─────────────────────────────────────────────────────────────────────────────
87
88/// Hectare (`10 000 m²`).
89#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
90#[unit(symbol = "ha", dimension = Area, ratio = 1e4)]
91pub struct Hectare;
92/// A quantity measured in hectares.
93pub type Hectares = Quantity<Hectare>;
94
95/// Are (`100 m²`).
96#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
97#[unit(symbol = "a", dimension = Area, ratio = 100.0)]
98pub struct Are;
99/// A quantity measured in ares.
100pub type Ares = Quantity<Are>;
101
102// ─────────────────────────────────────────────────────────────────────────────
103// Imperial / US customary area units
104// ─────────────────────────────────────────────────────────────────────────────
105
106/// Square inch (`6.4516e-4 m²`, exact: `0.0254² m²`).
107#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
108#[unit(symbol = "in²", dimension = Area, ratio = 6.4516e-4)]
109pub struct SquareInch;
110/// A quantity measured in square inches.
111pub type SquareInches = Quantity<SquareInch>;
112
113/// Square foot (`0.09290304 m²`, exact: `0.3048² m²`).
114#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
115#[unit(symbol = "ft²", dimension = Area, ratio = 0.09290304)]
116pub struct SquareFoot;
117/// A quantity measured in square feet.
118pub type SquareFeet = Quantity<SquareFoot>;
119
120/// Square yard (`0.83612736 m²`, exact: `0.9144² m²`).
121#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
122#[unit(symbol = "yd²", dimension = Area, ratio = 0.83612736)]
123pub struct SquareYard;
124/// A quantity measured in square yards.
125pub type SquareYards = Quantity<SquareYard>;
126
127/// Square mile (`2_589_988.110336 m²`, exact: `1609.344² m²`).
128#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
129#[unit(symbol = "mi²", dimension = Area, ratio = 2_589_988.110_336)]
130pub struct SquareMile;
131/// A quantity measured in square miles.
132pub type SquareMiles = Quantity<SquareMile>;
133
134/// Acre (exactly `4046.8564224 m²`).
135#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
136#[unit(symbol = "ac", dimension = Area, ratio = 4_046.856_422_4)]
137pub struct Acre;
138/// A quantity measured in acres.
139pub type Acres = Quantity<Acre>;
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use approx::assert_abs_diff_eq;
145
146    #[test]
147    fn sqm_to_sqkm() {
148        let a = SquareMeters::new(1_000_000.0);
149        let b: SquareKilometers = a.to();
150        assert_abs_diff_eq!(b.value(), 1.0, epsilon = 1e-12);
151    }
152
153    #[test]
154    fn hectare_to_sqm() {
155        let a = Hectares::new(1.0);
156        let b: SquareMeters = a.to();
157        assert_abs_diff_eq!(b.value(), 10_000.0, epsilon = 1e-9);
158    }
159
160    #[test]
161    fn acre_to_hectare() {
162        let a = Acres::new(1.0);
163        let b: Hectares = a.to();
164        assert_abs_diff_eq!(b.value(), 0.404_685_642_24, epsilon = 1e-9);
165    }
166
167    #[test]
168    fn sqft_to_sqm() {
169        let a = SquareFeet::new(1.0);
170        let b: SquareMeters = a.to();
171        assert_abs_diff_eq!(b.value(), 0.092_903_04, epsilon = 1e-9);
172    }
173
174    #[test]
175    fn length_product_to_area() {
176        use crate::length::{Meter, Meters};
177        use crate::Prod;
178
179        let side = Meters::new(5.0);
180        let area_prod: Quantity<Prod<Meter, Meter>> = side * side;
181        let area: SquareMeters = area_prod.to();
182        assert_abs_diff_eq!(area.value(), 25.0, epsilon = 1e-12);
183    }
184
185    #[test]
186    fn sqmile_to_sqkm() {
187        let a = SquareMiles::new(1.0);
188        let b: SquareKilometers = a.to();
189        assert_abs_diff_eq!(b.value(), 2.589_988_110_336, epsilon = 1e-6);
190    }
191
192    #[test]
193    fn sqcm_to_sqm() {
194        let a = SquareCentimeters::new(10_000.0);
195        let b: SquareMeters = a.to();
196        assert_abs_diff_eq!(b.value(), 1.0, epsilon = 1e-12);
197    }
198
199    #[test]
200    fn sqmm_to_sqcm() {
201        let a = SquareMillimeters::new(100.0);
202        let b: SquareCentimeters = a.to();
203        assert_abs_diff_eq!(b.value(), 1.0, epsilon = 1e-12);
204    }
205
206    #[test]
207    fn are_to_sqm() {
208        let a = Ares::new(1.0);
209        let b: SquareMeters = a.to();
210        assert_abs_diff_eq!(b.value(), 100.0, epsilon = 1e-12);
211    }
212
213    #[test]
214    fn sqinch_to_sqcm() {
215        let a = SquareInches::new(1.0);
216        let b: SquareCentimeters = a.to();
217        // 1 in² = 6.4516 cm²
218        assert_abs_diff_eq!(b.value(), 6.4516, epsilon = 1e-9);
219    }
220
221    #[test]
222    fn sqyard_to_sqm() {
223        let a = SquareYards::new(1.0);
224        let b: SquareMeters = a.to();
225        assert_abs_diff_eq!(b.value(), 0.836_127_36, epsilon = 1e-9);
226    }
227
228    #[test]
229    fn roundtrip_sqcm_sqm() {
230        let original = SquareCentimeters::new(250.0);
231        let converted = original.to::<SquareMeter>();
232        let back = converted.to::<SquareCentimeter>();
233        assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-10);
234    }
235
236    #[test]
237    fn symbols_are_correct() {
238        assert_eq!(SquareMeter::SYMBOL, "m²");
239        assert_eq!(Hectare::SYMBOL, "ha");
240        assert_eq!(Acre::SYMBOL, "ac");
241        assert_eq!(SquareInch::SYMBOL, "in²");
242    }
243}