• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geo-engine / geoengine / 3929938005

pending completion
3929938005

push

github

GitHub
Merge #713

84930 of 96741 relevant lines covered (87.79%)

79640.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.62
/datatypes/src/plots/box_plot.rs
1
use crate::error;
2
use crate::plots::{Plot, PlotData, PlotMetaData};
3
use crate::util::Result;
4
use serde::{Deserialize, Serialize};
5
use snafu::ensure;
6

7
/// A box plot consists of multiple boxes (`BoxPlotAttribute`)
8
#[derive(Debug, Deserialize, Serialize)]
26✔
9
#[serde(rename_all = "camelCase")]
10
pub struct BoxPlot {
11
    values: Vec<BoxPlotAttribute>,
12
}
13

14
impl BoxPlot {
15
    /// Creates a new box plot without any boxes.
16
    pub fn new() -> BoxPlot {
25✔
17
        BoxPlot { values: vec![] }
25✔
18
    }
25✔
19

20
    /// Adds a new box/attribute to this box plot.
21
    pub fn add_attribute(&mut self, value: BoxPlotAttribute) {
25✔
22
        self.values.push(value);
25✔
23
    }
25✔
24
}
25

26
impl Default for BoxPlot {
27
    fn default() -> Self {
×
28
        BoxPlot::new()
×
29
    }
×
30
}
31

32
/// Represents a single box of a box plot including whiskers
33
#[derive(Debug, Deserialize, Serialize)]
26✔
34
#[serde(rename_all = "camelCase")]
35
pub struct BoxPlotAttribute {
36
    pub name: String,
37
    pub min: f64,
38
    pub max: f64,
39
    pub median: f64,
40
    pub q1: f64,
41
    pub q3: f64,
42
    pub is_exact: bool,
43
}
44

45
impl BoxPlotAttribute {
46
    pub fn new(
29✔
47
        name: String,
29✔
48
        min: f64,
29✔
49
        max: f64,
29✔
50
        median: f64,
29✔
51
        q1: f64,
29✔
52
        q3: f64,
29✔
53
        is_exact: bool,
29✔
54
    ) -> Result<BoxPlotAttribute> {
29✔
55
        ensure!(
29✔
56
            !min.is_nan() && !max.is_nan() && !median.is_nan() && !q1.is_nan() && !q3.is_nan(),
29✔
57
            error::Plot {
1✔
58
                details: "NaN values not allowed in box plots."
1✔
59
            }
1✔
60
        );
61

62
        ensure!(
28✔
63
            min <= q1 && q1 <= median && median <= q3 && q3 <= max,
28✔
64
            error::Plot {
1✔
65
                details: format!("Illegal box plot values. min: {min}, q1: {q1}, median: {median}, q3: {q3}, max: {max}")
1✔
66
            }
1✔
67
        );
68

69
        Ok(BoxPlotAttribute {
27✔
70
            name,
27✔
71
            min,
27✔
72
            max,
27✔
73
            median,
27✔
74
            q1,
27✔
75
            q3,
27✔
76
            is_exact,
27✔
77
        })
27✔
78
    }
29✔
79
}
80

81
impl Plot for BoxPlot {
82
    fn to_vega_embeddable(&self, _allow_interactions: bool) -> Result<PlotData> {
25✔
83
        let vega_spec = serde_json::json!({
25✔
84
            "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
25✔
85
            "width": "container",
25✔
86
            "data": self,
25✔
87
            "encoding": {"x": {"field": "name", "type": "nominal" }},
25✔
88
            "layer": [
25✔
89
              {
25✔
90
                "mark": {"type": "rule"},
25✔
91
                "encoding": {
25✔
92
                  "y": {"field": "min", "type": "quantitative", "scale": {"zero": false} },
25✔
93
                  "y2": {"field": "max"}
25✔
94
                }
25✔
95
              },
25✔
96
              {
25✔
97
                "mark": {"type": "bar", "cornerRadius": 5, "width": {"band": 0.75} },
25✔
98
                "encoding": {
25✔
99
                  "y": {"field": "q1", "type": "quantitative"},
25✔
100
                  "y2": {"field": "q3"},
25✔
101
                  "color": {"field": "name", "type": "nominal"}
25✔
102
                }
25✔
103
              },
25✔
104
              {
25✔
105
                "mark": {"type": "rect", "color": "white", "width": { "band": 0.75}, "height": 1  },
25✔
106
                "encoding": {"y": {"field": "median", "type": "quantitative"} }
25✔
107
              }
25✔
108
            ],
25✔
109
            "config": {
25✔
110
              "axisXDiscrete": { "title": null },
25✔
111
              "axisYQuantitative": { "title": null },
25✔
112
              "legend": {
25✔
113
                "disable": true
25✔
114
              }
25✔
115
            }
25✔
116
        });
25✔
117

25✔
118
        Ok(PlotData {
25✔
119
            vega_string: vega_spec.to_string(),
25✔
120
            metadata: PlotMetaData::None,
25✔
121
        })
25✔
122
    }
25✔
123
}
124

