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}