1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
//! Properties from various slides
//!

mod openslide;
mod tiff;
mod aperio;
//mod hamamatsu;

use std::collections::HashMap;

use self::openslide::LevelProperties;

/// This struct defines an inferface to the various properties of the various formats.
///
/// These properties are also available as a `HashMap<String, String>` which can be obtained with
/// the `OpenSlide::get_properties()` method. However, this way of interacting with the property
/// values is not ideal, hence this struct. Motivation:
///
///   - Get the values with fitting types in stead of Strings, ready to be used straight away.
///   - Every property is easier to document.
///   - More convenient naming (arguable).
///
/// Many formats implements openslide properties (`openslide.<property>` in the HashMap returned by
/// the `OpenSlide::get_properties()` method), and many formats also implements some Tiff
/// properties (`openslide.<property>` in the HashMap returned by the `OpenSlide::get_properties()`
/// method). Then there are properties that are unique to each format (it may be the same
/// properties, but with different naming conventions etc.). This interface gives the programmer
/// access to all (known) properties (if some exists and are not implemented here, this is a bug).
/// If some property does not exist for some slide, the method for this property returns `None`.
/// What properties that are available to each slide is somewhat arbitrary (or, at least unknown to
/// the author of this library per now), so in order to discover available properties, you can
/// print the result of the `OpenSlide::get_properties()` method, or use the
/// `Properties::print_available()` method (recommended).
///
#[derive(Clone, Debug)]
pub struct Properties {
    openslide_properties: openslide::OpenSlide,
    tiff_properties: tiff::Tiff,
    aperio_properties: aperio::Aperio,
}

impl Properties {

    /// Initialises a new `Properties` struct.
    ///
    /// This is done by submitting a property_map, which is obtained from the
    /// `OpenSlide::get_properties()` method, but this is abstracted away from the user, and
    /// happens automatically when defining an `OpenSlide` struct.
    pub fn new(property_map: &HashMap<String, String>) -> Self {
        let mut tiff_properties = tiff::Tiff::default();
        // Openslide properties requires special treatement because we need to find out how many
        // levels there are in the initialization.
        let mut openslide_properties = openslide::OpenSlide::new(property_map);
        let mut aperio_properties = aperio::Aperio::default();

        for (key, value) in property_map {
            let parent = key.split('.').nth(0);
            match parent {
                Some("openslide") => openslide_properties.parse_property_name(key, value),
                Some("tiff") => tiff_properties.parse_property_name(key, value),
                Some("aperio") => aperio_properties.parse_property_name(key, value),
                _ => println!("Could not parse {}", key),
            }
        }

        Properties {
            tiff_properties: tiff_properties,
            openslide_properties: openslide_properties,
            aperio_properties: aperio_properties,
        }
    }

    /// Print available properties (key, value) (where the value is not `None`).
    ///
    /// # OpenSlide properties
    pub fn print_available(&self) {
        self.openslide_properties.print_available();
        self.tiff_properties.print_available();
        self.aperio_properties.print_available();
    }

    // Openslide properties (the markdown header is on the method above)

    /// Slide vendor
    pub fn vendor(&self) -> Option<String> {
        self.openslide_properties.vendor.clone()
    }

    /// Quickhash 1
    pub fn quickhash_1(&self) -> Option<String> {
        self.openslide_properties.quickhash_1.clone()
    }

    /// Micrometer (microns) per pixel in the x direction.
    pub fn mpp_x(&self) -> Option<f32> {
        // TODO: Replace x / y direction with horisontal / vertical in documentation
        self.openslide_properties.mpp_x
    }

    /// Micrometer (microns) per pixel in the y direction.
    pub fn mpp_y(&self) -> Option<f32> {
        // TODO: Replace x / y direction with horisontal / vertical in documentation
        self.openslide_properties.mpp_y
    }

    /// Objective power
    pub fn objective_power(&self) -> Option<u32> {
        self.openslide_properties.objective_power
    }

    /// Comment
    pub fn comment(&self) -> Option<String> {
        self.openslide_properties.comment.clone()
    }

    /// Number of zoom levels
    pub fn level_count(&self) -> Option<u32> {
        self.openslide_properties.level_count
    }