125
#[cfg(test)]
126
mod tests {
127
    use crate::plots::box_plot::BoxPlotAttribute;
128
    use crate::plots::{BoxPlot, Plot, PlotData, PlotMetaData};
129

130
    #[test]
1✔
131
    fn test_ser() {
1✔
132
        let mut bp = BoxPlot::new();
1✔
133
        bp.add_attribute(
1✔
134
            BoxPlotAttribute::new("A1".to_string(), 12.0, 83.0, 35.0, 20.0, 55.0, true).unwrap(),
1✔
135
        );
1✔
136

1✔
137
        let ser = serde_json::to_value(&bp).unwrap();
1✔
138

1✔
139
        let expected = serde_json::json!({
1✔
140
            "values": [
1✔
141
                { "name": "A1", "min": 12.0, "max": 83.0, "median" : 35.0, "q1": 20.0, "q3": 55.0, "isExact": true }
1✔
142
            ]
1✔
143
        });
1✔
144
        assert_eq!(expected, ser);
1✔
145

146
        assert_eq!(
1✔
147
            bp.to_vega_embeddable(false).unwrap(),
1✔
148
            PlotData {
1✔
149
                vega_string: r#"{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","config":{"axisXDiscrete":{"title":null},"axisYQuantitative":{"title":null},"legend":{"disable":true}},"data":{"values":[{"isExact":true,"max":83.0,"median":35.0,"min":12.0,"name":"A1","q1":20.0,"q3":55.0}]},"encoding":{"x":{"field":"name","type":"nominal"}},"layer":[{"encoding":{"y":{"field":"min","scale":{"zero":false},"type":"quantitative"},"y2":{"field":"max"}},"mark":{"type":"rule"}},{"encoding":{"color":{"field":"name","type":"nominal"},"y":{"field":"q1","type":"quantitative"},"y2":{"field":"q3"}},"mark":{"cornerRadius":5,"type":"bar","width":{"band":0.75}}},{"encoding":{"y":{"field":"median","type":"quantitative"}},"mark":{"color":"white","height":1,"type":"rect","width":{"band":0.75}}}],"width":"container"}"#.to_owned(),
1✔
150
                metadata: PlotMetaData::None
1✔
151
            }
1✔
152
        );
1✔
153
    }
1✔
154

155
    #[test]
1✔
156
    fn ok() {
1✔
157
        assert!(
1✔
158
            BoxPlotAttribute::new("A1".to_string(), 12.0, 83.0, 35.0, 20.0, 55.0, true).is_ok()
1✔
159
        );
1✔
160
        assert!(
1✔
161
            BoxPlotAttribute::new("A2".to_string(), 35.0, 112.0, 76.0, 55.0, 99.0, false).is_ok()
1✔
162
        );
1✔
163
    }
1✔
164

165
    #[test]
1✔
166
    fn nan() {
1✔
167
        assert!(
1✔
168
            BoxPlotAttribute::new("A1".to_string(), f64::NAN, 83.0, 35.0, 20.0, 55.0, true)
1✔
169
                .is_err()
1✔
170
        );
1✔
171
    }
1✔
172

173
    #[test]
1✔
174
    fn illegal_order() {
1✔
175
        assert!(
1✔
176
            BoxPlotAttribute::new("A1".to_string(), 12.0, 35.0, 83.0, 20.0, 55.0, true).is_err()
1✔
177
        );
1✔
178
    }
1✔
179
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc