1use crate::layers::{Layer, dist_sq_to_segment, projection_factor, serde_stroke};
33use crate::projection::{GeoPos, MapProjection};
34use egui::{Color32, Painter, Pos2, Response, Stroke};
35use serde::{Deserialize, Serialize};
36use std::any::Any;
37
38#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
40pub struct Polyline(pub Vec<GeoPos>);
41
42#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
44pub enum DrawMode {
45 #[default]
47 Disabled,
48 Draw,
50 Erase,
52}
53
54#[derive(Clone, Serialize, Deserialize)]
56#[serde(default)]
57pub struct DrawingLayer {
58 polylines: Vec<Polyline>,
59
60 #[serde(with = "serde_stroke")]
62 pub stroke: Stroke,
63
64 #[serde(skip)]
66 pub draw_mode: DrawMode,
67}
68
69impl DrawingLayer {
70 #[cfg(feature = "geojson")]
72 #[cfg(feature = "geojson")]
74 pub fn to_geojson_str(&self, layer_id: &str) -> Result<String, serde_json::Error> {
75 let features: Vec<geojson::Feature> = self
76 .polylines
77 .clone()
78 .into_iter()
79 .map(|p| {
80 let mut feature = geojson::Feature::from(p);
81 if let Some(properties) = &mut feature.properties {
82 properties.insert(
83 "stroke_width".to_string(),
84 serde_json::Value::from(self.stroke.width),
85 );
86 properties.insert(
87 "stroke_color".to_string(),
88 serde_json::Value::String(self.stroke.color.to_hex()),
89 );
90 properties.insert(
91 "layer_id".to_string(),
92 serde_json::Value::String(layer_id.to_string()),
93 );
94 }
95 feature
96 })
97 .collect();
98
99 let mut foreign_members = serde_json::Map::new();
100 foreign_members.insert(
101 "stroke_width".to_string(),
102 serde_json::Value::from(self.stroke.width),
103 );
104 foreign_members.insert(
105 "stroke_color".to_string(),
106 serde_json::Value::String(self.stroke.color.to_hex()),
107 );
108 foreign_members.insert(
109 "layer_id".to_string(),
110 serde_json::Value::String(layer_id.to_string()),
111 );
112
113 let feature_collection = geojson::FeatureCollection {
114 bbox: None,
115 features,
116 foreign_members: Some(foreign_members),
117 };
118 serde_json::to_string(&feature_collection)
119 }
120
121 #[cfg(feature = "geojson")]
126 pub fn from_geojson_str(
127 &mut self,
128 s: &str,
129 layer_id: Option<&str>,
130 ) -> Result<(), serde_json::Error> {
131 let feature_collection: geojson::FeatureCollection = serde_json::from_str(s)?;
132 let new_polylines: Vec<Polyline> = feature_collection
133 .features
134 .iter()
135 .filter_map(|f| {
136 if let Some(target_id) = layer_id {
138 if let Some(properties) = &f.properties {
139 if let Some(val) = properties.get("layer_id") {
140 if let Some(id) = val.as_str() {
141 if id != target_id {
142 return None;
143 }
144 } else {
145 return None;
147 }
148 } else {
149 return None;
151 }
152 } else {
153 return None;
155 }
156 }
157
158 let polyline = Polyline::try_from(f.clone()).ok();
159 if polyline.is_some()
160 && let Some(properties) = &f.properties
161 {
162 if let Some(value) = properties.get("stroke_width")
163 && let Some(width) = value.as_f64()
164 {
165 self.stroke.width = width as f32;
166 }
167 if let Some(value) = properties.get("stroke_color")
168 && let Some(s) = value.as_str()
169 && let Ok(color) = Color32::from_hex(s)
170 {
171 self.stroke.color = color;
172 }
173 }
174 polyline
175 })
176 .collect();
177 self.polylines.extend(new_polylines);
178
179 if let Some(foreign_members) = feature_collection.foreign_members {
180 if let Some(value) = foreign_members.get("stroke_width")
181 && let Some(width) = value.as_f64()
182 {
183 self.stroke.width = width as f32;
184 }
185 if let Some(value) = foreign_members.get("stroke_color")
186 && let Some(s) = value.as_str()
187 && let Ok(color) = Color32::from_hex(s)
188 {
189 self.stroke.color = color;
190 }
191 }
192
193 Ok(())
194 }
195
196 #[must_use]
198 pub fn new(stroke: Stroke) -> Self {
199 Self {
200 polylines: Vec::new(),
201 stroke,
202 draw_mode: DrawMode::default(),
203 }
204 }
205}
206
207impl Default for DrawingLayer {
208 fn default() -> Self {
209 Self {
210 polylines: Vec::new(),
211 stroke: Stroke::new(2.0, Color32::RED),
212 draw_mode: DrawMode::default(),
213 }
214 }
215}
216
217impl DrawingLayer {
218 fn handle_draw_input(&mut self, response: &Response, projection: &MapProjection) -> bool {
219 if response.hovered() {
220 response.ctx.set_cursor_icon(egui::CursorIcon::Crosshair);
221 }
222
223 if response.clicked()
224 && let Some(pointer_pos) = response.interact_pointer_pos()
225 {
226 let geo_pos = projection.unproject(pointer_pos);
227 if let Some(last_line) = self.polylines.last_mut()
228 && response.ctx.input(|i| i.modifiers.shift)
229 {
230 last_line.0.push(geo_pos);
231 } else {
232 let geo_pos2 = projection.unproject(pointer_pos + egui::vec2(1.0, 0.0));
234 self.polylines.push(Polyline(vec![geo_pos, geo_pos2]));
235 }
236 }
237
238 if response.drag_started() {
239 self.polylines.push(Polyline(Vec::new()));
240 }
241
242 if response.dragged()
243 && let Some(pointer_pos) = response.interact_pointer_pos()
244 && let Some(last_line) = self.polylines.last_mut()
245 {
246 let geo_pos = projection.unproject(pointer_pos);
247 last_line.0.push(geo_pos);
248 }
249
250 response.hovered()
253 }
254
255 fn handle_erase_input(&mut self, response: &Response, projection: &MapProjection) -> bool {
256 if response.hovered() {
257 response.ctx.set_cursor_icon(egui::CursorIcon::NotAllowed);
258 }
259
260 if (response.dragged() || response.clicked())
261 && let Some(pointer_pos) = response.interact_pointer_pos()
262 {
263 self.erase_at(pointer_pos, projection);
264 }
265 response.hovered()
266 }
267
268 fn erase_at(&mut self, pointer_pos: Pos2, projection: &MapProjection) {
269 let erase_radius_screen = self.stroke.width;
270 let erase_radius_sq = erase_radius_screen * erase_radius_screen;
271
272 let old_polylines = std::mem::take(&mut self.polylines);
273 self.polylines = old_polylines
274 .into_iter()
275 .flat_map(|polyline| {
276 split_polyline_by_erase_circle(
277 &polyline.0,
278 pointer_pos,
279 erase_radius_sq,
280 projection,
281 )
282 .into_iter()
283 .map(Polyline)
284 })
285 .collect();
286 }
287}
288
289impl Layer for DrawingLayer {
290 fn as_any(&self) -> &dyn Any {
291 self
292 }
293
294 fn as_any_mut(&mut self) -> &mut dyn Any {
295 self
296 }
297
298 fn handle_input(&mut self, response: &Response, projection: &MapProjection) -> bool {
299 match self.draw_mode {
300 DrawMode::Disabled => false,
301 DrawMode::Draw => self.handle_draw_input(response, projection),
302 DrawMode::Erase => self.handle_erase_input(response, projection),
303 }
304 }
305
306 fn draw(&self, painter: &Painter, projection: &MapProjection) {
307 for polyline in &self.polylines {
308 if polyline.0.len() > 1 {
309 let screen_points: Vec<egui::Pos2> =
310 polyline.0.iter().map(|p| projection.project(*p)).collect();
311 painter.add(egui::Shape::line(screen_points, self.stroke));
312 }
313 }
314 }
315}
316
317fn split_polyline_by_erase_circle(
319 polyline: &[GeoPos],
320 pointer_pos: Pos2,
321 erase_radius_sq: f32,
322 projection: &MapProjection,
323) -> Vec<Vec<GeoPos>> {
324 if polyline.len() < 2 {
325 return vec![];
326 }
327
328 let screen_points: Vec<Pos2> = polyline.iter().map(|p| projection.project(*p)).collect();
329
330 let mut new_polylines = Vec::new();
331 let mut current_line = Vec::new();
332 let mut in_visible_part = true;
333
334 if dist_sq_to_segment(pointer_pos, screen_points[0], screen_points[1]) < erase_radius_sq {
336 in_visible_part = false;
337 } else {
338 current_line.push(polyline[0]);
339 }
340
341 for i in 0..(polyline.len() - 1) {
342 let p2_geo = polyline[i + 1];
343 let p1_screen = screen_points[i];
344 let p2_screen = screen_points[i + 1];
345
346 let segment_is_erased =
347 dist_sq_to_segment(pointer_pos, p1_screen, p2_screen) < erase_radius_sq;
348
349 if in_visible_part {
350 if segment_is_erased {
351 let t = projection_factor(pointer_pos, p1_screen, p2_screen);
353 let split_point_screen = p1_screen.lerp(p2_screen, t);
354 let split_point_geo = projection.unproject(split_point_screen);
355 current_line.push(split_point_geo);
356
357 if current_line.len() > 1 {
358 new_polylines.push(std::mem::take(&mut current_line));
359 }
360 in_visible_part = false;
361 } else {
362 current_line.push(p2_geo);
364 }
365 } else {
366 if !segment_is_erased {
368 let t = projection_factor(pointer_pos, p1_screen, p2_screen);
370 let split_point_screen = p1_screen.lerp(p2_screen, t);
371 let split_point_geo = projection.unproject(split_point_screen);
372
373 current_line.push(split_point_geo);
375 current_line.push(p2_geo);
376 in_visible_part = true;
377 }
378 }
380 }
381
382 if current_line.len() > 1 {
383 new_polylines.push(current_line);
384 }
385
386 new_polylines
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn drawing_layer_new() {
395 let layer = DrawingLayer::default();
396 assert_eq!(layer.draw_mode, DrawMode::Disabled);
397 assert!(layer.polylines.is_empty());
398 }
399
400 #[test]
401 fn drawing_layer_as_any() {
402 let layer = DrawingLayer::default();
403 assert!(layer.as_any().is::<DrawingLayer>());
404 }
405
406 #[test]
407 fn drawing_layer_as_any_mut() {
408 let mut layer = DrawingLayer::default();
409 assert!(layer.as_any_mut().is::<DrawingLayer>());
410 }
411
412 #[test]
413 fn drawing_layer_serde() {
414 let mut layer = DrawingLayer::default();
415 layer.draw_mode = DrawMode::Draw; layer.polylines.push(Polyline(vec![
417 GeoPos { lon: 1.0, lat: 2.0 },
418 GeoPos { lon: 3.0, lat: 4.0 },
419 ]));
420 layer.stroke = Stroke::new(5.0, Color32::BLUE); let json = serde_json::to_string(&layer).unwrap();
423
424 assert!(json.contains(r##""polylines":[[{"lon":1.0,"lat":2.0},{"lon":3.0,"lat":4.0}]],"stroke":{"width":5.0,"color":"#0000ffff"}"##));
426 assert!(!json.contains("draw_mode"));
427
428 let deserialized: DrawingLayer = serde_json::from_str(&json).unwrap();
429
430 assert_eq!(deserialized.polylines, layer.polylines);
432
433 assert_eq!(deserialized.stroke.width, 5.0);
435 assert_eq!(deserialized.stroke.color, Color32::BLUE);
436
437 assert_eq!(deserialized.draw_mode, DrawMode::Disabled);
439 }
440
441 #[cfg(feature = "geojson")]
442 mod geojson_tests {
443 use super::*;
444
445 #[test]
446 fn drawing_layer_geojson() {
447 let mut layer = DrawingLayer::default();
448 layer.polylines.push(Polyline(vec![
449 (10.0, 20.0).into(),
450 (30.0, 40.0).into(),
451 (50.0, 60.0).into(),
452 ]));
453 layer.stroke = Stroke::new(5.0, Color32::BLUE);
454
455 let geojson_str = layer.to_geojson_str("my_layer").unwrap();
456
457 let mut new_layer = DrawingLayer::default();
459 new_layer
460 .from_geojson_str(&geojson_str, Some("my_layer"))
461 .unwrap();
462
463 assert_eq!(new_layer.polylines.len(), 1);
464 assert_eq!(layer.polylines[0], new_layer.polylines[0]);
465 assert_eq!(layer.stroke, new_layer.stroke);
466
467 let mut other_layer = DrawingLayer::default();
469 other_layer
470 .from_geojson_str(&geojson_str, Some("other_layer"))
471 .unwrap();
472 assert_eq!(other_layer.polylines.len(), 0);
473
474 let mut all_layer = DrawingLayer::default();
476 all_layer.from_geojson_str(&geojson_str, None).unwrap();
477 assert_eq!(all_layer.polylines.len(), 1);
478 }
479 }
480}