    /// Vector of level-dependent properties. The position in the returned vector corresponds to
    /// the zoom level.
    ///
    /// # Tiff properties
    pub fn levels(&self) -> Option<Vec<LevelProperties>> {
        self.openslide_properties.levels.clone()
    }

    // Tiff properties (the markdown header is on the method above)

    pub fn image_description(&self) -> Option<String> {
        self.tiff_properties.image_description.clone()
    }

    pub fn software(&self) -> Option<String> {
        self.tiff_properties.software.clone()
    }

    /// Model name
    pub fn model(&self) -> Option<String> {
        self.tiff_properties.model.clone()
    }

    pub fn date_time(&self) -> Option<String> {
        self.tiff_properties.date_time.clone()
    }

    pub fn make(&self) -> Option<String> {
        self.tiff_properties.make.clone()
    }

    /// Resolution in the x direction
    pub fn x_resolution(&self) -> Option<f32> {
        // TODO: Replace x / y direction with horisontal / vertical in documentation
        self.tiff_properties.x_resolution
    }

    /// Resolution in the y direction
    pub fn y_resolution(&self) -> Option<f32> {
        // TODO: Replace x / y direction with horisontal / vertical in documentation
        self.tiff_properties.y_resolution
    }

    /// Resolution unit (e.g. centimeter or inch)
    ///
    /// # Aperio properties
    pub fn resolution_unit(&self) -> Option<String> {
        self.tiff_properties.resolution_unit.clone()
    }

    // Aperio properties (the markdown header is on the method above)

    /// Slide filename
    pub fn filename(&self) -> Option<String> {
        self.aperio_properties.filename.clone()
    }

    /// Slide image id
    pub fn image_id(&self) -> Option<String> {
        self.aperio_properties.image_id.clone()
    }

    /// ScanScope id
    pub fn scan_scope_id(&self) -> Option<String> {
        self.aperio_properties.scan_scope_id.clone()
    }

    /// Date of creation (mm/dd/yy)
    pub fn date(&self) -> Option<String> {
        // TODO: Change this to a rust date type
        self.aperio_properties.date.clone()
    }

    /// Time of creation (hh:mm:ss)
    pub fn time(&self) -> Option<String> {
        // TODO: Change this to a rust time type
        self.aperio_properties.time.clone()
    }

    /// User
    pub fn user(&self) -> Option<String> {
        self.aperio_properties.user.clone()
    }

    /// ICC profile
    pub fn icc_profile(&self) -> Option<String> {
        self.aperio_properties.icc_profile.clone()
    }

    /// Parmset
    pub fn parmset(&self) -> Option<String> {
        self.aperio_properties.parmset.clone()
    }

    /// Slide height
    pub fn original_height(&self) -> Option<u32> {
        self.aperio_properties.original_height
    }

    /// Slide width
    pub fn original_width(&self) -> Option<u32> {
        self.aperio_properties.original_height
    }

    pub fn top(&self) -> Option<f32> {
        self.aperio_properties.top
    }

    pub fn left(&self) -> Option<f32> {
        self.aperio_properties.left
    }

    /// Micrometer per pixel
    pub fn mpp(&self) -> Option<f32> {
        self.aperio_properties.mpp
    }

    /// Line camera skew
    pub fn line_camera_skew(&self) -> Option<f32> {
        self.aperio_properties.line_camera_skew
    }

    /// Line area offset in horizontal(?) direction
    pub fn line_area_x_offset(&self) -> Option<f32> {
        self.aperio_properties.line_area_x_offset
    }

    /// Line area offset in vertical(?) direction
    pub fn line_area_y_offset(&self) -> Option<f32> {
        self.aperio_properties.line_area_y_offset
    }

    /// Focus offset
    pub fn focus_offset(&self) -> Option<f32> {
        self.aperio_properties.focus_offset
    }

    pub fn app_mag(&self) -> Option<u32> {
        self.aperio_properties.app_mag
    }

    /// Scan stripe width
    pub fn stripe_width(&self) -> Option<u32> {
        self.aperio_properties.stripe_width
    }

    pub fn filtered(&self) -> Option<u32> {
        self.aperio_properties.filtered
    }
}