lyon_geo_types/
lib.rs

1use geo_types::{Coordinate, LineString, MultiLineString, MultiPolygon, Polygon};
2use lyon_path::geom::Point;
3use lyon_path::{iterator::PathIterator, Path, PathEvent};
4
5pub trait IntoGeoCoordinate {
6    fn into_coord(self) -> Coordinate<f64>;
7}
8
9impl IntoGeoCoordinate for Point<f32> {
10    fn into_coord(self) -> Coordinate<f64> {
11        Coordinate {
12            x: self.x as f64,
13            y: self.y as f64,
14        }
15    }
16}
17
18pub trait IntoGeoLineStringSimple {
19    fn into_line_string(self) -> LineString<f64>;
20}
21
22impl<'a> IntoGeoLineStringSimple for lyon_path::Polygon<'a, Point<f32>> {
23    fn into_line_string(self) -> LineString<f64> {
24        let mut out = LineString(self.points.into_iter().map(|p| p.into_coord()).collect());
25        if self.closed {
26            out.close()
27        }
28        out
29    }
30}
31
32pub trait IntoGeoMultiLineStringSimple {
33    fn into_multi_line_string(self) -> MultiLineString<f64>;
34}
35
36impl<T> IntoGeoMultiLineStringSimple for T
37where
38    T: IntoGeoLineStringSimple,
39{
40    fn into_multi_line_string(self) -> MultiLineString<f64> {
41        MultiLineString(vec![self.into_line_string()])
42    }
43}
44
45pub trait IntoGeoPolygonSimple {
46    fn into_poly(self) -> Polygon<f64>;
47}
48
49impl<T> IntoGeoPolygonSimple for T
50where
51    T: IntoGeoMultiLineStringSimple,
52{
53    fn into_poly(self) -> Polygon<f64> {
54        let mut mls = self.into_multi_line_string().0;
55        if mls.is_empty() {
56            Polygon::new(LineString(Vec::new()), Vec::new())
57        } else {
58            let exterior = mls.remove(0);
59            Polygon::new(exterior, mls)
60        }
61    }
62}
63
64pub trait IntoGeoMultiLineString {
65    fn into_multi_line_string(self, tolerance: f32) -> MultiLineString<f64>;
66}
67
68impl<T> IntoGeoMultiLineString for T
69where
70    T: IntoIterator<Item = PathEvent> + Sized,
71{
72    fn into_multi_line_string(self, tolerance: f32) -> MultiLineString<f64> {
73        let mut out = Vec::new();
74        let mut current_line = Vec::new();
75        for event in self.into_iter().flattened(tolerance) {
76            match event {
77                PathEvent::Begin { at } => {
78                    current_line = vec![at.into_coord()];
79                }
80                PathEvent::Line { to, .. } => current_line.push(to.into_coord()),
81                PathEvent::End { close, .. } => {
82                    let mut line_string = LineString(current_line.clone());
83                    if close {
84                        line_string.close()
85                    }
86                    out.push(line_string)
87                }
88                _ => unreachable!("only Begin, Line and End PathEvents should be present"),
89            }
90        }
91        MultiLineString(out)
92    }
93}
94
95pub trait IntoGeoPolygon {
96    fn into_poly(self, tolerance: f32) -> Polygon<f64>;
97}
98
99impl<T> IntoGeoPolygon for T
100where
101    T: IntoGeoMultiLineString,
102{
103    fn into_poly(self, tolerance: f32) -> Polygon<f64> {
104        let mut mls = self.into_multi_line_string(tolerance).0;
105        if mls.is_empty() {
106            Polygon::new(LineString(Vec::new()), Vec::new())
107        } else {
108            let exterior = mls.remove(0);
109            Polygon::new(exterior, mls)
110        }
111    }
112}
113
114pub trait IntoGeoMultiPolygon {
115    fn into_multi_poly(self, tolerance: f32) -> MultiPolygon<f64>;
116}
117
118impl<T> IntoGeoMultiPolygon for T
119where
120    T: IntoGeoMultiLineString,
121{
122    fn into_multi_poly(self, tolerance: f32) -> MultiPolygon<f64> {
123        MultiPolygon(
124            self.into_multi_line_string(tolerance)
125                .into_iter()
126                .map(|ls| Polygon::new(ls, Vec::new()))
127                .collect(),
128        )
129    }
130}
131
132pub trait IntoLyonPoint {
133    fn into_point(self) -> Point<f32>;
134}
135
136impl IntoLyonPoint for Coordinate<f64> {
137    fn into_point(self) -> Point<f32> {
138        Point::new(self.x as f32, self.y as f32)
139    }
140}
141
142pub trait IntoLyonPath {
143    fn into_path(self) -> Path;
144}
145
146impl IntoLyonPath for LineString<f64> {
147    fn into_path(self) -> Path {
148        let is_closed = self.is_closed();
149        let mut coords = self.into_iter();
150        let mut builder = Path::builder();
151        if let Some(coord) = coords.next() {
152            builder.begin(coord.into_point());
153        }
154        for coord in coords {
155            builder.line_to(coord.into_point());
156        }
157        builder.end(is_closed);
158        builder.build()
159    }
160}
161
162impl IntoLyonPath for MultiLineString<f64> {
163    fn into_path(self) -> Path {
164        let mut builder = Path::builder();
165        for line_string in self {
166            builder.concatenate(&[line_string.into_path().as_slice()]);
167        }
168        builder.build()
169    }
170}
171
172impl IntoLyonPath for Polygon<f64> {
173    fn into_path(self) -> Path {
174        let mut builder = Path::builder();
175        let (exterior, interior) = self.into_inner();
176        for line_string in std::iter::once(exterior).chain(interior.into_iter()) {
177            builder.concatenate(&[line_string.into_path().as_slice()]);
178        }
179        builder.build()
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186    use lyon::math::point;
187    use lyon::path::{Path, Polygon};
188
189    #[test]
190    fn test_simple() {
191        let path = {
192            let mut builder = Path::builder();
193            builder.begin(point(0., 0.));
194            builder.line_to(point(10., 10.));
195            builder.line_to(point(5., 20.));
196            builder.close();
197            builder.build()
198        };
199        println!("{:?}", path.into_multi_line_string(0.1));
200    }
201
202    #[test]
203    fn test_curve() {
204        let path = {
205            let mut builder = Path::builder();
206            builder.begin(point(0., 0.));
207            builder.line_to(point(10., 10.));
208            builder.quadratic_bezier_to(point(10., 20.), point(5., 20.));
209            builder.close();
210            builder.build()
211        };
212        println!("{:?}", path.into_multi_line_string(0.1));
213    }
214
215    #[test]
216    fn test_svg_multi() {
217        let path = {
218            let mut builder = Path::builder().with_svg();
219            builder.move_to(point(0., 0.));
220            builder.line_to(point(10., 10.));
221            builder.line_to(point(5., 20.));
222            builder.close();
223            builder.move_to(point(20., 30.));
224            builder.line_to(point(40., 50.));
225            builder.line_to(point(30., 40.));
226            builder.close();
227            builder.build()
228        };
229        println!("{:?}", path.into_multi_line_string(0.1));
230    }
231
232    #[test]
233    fn test_polygon() {
234        let poly = Polygon {
235            points: &[
236                point(-100., 100.),
237                point(100., 100.),
238                point(100., -100.),
239                point(-100., -100.),
240            ],
241            closed: true,
242        };
243        println!("{:?}", poly.path_events().into_multi_line_string(0.1));
244    }
245}