diff --git a/src/coords/mapper.rs b/src/coords/mapper.rs index 1c0e71f..9e46d76 100644 --- a/src/coords/mapper.rs +++ b/src/coords/mapper.rs @@ -1,3 +1,5 @@ +use crate::render::WindowCoord; + use super::{proj::ProjectionS, Range}; use geo_types::{coord, Coord as GCoord, LineString}; use proj::{Proj, ProjError}; @@ -7,6 +9,9 @@ pub struct Mapper { proj: Proj, pub range: (Range, Range), bounds: (f64, f64, f64, f64), + scale: f32, + translate: (f32, f32), + focus_point: Option<(f64, f64)>, } impl From for Mapper { fn from(proj: Proj) -> Self { @@ -16,6 +21,9 @@ impl From for Mapper { proj: proj, range: (default_range.0.into(), default_range.1.into()), bounds, + scale: 1.0, + translate: (0.0, 0.0), + focus_point: None, } } } @@ -32,34 +40,92 @@ impl Mapper { proj: Proj, lon_range: std::ops::Range, lat_range: std::ops::Range, + scale: f32, + translate: (f32, f32), ) -> Self { - let bounds = Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap(); + let bounds = + Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap(); let range = (lon_range.into(), lat_range.into()); Self { proj: proj, range, bounds, + scale, + translate, + focus_point: None, } } + pub fn fore_map(&self, coord: WindowCoord) -> Result<(f64, f64), ProjError> { + let (t1, t2) = if let Some(_) = self.focus_point { + let (a, b) = self.translate; + (a * (self.scale - 1.0), b * (self.scale - 1.0)) + } else { + self.translate + }; + let (x, y) = coord; + let c = (x as f64 + t1 as f64) / self.scale as f64 * (self.bounds.1 - self.bounds.0) + + self.bounds.0; + let d = (y as f64 + t2 as f64) / self.scale as f64 * (self.bounds.3 - self.bounds.2) + + self.bounds.2; + let (lon, lat) = self.proj.project((c, d), true)?; + Ok((lon.to_degrees(), lat.to_degrees())) + } + + pub fn set_scale(&mut self, scale: f32) { + self.scale = scale; + } + + pub fn set_translate(&mut self, translate: (f32, f32)) { + self.translate = translate; + } + + pub fn set_focus_point(&mut self, point: (f64, f64)) { + self.focus_point = Some(point); + let (rx, ry) = self.map(point).unwrap(); + self.translate = (rx as f32, ry as f32); + } + + pub fn point_in_bound(&self, point: (f64, f64)) -> bool { + let (x, y) = point; + let (lon_range, lat_range) = self.range; + return (x <= lon_range.1 && x >= lon_range.0) && (y <= lat_range.1 && y >= lat_range.0); + } + pub fn map(&self, point: (f64, f64)) -> Result<(f64, f64), ProjError> { + let mut point = point; + if !self.point_in_bound(point) { + point = ( + point.0.clamp(self.range.0 .0, self.range.0 .1), + point.1.clamp(self.range.1 .0, self.range.1 .1), + ); + } let (p1, p2) = self .proj .convert((point.0.to_radians(), point.1.to_radians()))?; - let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0); - let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2); + let (t1, t2) = if let Some(_) = self.focus_point { + let (a, b) = self.translate; + (a * (self.scale - 1.0), b * (self.scale - 1.0)) + } else { + self.translate + }; + + let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0) * self.scale as f64 + - t1 as f64 * self.scale as f64; + let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2) * self.scale as f64 + - t2 as f64 * self.scale as f64; Ok((x, y)) } - pub fn set_lon_range(&mut self, range: std::ops::Range)->&mut Self { + pub fn set_lon_range(&mut self, range: std::ops::Range) -> &mut Self { self.range.0 = range.into(); self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap(); self } - pub fn set_lat_range(&mut self, range: std::ops::Range)->&mut Self { + pub fn set_lat_range(&mut self, range: std::ops::Range) -> &mut Self { self.range.1 = range.into(); self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap(); self diff --git a/src/monitor/mod.rs b/src/monitor/mod.rs index ce85f09..a8843e5 100644 --- a/src/monitor/mod.rs +++ b/src/monitor/mod.rs @@ -1,14 +1,14 @@ mod imp; use crate::data::RadarData2d; use crate::render::Render; +use adw::prelude::{GLAreaExt, GestureDragExt}; use glib::clone; use glib::subclass::prelude::*; use gtk::glib; use gtk::traits::WidgetExt; use gtk::{EventControllerScrollFlags, Inhibit}; use num_traits::{AsPrimitive, FromPrimitive, Num}; -use proj::{Proj, ProjError}; -use std::borrow::Borrow; +use proj::ProjError; use std::fmt::Debug; glib::wrapper! { @@ -22,32 +22,50 @@ impl Monitor { let pointer_location_detecture = gtk::EventControllerMotion::new(); pointer_location_detecture.connect_motion( clone!(@weak this as s => move |_context, x, y| { - s.imp().renderer.borrow().change_cfg( - |cfg| cfg.pointer_location = (x as f32, y as f32) - ); - }), + s.imp().renderer.borrow_mut().change_pointer(x as f32, y as f32); + } + ), ); - let scale_detecture = gtk::EventControllerScroll::new(EventControllerScrollFlags::VERTICAL); - scale_detecture.connect_scroll(clone!( - @weak this as s => @default-panic,move |_context, _x, y| { - let renderer = s.imp().borrow().renderer.borrow(); + @weak this as s, @weak render as r => @default-panic,move |_context, _x, y| { + let renderer = s.imp().renderer.borrow(); renderer.change_cfg(|cfg| { let current_scale = cfg.scale; - cfg.scale = (current_scale + y as f32 / 100.0).max(1.0).min(5.0); + let scale = (current_scale - y as f32 / 10.0).max(1.0).min(5.0); + cfg.scale = scale; + r.imp().mapper.borrow_mut().set_scale(scale); }); + r.calc_translate(); Inhibit(false) } )); + let drag_detecture = gtk::GestureDrag::new(); + drag_detecture.connect_drag_update(clone!( + @weak render as r => move |this, _, _| { + let (ox, oy) = this.offset().unwrap_or((0.0,0.0)); + r.change_cfg(|cfg|{ + cfg.updated_translate = ( - ox as f32, -oy as f32); + } ); + })); + drag_detecture.connect_drag_end(clone!( + @weak render as r => move |this,_,_|{ + let t = r.imp().translate(); + r.change_cfg(|cfg| { + cfg.translate = t ; + cfg.updated_translate = (0.0,0.0); + }) + } + )); + render.add_controller(pointer_location_detecture); render.add_controller(scale_detecture); + render.add_controller(drag_detecture); render.set_hexpand(true); render.set_vexpand(true); render.set_parent(&this); this.imp().renderer.replace(render); - this } diff --git a/src/render/background/imp.rs b/src/render/background/imp.rs index b20355b..b3287d4 100644 --- a/src/render/background/imp.rs +++ b/src/render/background/imp.rs @@ -1,6 +1,7 @@ use crate::render::{imp, WindowCoord}; use femtovg::Paint; use geo_macros::Prj; +use geo_types::MultiPolygon; use gtk::glib; use gtk::subclass::prelude::*; use std::cell::RefCell; @@ -23,6 +24,7 @@ impl BackgroundConfig { #[derive(Default)] pub struct BackgroundWidget { pub(super) config: RefCell, + pub(super) shp: RefCell>> } #[glib::object_subclass] diff --git a/src/render/background/mod.rs b/src/render/background/mod.rs index a0049ba..7c8e1e8 100644 --- a/src/render/background/mod.rs +++ b/src/render/background/mod.rs @@ -3,6 +3,7 @@ use crate::coords::{Mapper, Range}; use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path}; use geo_types::{line_string, LineString, Point}; use glib::subclass::types::ObjectSubclassIsExt; +use shapefile; use std::cell::Ref; pub use self::imp::BackgroundConfig; @@ -21,6 +22,17 @@ impl BackgroundWidget { pub fn new(config: BackgroundConfig) -> Self { let this: Self = glib::Object::new(); let imp = this.imp(); + let polygons = shapefile::read_as::<_, shapefile::Polygon, shapefile::dbase::Record>( + "/Users/tsuki/Downloads/ne_110m_admin_0_countries/ne_110m_admin_0_countries.shp", + ) + .unwrap(); + + let polys: Vec> = polygons + .into_iter() + .map(|(polygon, _)| polygon.into()) + .collect(); + + imp.shp.replace(Some(polys)); imp.config.replace(config); this } @@ -45,9 +57,11 @@ impl BackgroundWidget { fn mesh_lines( &self, canvas: &mut Canvas, + scale: f32, + translate: (f32, f32), canvas_width: f32, canvas_height: f32, - mapper: Ref<'_, Mapper>, + mapper: &Ref<'_, Mapper>, bound: (Range, Range), ) { let imp = self.imp(); @@ -63,7 +77,13 @@ impl BackgroundWidget { LineString::new( result .points() - .map(|p| (p.x() as f32 * canvas_width, p.y() as f32 * canvas_height).into()) + .map(|p| { + ( + p.x() as f32 * canvas_width - translate.0, + (1.0 - p.y() as f32) * canvas_height - translate.1, + ) + .into() + }) .collect(), ) }); @@ -76,14 +96,16 @@ impl BackgroundWidget { let mut paint = Paint::color(Color::white()); paint.set_font_size(25.0); paint.set_line_width(1.0); - let text_location = mapper.map((left, *lat)).unwrap(); - - let _ = canvas.stroke_text( - text_location.0 as f32 * canvas_width, - text_location.1 as f32 * canvas_height, - format!("{:.2} N", lat), - &paint, + let (x, y) = self.map_to_screen( + mapper, + scale, + translate, + canvas_height, + canvas_width, + (left, *lat), ); + + let _ = canvas.stroke_text(x, y, format!("{:.2} N", lat), &paint); }); } @@ -92,14 +114,15 @@ impl BackgroundWidget { let mut paint = Paint::color(Color::white()); paint.set_font_size(25.0); paint.set_line_width(1.0); - let text_location = mapper.map((*lon, top + 0.1)).unwrap(); - - let _ = canvas.stroke_text( - text_location.0 as f32 * canvas_width, - text_location.1 as f32 * canvas_height, - format!("{:.2}", lon), - &paint, + let (x, y) = self.map_to_screen( + mapper, + scale, + translate, + canvas_height, + canvas_width, + (*lon + 0.5, bottom + 0.1), ); + let _ = canvas.stroke_text(x, y, format!("{:.2}", lon), &paint); }); } @@ -110,7 +133,13 @@ impl BackgroundWidget { LineString::new( result .points() - .map(|p| (p.x() as f32 * canvas_width, p.y() as f32 * canvas_height).into()) + .map(|p| { + ( + p.x() as f32 * canvas_width - translate.0, + (1.0 - p.y()) as f32 * canvas_height - translate.1, + ) + .into() + }) .collect(), ) }); @@ -124,25 +153,100 @@ impl BackgroundWidget { canvas: &mut Canvas, scale: f32, dpi: i32, + translate: (f32, f32), mapper: Ref<'_, Mapper>, bound: (Range, Range), ) { let canvas_width = canvas.width(); let canvas_height = canvas.height(); - let config = self.imp().config.borrow(); + self.mesh_lines( + canvas, + scale, + translate, + canvas_width, + canvas_height, + &mapper, + bound, + ); + self.shp( + mapper, + canvas, + translate, + scale, + canvas_width, + canvas_height, + ); + } - self.mesh_lines(canvas, canvas_width, canvas_height, mapper, bound); + fn shp( + &self, + mapper: Ref<'_, Mapper>, + canvas: &mut Canvas, + translate: (f32, f32), + scale: f32, + canvas_width: f32, + canvas_height: f32, + ) { + if let Some(s) = self.imp().shp.borrow().as_ref() { + for p in s.iter() { + for poly in p.iter() { + let mut points = poly.exterior().points(); + let first_point = points.next().unwrap(); + let mut path = Path::new(); + + let first = self.map_to_screen( + &mapper, + scale, + translate, + canvas_height, + canvas_width, + (first_point.x(), first_point.y()), + ); + + path.move_to(first.0, first.1); + + for p in points { + let (nx, ny) = self.map_to_screen( + &mapper, + scale, + translate, + canvas_height, + canvas_width, + (p.x(), p.y()), + ); + path.line_to(nx, ny); + } + + canvas.stroke_path(&path, &Paint::color(Color::white())); + } + } + } + } + + #[inline] + fn map_to_screen( + &self, + mapper: &Ref<'_, Mapper>, + scale: f32, + translate: (f32, f32), + canvas_height: f32, + canvas_width: f32, + point: (f64, f64), + ) -> (f32, f32) { + let (x, y) = mapper.map(point).unwrap(); + ( + x as f32 * canvas_width - translate.0, + (1.0 - y as f32) * canvas_height - translate.1, + ) } pub fn set_lat_lines(&self, lat_lines: Vec) { - println!("set lat lines {:?}", lat_lines); let imp = self.imp(); imp.config.borrow_mut().lat_lines = lat_lines; } pub fn set_lon_lines(&self, lon_lines: Vec) { - println!("set lon lines {:?}", lon_lines); let imp = self.imp(); imp.config.borrow_mut().lon_lines = lon_lines; } diff --git a/src/render/imp.rs b/src/render/imp.rs index 75fc6a4..702b3e3 100644 --- a/src/render/imp.rs +++ b/src/render/imp.rs @@ -3,8 +3,9 @@ use super::foreground::ForegroundWidget; use super::WindowCoord; use crate::coords::proj::Mercator; use crate::coords::Mapper; -use femtovg::{Color, Paint, Path, FontId}; +use femtovg::{Color, FontId, Paint, Path}; use gtk::glib; +use gtk::prelude::WidgetExtManual; use gtk::subclass::prelude::*; use gtk::traits::{GLAreaExt, WidgetExt}; use ndarray::Array2; @@ -17,7 +18,9 @@ pub struct RenderConfig { pub dim2: Option>, pub scale: f32, pub pointer_location: WindowCoord, - pub transform: WindowCoord, + pub pointer_lon_lat: (f64, f64), + pub translate: WindowCoord, + pub updated_translate: WindowCoord, } struct Fonts { @@ -31,9 +34,7 @@ pub struct Render { pub(super) foreground: RefCell, pub config: RefCell, pub mapper: RefCell, - canvas: RefCell>>, - canvas_width: RefCell, - canvas_height: RefCell, + pub(super) canvas: RefCell>>, } impl Default for Render { @@ -44,8 +45,6 @@ impl Default for Render { config: RefCell::new(RenderConfig::default()), mapper: RefCell::new(Mercator::new().into()), canvas: RefCell::new(None), - canvas_height: RefCell::new(0.0), - canvas_width: RefCell::new(0.0), } } } @@ -63,6 +62,11 @@ impl ObjectImpl for Render { self.parent_constructed(); let area = self.obj(); area.set_has_stencil_buffer(true); + + area.add_tick_callback(|area, _| { + area.queue_render(); + glib::Continue(true) + }); } } @@ -90,7 +94,6 @@ impl GLAreaImpl for Render { let w = canvas.width(); let h = canvas.height(); let configs = self.config.borrow(); - canvas.clear_rect( 0, 0, @@ -101,7 +104,6 @@ impl GLAreaImpl for Render { let mapper = self.mapper.borrow(); let (lon_range, lat_range) = mapper.range.clone(); - { let background_widget = self.background.borrow_mut(); background_widget.set_lat_lines(lat_range.key_points(9)); @@ -110,7 +112,7 @@ impl GLAreaImpl for Render { let background_widget = self.background.borrow_mut(); background_widget.set_lon_lines(lon_range.key_points(20)); } - + let translate = self.translate(); self.foreground .borrow() .draw(canvas, configs.scale, dpi, self.mapper.borrow()); @@ -119,12 +121,11 @@ impl GLAreaImpl for Render { canvas, configs.scale, dpi, + translate, self.mapper.borrow(), (lon_range, lat_range), ); - canvas.flush(); - true } } @@ -157,7 +158,17 @@ impl Render { }; renderer.set_screen_target(Some(fbo)); let mut canvas = Canvas::new(renderer).expect("Cannot create canvas"); - canvas.add_font_dir("/Users/tsuki/projects/radar-g/src/assets").unwrap(); + canvas + .add_font_dir("/Users/tsuki/projects/radar-g/src/assets") + .unwrap(); self.canvas.replace(Some(canvas)); } + + pub fn translate(&self) -> WindowCoord { + let cfg = self.config.borrow(); + let (rx, ry) = cfg.translate; + let (ux, uy) = cfg.updated_translate; + + return (rx + ux, ry + uy); + } } diff --git a/src/render/mod.rs b/src/render/mod.rs index 3dd9f2c..78d97a3 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -11,6 +11,7 @@ pub use self::background::{BackgroundConfig, BackgroundWidget}; pub use self::foreground::{ForegroundConfig, ForegroundWidget}; use self::imp::RenderConfig; use crate::data::DownSampleMeth; +use adw::prelude::WidgetExt; use femtovg::Color; pub use glib::subclass::prelude::*; use image::RgbImage; @@ -27,12 +28,20 @@ glib::wrapper! { impl Default for Render { fn default() -> Self { - Self::new(BackgroundWidget::default(), ForegroundWidget::default(),None) + Self::new( + BackgroundWidget::default(), + ForegroundWidget::default(), + None, + ) } } impl Render { - pub fn new(background: BackgroundWidget, foreground: ForegroundWidget,mapper: Option) -> Self { + pub fn new( + background: BackgroundWidget, + foreground: ForegroundWidget, + mapper: Option, + ) -> Self { let this: Self = glib::Object::new(); this.imp().background.replace(background); this.imp().foreground.replace(foreground); @@ -54,6 +63,41 @@ impl Render { f(&mut cfg); } + pub fn change_pointer(&mut self, x: f32, y: f32) { + if let Some(canvas) = self.imp().canvas.borrow().as_ref() { + let dpi = self.scale_factor(); + let cw = canvas.width(); + let ch = canvas.height(); + let (tx, ty) = self.imp().translate(); + println!("tx,ty: {:?}", (tx, ty)); + println!("x :{} , y: {}, x + tx :{}", x, y, x + tx); + let (lon, lat) = self + .imp() + .mapper + .borrow() + .fore_map(( + (x * dpi as f32+ tx) / cw, + (1.0 - (y * dpi as f32+ ty) / ch ), + )) + .unwrap(); + println!("lon,lat : {:?}", (lon, lat)); + let mut cfg = self.imp().config.borrow_mut(); + cfg.pointer_lon_lat = (lon, lat); + cfg.pointer_location = (x, y); + } + } + + pub fn calc_translate(&self) { + let ll = self.imp().config.borrow().pointer_lon_lat; + let scale = self.imp().config.borrow().scale; + let (rx, ry) = self.imp().mapper.borrow().map((ll.0, ll.1)).unwrap(); + + self.imp() + .mapper + .borrow_mut() + .set_translate((rx as f32 * (scale - 1.0), ry as f32 * (scale - 1.0))); + } + pub fn set_mapper(&self, mapper: Mapper) { self.imp().mapper.replace(mapper); } @@ -104,8 +148,8 @@ impl Render { let rainbow = pjp.run(&data).unwrap(); self.imp().foreground.borrow_mut().set_data(rainbow); - let pbow:Array2> = rrp.run(&data).unwrap(); + let pbow: Array2> = rrp.run(&data).unwrap(); self.imp().foreground.borrow_mut().set_colors(pbow); Ok(()) } -} \ No newline at end of file +}