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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
use crate::error::{GeozeroError, 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 const fn xy() -> Self {
        CoordDimensions {
            z: false,
            m: false,
            t: false,
            tm: false,
        }
    }
    pub const fn xyz() -> Self {
        CoordDimensions {
            z: true,
            m: false,
            t: false,
            tm: false,
        }
    }
    pub const fn xyzm() -> Self {
        CoordDimensions {
            z: true,
            m: true,
            t: false,
            tm: false,
        }
    }
    pub const 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(())
    }

    /// Process empty coordinates, like WKT's `POINT EMPTY`
    ///
    /// - `idx` is the positional index inside this geometry. `idx` will usually be 0 except in the
    ///   case of a MultiPoint or GeometryCollection.
    fn empty_point(&mut self, idx: usize) -> Result<()> {
        Err(GeozeroError::Geometry(
            "The input was an empty Point, but the output doesn't support empty Points".to_string(),
        ))
    }

    /// 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` calls to [`xy()`][`Self::xy()`] or [`coordinate()`][`Self::coordinate()`]
    ///
    /// ## Parameters
    ///
    /// - `size`: the number of Points in this MultiPoint
    /// - `idx`: the positional index of this MultiPoint. This will be 0 except in the case of a
    ///   GeometryCollection.
    ///
    /// ## Following events
    ///
    /// - `size` calls to [`xy()`][`Self::xy()`] or [`coordinate()`][`Self::coordinate()`] for each point.
    /// - [`multipoint_end`][Self::multipoint_end()] to end this MultiPoint
    ///
    /// As of v0.12, `point_begin` and `point_end` are **not** called for each point in a
    /// MultiPoint. See also discussion in [#184](https://github.com/georust/geozero/issues/184).
    fn multipoint_begin(&mut self, size: usize, idx: usize) -> Result<()> {
        Ok(())
    }

    /// End of MultiPoint processing
    ///
    /// - `idx`: the positional index of this MultiPoint. This will be 0 except in the case of a
    ///   GeometryCollection.
    fn multipoint_end(&mut self, idx: usize) -> Result<()> {
        Ok(())
    }

    /// Begin of `LineString` processing
    ///
    /// ## Parameters
    ///
    /// - `tagged`: if `false`, this `LineString` is either a Polygon ring or part of a `MultiLineString`
    /// - `size`: the number of coordinates in this LineString
    /// - `idx`: the positional index of this LineString. This will be 0 for a tagged LineString
    ///   except in the case of a GeometryCollection. This can be non-zero for an untagged
    ///   LineString for MultiLineStrings or Polygons with multiple interiors.
    ///
    /// ## Following events
    ///
    /// - `size` calls to [`xy()`][`Self::xy()`] or [`coordinate()`][`Self::coordinate()`] for each coordinate.
    /// - [`linestring_end`][Self::linestring_end()] to end this LineString
    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)
    ///
    /// ## Following events
    ///
    /// - `size` calls to:
    ///     - [`linestring_begin`][Self::linestring_begin] (with `tagged` set to `false`).
    ///     - one or more calls to [`xy()`][`Self::xy()`] or [`coordinate()`][`Self::coordinate()`] for each coordinate in the LineString.
    ///     - [`linestring_end`][Self::linestring_end]
    /// - [`multilinestring_end`][Self::multilinestring_end()] to end this MultiLineString
    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
    ///
    /// ## Parameters
    ///
    /// - `tagged`: if `false`, this `Polygon` is part of a `MultiPolygon`.
    /// - `size`: the number of rings in this Polygon, _including_ the exterior ring.
    /// - `idx`: the positional index of this Polygon. This will be 0 for a tagged Polygon
    ///   except in the case of a GeometryCollection. This can be non-zero for an untagged
    ///   Polygon for a MultiPolygon with multiple interiors
    ///
    /// ## Following events
    ///
    /// - `size` calls to:
    ///     - [`linestring_begin`][Self::linestring_begin] (with `tagged` set to `false`).
    ///     - one or more calls to [`xy()`][`Self::xy()`] or [`coordinate()`][`Self::coordinate()`] for each coordinate in the ring.
    ///     - [`linestring_end`][Self::linestring_end]
    /// - [`polygon_end`][Self::polygon_end()] to end this Polygon
    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
    ///
    /// ## Parameters
    ///
    /// - `size`: the number of Polygons in this MultiPolygon.
    /// - `idx`: the positional index of this MultiPolygon. This will be 0 except in the case of a
    ///   GeometryCollection.
    ///
    /// ## Following events
    ///
    /// - `size` calls to:
    ///     - [`polygon_begin`][Self::polygon_begin] (with `tagged` set to `false`).
    ///     - See [`polygon_begin`][Self::polygon_begin] for its internal calls.
    ///     - [`polygon_end`][Self::polygon_end]
    /// - [`multipolygon_end`][Self::multipolygon_end()] to end this MultiPolygon
    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
    ///
    /// ## Parameters
    ///
    /// - `size`: the number of geometries in this GeometryCollection.
    /// - `idx`: the positional index of this GeometryCollection. This can be greater than 0 for
    ///   nested geometry collections but also when using `GeometryProcessor` to process a
    ///   `Feature` whose geometry is a `GeometryCollection`. For an example of this see [this
    ///   comment](https://github.com/georust/geozero/pull/183#discussion_r1454319662).
    ///
    /// ## Following events
    ///
    /// - `size` calls to one of the internal geometry `begin` and `end` methods, called in pairs.
    /// - [`geometrycollection_end`][Self::geometrycollection_end()] to end this GeometryCollection
    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 greater 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 Triangle 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 PolyhedralSurface 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 Tin 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()
    );
}