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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
//! Computes isorings and contour polygons by applying
//! [marching squares](https://en.wikipedia.org/wiki/Marching_squares)
//! to a rectangular array of numeric values.
//!
//! Outputs ring coordinates (using [`contour_rings`]) or
//! polygons contours (using [`ContourBuilder`]) as a Vec of GeoJSON Feature.
//!
//! This is a port of [d3-contour](https://github.com/d3/d3-contour/).
//!
//! #### Example:
//! ```
//! # use contour::ContourBuilder;
//! let c = ContourBuilder::new(10, 10, false); // x dim., y dim., smoothing
//! let res = c.contours(&vec![
//!     0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
//!     0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
//!     0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
//!     0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
//!     0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
//!     0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
//!     0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
//!     0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
//!     0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
//!     0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
//! ], &[0.5]); // values, thresholds
//! ```
//! __*Output:*__
//! ```text
//! [Feature {
//!   bbox: None,
//!   geometry: Some(Geometry {
//!     bbox: None,
//!     value: MultiPolygon([[[
//!       [6., 7.5], [6., 6.5], [6., 5.5], [6., 4.5],
//!       [6., 3.5], [5.5, 3.], [4.5, 3.], [3.5, 3.],
//!       [3., 3.5], [3., 4.5], [3., 5.5], [3., 6.5],
//!       [3., 7.5], [3.5, 8.], [4.5, 8.], [5.5, 8.],
//!       [6., 7.5]]]]),
//!     foreign_members: None
//!     }),
//!    id: None,
//!    properties: Some({"value": Number(0.5)}),
//!    foreign_members: None
//!    }]
//! ```
//!
//! [`contour_rings`]: fn.contour_rings.html
//! [`ContourBuilder`]: struct.ContourBuilder.html

mod area;
mod contour;

pub use crate::contour::{ContourBuilder, contour_rings};

#[cfg(test)]
mod tests {
    use crate::ContourBuilder;
    use geojson;

    #[test]
    fn test_empty_polygons() {
        let c = ContourBuilder::new(10, 10, true);
        let res = c.contours(&[
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
        ], &[0.5]);
        match res[0].clone().geometry.unwrap().value {
            geojson::Value::MultiPolygon(p) => {
                assert!(p.is_empty());
            }
            _ => panic!(""),
        };
    }

    #[test]
    fn test_simple_polygon() {
        let c = ContourBuilder::new(10, 10, true);
        let res = c.contours(&[
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
        ], &[0.5]);
        match res[0].clone().geometry.unwrap().value {
            geojson::Value::MultiPolygon(p) => {
                assert_eq!(
                    p,
                    vec![vec![vec![
                        vec![6., 7.5], vec![6., 6.5], vec![6., 5.5], vec![6., 4.5],
                        vec![6., 3.5], vec![5.5, 3.], vec![4.5, 3.], vec![3.5, 3.],
                        vec![3., 3.5], vec![3., 4.5], vec![3., 5.5], vec![3., 6.5],
                        vec![3., 7.5], vec![3.5, 8.], vec![4.5, 8.], vec![5.5, 8.],
                        vec![6., 7.5]]]]);
            }
            _ => panic!(""),
        };
    }

    #[test]
    fn test_polygon_with_hole() {
        let c = ContourBuilder::new(10, 10, true);
        let res = c.contours(&[
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
            0., 0., 0., 1., 0., 1., 0., 0., 0., 0.,
            0., 0., 0., 1., 0., 1., 0., 0., 0., 0.,
            0., 0., 0., 1., 0., 1., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
        ], &[0.5]);
        match res[0].clone().geometry.unwrap().value {
            geojson::Value::MultiPolygon(p) => {
                assert_eq!(
                    p,
                    vec![
                        vec![
                            vec![
                                vec![6., 7.5], vec![6., 6.5], vec![6., 5.5],
                                vec![6., 4.5], vec![6., 3.5], vec![5.5, 3.],
                                vec![4.5, 3.], vec![3.5, 3.], vec![3., 3.5],
                                vec![3., 4.5], vec![3., 5.5], vec![3., 6.5],
                                vec![3., 7.5], vec![3.5, 8.], vec![4.5, 8.],
                                vec![5.5, 8.], vec![6., 7.5],
                            ],
                            vec![
                                vec![4.5, 7.], vec![4., 6.5], vec![4., 5.5],
                                vec![4., 4.5], vec![4.5, 4.], vec![5., 4.5],
                                vec![5., 5.5], vec![5., 6.5],vec![4.5, 7.],
                            ],
                        ],
                    ],
                );
            }
            _ => panic!(""),
        };
    }

    #[test]
    fn test_multipolygon() {
        let c = ContourBuilder::new(10, 10, true);
        let res = c.contours(&[
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
            0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
            0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
            0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
            0., 0., 0., 1., 1., 0., 1., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
        ], &[0.5]);
        match res[0].clone().geometry.unwrap().value {
            geojson::Value::MultiPolygon(p) => {
                assert_eq!(
                    p,
                    vec![
                        vec![
                            vec![
                                vec![5., 7.5], vec![5., 6.5], vec![5., 5.5],
                                vec![5., 4.5], vec![5., 3.5], vec![4.5, 3.],
                                vec![3.5, 3.], vec![3., 3.5], vec![3., 4.5],
                                vec![3., 5.5], vec![3., 6.5], vec![3., 7.5],
                                vec![3.5, 8.], vec![4.5, 8.], vec![5., 7.5],
                            ],
                        ],
                        vec![
                            vec![
                                vec![7., 7.5], vec![7., 6.5], vec![7., 5.5],
                                vec![7., 4.5], vec![7., 3.5], vec![6.5, 3.],
                                vec![6., 3.5], vec![6., 4.5], vec![6., 5.5],
                                vec![6., 6.5], vec![6., 7.5], vec![6.5, 8.], vec![7., 7.5],
                            ],
                        ],
                ]);
            }
            _ => panic!(""),
        };
    }


