e57/
limits.rs

1use crate::error::Converter;
2use crate::Error;
3use crate::RecordDataType;
4use crate::RecordValue;
5use crate::Result;
6use roxmltree::Node;
7
8fn extract_limit(bounds: &Node, tag_name: &str) -> Result<Option<RecordValue>> {
9    if let Some(tag) = bounds.descendants().find(|n| n.has_tag_name(tag_name)) {
10        let type_str = tag
11            .attribute("type")
12            .invalid_err(format!("Cannot find type attribute of limit '{tag_name}'"))?;
13        let value_str = tag.text().unwrap_or("0");
14        Ok(match type_str {
15            "Integer" => Some(RecordValue::Integer(
16                value_str
17                    .parse::<i64>()
18                    .invalid_err("Cannot parse integer limit value")?,
19            )),
20            "ScaledInteger" => Some(RecordValue::ScaledInteger(
21                value_str
22                    .parse::<i64>()
23                    .invalid_err("Cannot parse scaled integer limit value")?,
24            )),
25            "Float" => {
26                let single = tag.attribute("precision").unwrap_or("double") == "single";
27                if single {
28                    Some(RecordValue::Single(
29                        value_str
30                            .parse::<f32>()
31                            .invalid_err("Cannot parse single limit value")?,
32                    ))
33                } else {
34                    Some(RecordValue::Double(
35                        value_str
36                            .parse::<f64>()
37                            .invalid_err("Cannot parse double limit value")?,
38                    ))
39                }
40            }
41            _ => Error::not_implemented(format!(
42                "Found unsupported limit of type '{type_str}' for '{tag_name}'"
43            ))?,
44        })
45    } else {
46        Ok(None)
47    }
48}
49
50/// Optional minimum and maximum values for intensity.
51#[derive(Clone, Debug)]
52pub struct IntensityLimits {
53    pub intensity_min: Option<RecordValue>,
54    pub intensity_max: Option<RecordValue>,
55}
56
57impl IntensityLimits {
58    pub(crate) fn from_node(node: &Node) -> Result<Self> {
59        let intensity_min = extract_limit(node, "intensityMinimum")?;
60        let intensity_max = extract_limit(node, "intensityMaximum")?;
61        Ok(Self {
62            intensity_min,
63            intensity_max,
64        })
65    }
66
67    pub(crate) fn from_record_type(data_type: &RecordDataType) -> Self {
68        let (intensity_min, intensity_max) = data_type.limits();
69        Self {
70            intensity_min,
71            intensity_max,
72        }
73    }
74
75    pub(crate) fn xml_string(&self) -> String {
76        let mut xml = String::from("<intensityLimits type=\"Structure\">\n");
77        if let Some(min) = &self.intensity_min {
78            xml += &record_value_to_xml("intensityMinimum", min);
79        }
80        if let Some(max) = &self.intensity_max {
81            xml += &record_value_to_xml("intensityMaximum", max);
82        }
83        xml += "</intensityLimits>\n";
84        xml
85    }
86}
87
88/// Optional minimum and maximum values for the colors red, green and blue.
89#[derive(Clone, Debug)]
90pub struct ColorLimits {
91    pub red_min: Option<RecordValue>,
92    pub red_max: Option<RecordValue>,
93    pub green_min: Option<RecordValue>,
94    pub green_max: Option<RecordValue>,
95    pub blue_min: Option<RecordValue>,
96    pub blue_max: Option<RecordValue>,
97}
98
99impl ColorLimits {
100    pub(crate) fn from_node(node: &Node) -> Result<Self> {
101        let red_min = extract_limit(node, "colorRedMinimum")?;
102        let red_max = extract_limit(node, "colorRedMaximum")?;
103        let green_min = extract_limit(node, "colorGreenMinimum")?;
104        let green_max = extract_limit(node, "colorGreenMaximum")?;
105        let blue_min = extract_limit(node, "colorBlueMinimum")?;
106        let blue_max = extract_limit(node, "colorBlueMaximum")?;
107        Ok(Self {
108            red_min,
109            red_max,
110            green_min,
111            green_max,
112            blue_min,
113            blue_max,
114        })
115    }
116
117    pub(crate) fn from_record_types(
118        red: &RecordDataType,
119        green: &RecordDataType,
120        blue: &RecordDataType,
121    ) -> Self {
122        let (red_min, red_max) = red.limits();
123        let (green_min, green_max) = green.limits();
124        let (blue_min, blue_max) = blue.limits();
125        Self {
126            red_min,
127            red_max,
128            green_min,
129            green_max,
130            blue_min,
131            blue_max,
132        }
133    }
134
135    pub(crate) fn xml_string(&self) -> String {
136        let mut xml = String::from("<colorLimits type=\"Structure\">\n");
137        if let Some(min) = &self.red_min {
138            xml += &record_value_to_xml("colorRedMinimum", min);
139        }
140        if let Some(max) = &self.red_max {
141            xml += &record_value_to_xml("colorRedMaximum", max);
142        }
143        if let Some(min) = &self.green_min {
144            xml += &record_value_to_xml("colorGreenMinimum", min);
145        }
146        if let Some(max) = &self.green_max {
147            xml += &record_value_to_xml("colorGreenMaximum", max);
148        }
149        if let Some(min) = &self.blue_min {
150            xml += &record_value_to_xml("colorBlueMinimum", min);
151        }
152        if let Some(max) = &self.blue_max {
153            xml += &record_value_to_xml("colorBlueMaximum", max);
154        }
155        xml += "</colorLimits>\n";
156        xml
157    }
158}
159
160/// Converts a record value to a XML limit tag with the correct type
161fn record_value_to_xml(tag_name: &str, value: &RecordValue) -> String {
162    match value {
163        RecordValue::Integer(value) => {
164            format!("<{tag_name} type=\"Integer\">{value}</{tag_name}>\n")
165        }
166        RecordValue::ScaledInteger(value) => {
167            format!("<{tag_name} type=\"ScaledInteger\">{value}</{tag_name}>\n")
168        }
169        RecordValue::Single(value) => {
170            format!("<{tag_name} type=\"Float\" precision=\"single\">{value}</{tag_name}>\n")
171        }
172        RecordValue::Double(value) => format!("<{tag_name} type=\"Float\">{value}</{tag_name}>\n"),
173    }
174}