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
use crate::error::Result;

/// Dimensions requested for processing
#[derive(Default, Clone, Copy)]
pub struct CoordDimensions {
    /// height
    pub z: bool,
    /// measurement
    pub m: bool,
    /// geodetic decimal year time
    pub t: bool,
    /// time nanosecond measurement
    pub tm: bool,
}

impl CoordDimensions {
    pub fn xy() -> Self {
        CoordDimensions {
            z: false,
            m: false,
            t: false,
            tm: false,
        }
    }
    pub fn xyz() -> Self {
        CoordDimensions {
            z: true,
            m: false,
            t: false,
            tm: false,
        }
    }
    pub fn xyzm() -> Self {
        CoordDimensions {
            z: true,
            m: true,
            t: false,
            tm: false,
        }
    }
    pub fn xym() -> Self {
        CoordDimensions {
            z: false,
            m: true,
            t: false,
            tm: false,
        }
    }
}

/// Geometry processing trait
///
/// # Usage example:
///
/// ```rust
/// use geozero::{GeomProcessor, error::Result};
///
/// struct CoordPrinter;
///
/// impl GeomProcessor for CoordPrinter {
///     fn xy(&mut self, x: f64, y: f64, _idx: usize) -> Result<()> {
///         Ok(println!("({} {})", x, y))
///     }
/// }
/// ```
#[allow(unused_variables)]
pub trait GeomProcessor {
    /// Additional dimensions requested when processing coordinates
    fn dimensions(&self) -> CoordDimensions {
        CoordDimensions::xy()
    }

    /// Request additional dimensions for coordinate processing
    fn multi_dim(&self) -> bool {
        let dimensions = self.dimensions();
        dimensions.z || dimensions.m || dimensions.t || dimensions.tm
    }

    /// SRID of geometries
    ///
    /// Emitted before geometry begin
    fn srid(&mut self, srid: Option<i32>) -> Result<()> {
        Ok(())
    }

    /// Process coordinate with x,y dimensions
    fn xy(&mut self, x: f64, y: f64, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Process coordinate with all requested dimensions
    fn coordinate(
        &mut self,
        x: f64,
        y: f64,
        z: Option<f64>,
        m: Option<f64>,
        t: Option<f64>,
        tm: Option<u64>,
        idx: usize,
    ) -> Result<()> {
        Ok(())
    }

    /// Begin of Point processing
    ///
    /// Next: xy/coordinate
    fn point_begin(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of Point processing
    fn point_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of MultiPoint processing
    ///
    /// Next: size * xy/coordinate
    fn multipoint_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of MultiPoint processing
    fn multipoint_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of LineString processing
    ///
    /// An untagged LineString is either a Polygon ring or part of a MultiLineString
    ///
    /// Next: size * xy/coordinate
    fn linestring_begin(&mut self, tagged: bool, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of LineString processing
    fn linestring_end(&mut self, tagged: bool, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of MultiLineString processing
    ///
    /// Next: size * LineString (untagged)
    fn multilinestring_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of MultiLineString processing
    fn multilinestring_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of Polygon processing
    ///
    /// An untagged Polygon is part of a MultiPolygon
    ///
    /// Next: size * LineString (untagged) = rings
    fn polygon_begin(&mut self, tagged: bool, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of Polygon processing
    fn polygon_end(&mut self, tagged: bool, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of MultiPolygon processing
    ///
    /// Next: size * Polygon (untagged)
    fn multipolygon_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of MultiPolygon processing
    fn multipolygon_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of GeometryCollection processing
    fn geometrycollection_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of GeometryCollection processing
    fn geometrycollection_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of CircularString processing
    ///
    /// The CircularString is the basic curve type, similar to a LineString in the linear world. A single segment required three points, the start and end points (first and third) and any other point on the arc. The exception to this is for a closed circle, where the start and end points are the same. In this case the second point MUST be the center of the arc, ie the opposite side of the circle. To chain arcs together, the last point of the previous arc becomes the first point of the next arc, just like in LineString. This means that a valid circular string must have an odd number of points greated than 1.
    ///
    /// Next: size * xy/coordinate
    fn circularstring_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of CircularString processing
    fn circularstring_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of CompoundCurve processing
    ///
    /// A compound curve is a single, continuous curve that has both curved (circular) segments and linear segments. That means that in addition to having well-formed components, the end point of every component (except the last) must be coincident with the start point of the following component.
    ///
    /// Next: size * (CircularString | LineString (untagged))
    fn compoundcurve_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of CompoundCurve processing
    fn compoundcurve_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of CurvePolygon processing
    ///
    /// A CurvePolygon is just like a polygon, with an outer ring and zero or more inner rings. The difference is that a ring can take the form of a circular string, linear string or compound string.
    ///
    /// Next: size * (CircularString | LineString (untagged) | CompoundCurve)
    fn curvepolygon_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of CurvePolygon processing
    fn curvepolygon_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of MultiCurve processing
    ///
    /// The MultiCurve is a collection of curves, which can include linear strings, circular strings or compound strings.
    ///
    /// Next: size * (CircularString | LineString (untagged) | CompoundCurve)
    fn multicurve_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of MultiCurve processing
    fn multicurve_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of MultiSurface processing
    ///
    /// The MultiSurface is a collection of surfaces, which can be (linear) polygons or curve polygons.
    ///
    /// Next: size * (CurvePolygon | Polygon (untagged))
    fn multisurface_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of MultiSurface processing
    fn multisurface_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }
    /// Begin of Triangle processing
    ///
    /// An untagged Triangle is part of a Tin
    ///
    /// Next: size * LineString (untagged) = rings
    fn triangle_begin(&mut self, tagged: bool, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of Polygon processing
    fn triangle_end(&mut self, tagged: bool, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of PolyhedralSurface processing
    ///
    /// Next: size * Polygon (untagged)
    fn polyhedralsurface_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of MultiPolygon processing
    fn polyhedralsurface_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of Tin processing
    ///
    /// Next: size * Polygon (untagged)
    fn tin_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of MultiPolygon processing
    fn tin_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }
}

#[test]
fn error_message() {
    use crate::error::GeozeroError;
    struct Test;
    impl GeomProcessor for Test {
        fn linestring_begin(&mut self, _tagged: bool, _size: usize, _idx: usize) -> Result<()> {
            Err(GeozeroError::Geometry("test".to_string()))
        }
    }
    assert_eq!(
        Test {}
            .linestring_begin(false, 0, 0)
            .err()
            .unwrap()
            .to_string(),
        "processing geometry `test`".to_string()
    );
}