melvin_ob/objective/
known_img_objective.rs

1use crate::imaging::CameraAngle;
2use crate::util::Vec2D;
3use crate::http_handler::{ImageObjective, ZoneType};
4use chrono::{DateTime, Utc};
5use fixed::types::I32F32;
6use num::ToPrimitive;
7use std::cmp::Ordering;
8
9/// Represents a known image objective that specifies a region of interest on the map.
10///
11/// This objective includes details like the time frame, required camera angle, and coverage percentage.
12#[derive(Debug, Clone)]
13pub struct KnownImgObjective {
14    /// Unique identifier for the objective.
15    id: usize,
16    /// Human-readable name for the objective.
17    name: String,
18    /// Start time of the objective in UTC.
19    start: DateTime<Utc>,
20    /// End time of the objective in UTC.
21    end: DateTime<Utc>,
22    /// Coordinates of the objective zone as `[x_min, y_min, x_max, y_max]`.
23    zone: [i32; 4],
24    /// Required camera angle for the objective.
25    optic_required: CameraAngle,
26    /// Coverage percentage required for the objective.
27    coverage_required: f64,
28}
29
30impl KnownImgObjective {
31    /// Constructs a new [`KnownImgObjective`] from the provided parameters.
32    pub fn new(
33        id: usize,
34        name: String,
35        start: DateTime<Utc>,
36        end: DateTime<Utc>,
37        zone: [i32; 4],
38        optic_required: CameraAngle,
39        coverage_required: f64,
40    ) -> KnownImgObjective {
41        KnownImgObjective { id, name, start, end, zone, optic_required, coverage_required }
42    }
43
44    /// Returns the unique identifier of the objective.
45    pub fn id(&self) -> usize { self.id }
46    /// Returns the start time of the objective.
47    pub fn start(&self) -> DateTime<Utc> { self.start }
48    /// Returns the end time of the objective.
49    pub fn end(&self) -> DateTime<Utc> { self.end }
50    /// Returns the human-readable name of the objective.
51    pub fn name(&self) -> &str { &self.name }
52    /// Returns the zone coordinates of the objective.
53    pub fn zone(&self) -> [i32; 4] { self.zone }
54    /// Returns the required camera angle for the objective.
55    pub fn optic_required(&self) -> CameraAngle { self.optic_required }
56    /// Returns the coverage percentage required for the objective.
57    pub fn coverage_required(&self) -> f64 { self.coverage_required }
58    /// Returns the width of the zone.
59    pub fn width(&self) -> i32 { self.zone[2] - self.zone[0] }
60    /// Returns the height of the zone.
61    pub fn height(&self) -> i32 { self.zone[3] - self.zone[1] }
62
63    /// Calculates the central point of the image zone and wraps it around the map if necessary.
64    pub fn get_single_image_point(&self) -> Vec2D<I32F32> {
65        let x_size = self.zone[2] - self.zone[0];
66        let y_size = self.zone[3] - self.zone[1];
67        let pos = Vec2D::new(self.zone[0] + x_size / 2, self.zone[1] + y_size / 2);
68        Vec2D::new(I32F32::from(pos.x()), I32F32::from(pos.y())).wrap_around_map()
69    }
70
71    /// Returns the corners of the zone as pairs of points with their opposite corners.
72    pub fn get_corners(&self) -> [(Vec2D<I32F32>, Vec2D<I32F32>); 4] {
73        let first = Vec2D::new(I32F32::from(self.zone[0]), I32F32::from(self.zone[1]));
74        let second = Vec2D::new(I32F32::from(self.zone[0]), I32F32::from(self.zone[3]));
75        let third = Vec2D::new(I32F32::from(self.zone[2]), I32F32::from(self.zone[1]));
76        let fourth = Vec2D::new(I32F32::from(self.zone[2]), I32F32::from(self.zone[3]));
77        [
78            (first, first.unwrapped_to(&fourth)),
79            (second, second.unwrapped_to(&third)),
80            (third, third.unwrapped_to(&second)),
81            (fourth, fourth.unwrapped_to(&first)),
82        ]
83    }
84
85    /// Calculates the minimum number of images needed to meet the coverage requirements.
86    ///
87    /// # Returns
88    /// The minimum number of images as an integer.
89    #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
90    pub fn min_images(&self) -> i32 {
91        let lens_square_side_length = u32::from(self.optic_required().get_square_side_length());
92        let zone_width = self.zone[2] - self.zone[0];
93        let zone_height = self.zone[3] - self.zone[1];
94
95        let total_zone_area_size = f64::from(zone_width * zone_height);
96        let lens_area_size = f64::from(lens_square_side_length.pow(2));
97        let min_area_required = total_zone_area_size * self.coverage_required;
98
99        let min_number_of_images_required = (min_area_required / lens_area_size).ceil();
100        min_number_of_images_required.to_i32().unwrap()
101    }
102}
103
104impl TryFrom<ImageObjective> for KnownImgObjective {
105    type Error = std::io::Error;
106
107    /// Attempts to convert an [`ImageObjective`] into a [`KnownImgObjective`].
108    ///
109    /// # Errors
110    /// Returns an error if the provided [`ImageObjective`] is of type `SecretZone`.
111    fn try_from(obj: ImageObjective) -> Result<Self, Self::Error> {
112        match obj.zone_type() {
113            ZoneType::KnownZone(zone) => Ok(Self {
114                id: obj.id(),
115                name: String::from(obj.name()),
116                start: obj.start(),
117                end: obj.end(),
118                zone: *zone,
119                optic_required: CameraAngle::from(obj.optic_required()),
120                coverage_required: obj.coverage_required(),
121            }),
122            ZoneType::SecretZone(_) => Err(std::io::Error::new(
123                std::io::ErrorKind::Other,
124                "[FATAL] Wrong objective conversion!",
125            )),
126        }
127    }
128}
129
130impl TryFrom<(ImageObjective, [i32; 4])> for KnownImgObjective {
131    type Error = std::io::Error;
132
133    /// Attempts to convert a tuple of `(ImageObjective, zone)` into a [`KnownImgObjective`].
134    ///
135    /// # Errors
136    /// Returns an error if the `ImageObjective` is of type `KnownZone`.
137    fn try_from(obj_with_zone: (ImageObjective, [i32; 4])) -> Result<Self, Self::Error> {
138        let obj = obj_with_zone.0;
139        match obj.zone_type() {
140            ZoneType::SecretZone(_) => Ok(Self {
141                id: obj.id(),
142                name: String::from(obj.name()),
143                start: obj.start(),
144                end: obj.end(),
145                zone: obj_with_zone.1,
146                optic_required: CameraAngle::from(obj.optic_required()),
147                coverage_required: obj.coverage_required(),
148            }),
149            ZoneType::KnownZone(_) => Err(std::io::Error::new(
150                std::io::ErrorKind::Other,
151                "[FATAL] Wrong objective conversion!",
152            )),
153        }
154    }
155}
156
157impl Eq for KnownImgObjective {}
158
159impl PartialEq<Self> for KnownImgObjective {
160    /// Compares the equality of two `KnownImgObjective` instances based on their end time.
161    fn eq(&self, other: &Self) -> bool { self.end == other.end }
162}
163
164impl PartialOrd<Self> for KnownImgObjective {
165    /// Partially compares two `KnownImgObjective` instances based on their end time.
166    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
167}
168
169impl Ord for KnownImgObjective {
170    /// Compares two `KnownImgObjective` instances based on their end time.
171    fn cmp(&self, other: &Self) -> Ordering { self.end.cmp(&other.end) }
172}