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