    #[test]
    fn test_multipolygon_with_hole() {
        let c = ContourBuilder::new(10, 10, true);
        let res = c.contours(&[
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 1., 1., 1., 0., 1., 1., 1., 0., 0.,
            0., 1., 0., 1., 0., 1., 0., 1., 0., 0.,
            0., 1., 1., 1., 0., 1., 1., 1., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
        ], &[0.5]);
        match res[0].clone().geometry.unwrap().value {
            geojson::Value::MultiPolygon(p) => {
                assert_eq!(
                    p,
                    vec![
                        vec![
                            vec![
                                vec![4., 5.5], vec![4., 4.5], vec![4., 3.5],
                                vec![3.5, 3.], vec![2.5, 3.], vec![1.5, 3.],
                                vec![1., 3.5], vec![1., 4.5], vec![1., 5.5],
                                vec![1.5, 6.], vec![2.5, 6.], vec![3.5, 6.],
                                vec![4., 5.5],
                            ],
                            vec![
                                vec![2.5, 5.], vec![2., 4.5], vec![2.5, 4.],
                                vec![3., 4.5], vec![2.5, 5.],
                            ],
                        ],
                        vec![
                            vec![
                                vec![8., 5.5], vec![8., 4.5], vec![8., 3.5],
                                vec![7.5, 3.], vec![6.5, 3.], vec![5.5, 3.],
                                vec![5., 3.5], vec![5., 4.5], vec![5., 5.5],
                                vec![5.5, 6.], vec![6.5, 6.], vec![7.5, 6.], vec![8., 5.5],
                            ],
                            vec![
                                vec![6.5, 5.], vec![6., 4.5], vec![6.5, 4.],
                                vec![7., 4.5], vec![6.5, 5.],
                            ],
                        ],
                ]);
            }
            _ => panic!(""),
        };
    }

    #[test]
    fn test_simple_polygon_no_smoothing() {
        let c = ContourBuilder::new(10, 10, false);
        let res = c.contours(&[
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 2., 1., 2., 0., 0., 0., 0.,
            0., 0., 0., 2., 2., 2., 0., 0., 0., 0.,
            0., 0., 0., 1., 2., 1., 0., 0., 0., 0.,
            0., 0., 0., 2., 2., 2., 0., 0., 0., 0.,
            0., 0., 0., 2., 1., 2., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
        ], &[0.5]);
        match res[0].clone().geometry.unwrap().value {
            geojson::Value::MultiPolygon(p) => {
                assert_eq!(
                    p,
                    vec![
                        vec![
                            vec![
                                vec![6., 7.5], vec![6., 6.5], vec![6., 5.5],
                                vec![6., 4.5], vec![6., 3.5], vec![5.5, 3.],
                                vec![4.5, 3.], vec![3.5, 3.], vec![3., 3.5],
                                vec![3., 4.5], vec![3., 5.5], vec![3., 6.5],
                                vec![3., 7.5], vec![3.5, 8.], vec![4.5, 8.],
                                vec![5.5, 8.], vec![6., 7.5],
                            ]
                        ]
                ]);
            }
            _ => panic!(""),
        };
    }

    #[test]
    fn test_multiple_thresholds() {
        let c = ContourBuilder::new(10, 10, true);
        let res = c.contours(&[
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 1., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 1., 0., 0., 0.,
            0., 0., 0., 1., 2., 2., 1., 0., 0., 0.,
            0., 0., 0., 1., 1., 2., 1., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 1., 0., 0., 0.,
            0., 0., 0., 1., 1., 1., 1., 0., 0., 0.,
            0., 0., 0., 0., 0., 0., 0., 0., 0., 0.
        ], &[0.5, 1.5]);
        match res[0].clone().geometry.unwrap().value {
            geojson::Value::MultiPolygon(p) => {
                assert_eq!(
                    p,
                    vec![vec![vec![
                        vec![7.,8.5],vec![7.,7.5],vec![7.,6.5],vec![7.,5.5],vec![7.,4.5],
                        vec![7.,3.5],vec![6.5,3.],vec![5.5,3.],vec![4.5,3.],vec![3.5,3.],
                        vec![3.,3.5],vec![3.,4.5],vec![3.,5.5],vec![3.,6.5],vec![3.,7.5],
                        vec![3.,8.5],vec![3.5,9.],vec![4.5,9.],vec![5.5,9.],vec![6.5,9.],
                        vec![7.,8.5]]
                    ]
                ]);
            }
            _ => panic!(""),
        };
        match res[1].clone().geometry.unwrap().value {
            geojson::Value::MultiPolygon(p) => {
                assert_eq!(
                    p,
                    vec![vec![vec![
                        vec![6.,6.5],vec![6.,5.5],vec![5.5,5.],vec![4.5,5.],
                        vec![4.,5.5],vec![4.5,6.],vec![5.,6.5],vec![5.5,7.],
                        vec![6.,6.5]
                    ]]
                ]);
            }
            _ => panic!(""),
        };
    }
}