use tracing::*; use super::messages::{MonitorInputMsg, MonitorOutputMsg}; use crate::coords::cms::CMS; use crate::pipeline::offscreen_renderer::OffscreenRenderer; use crate::widgets::predefined::color_mapper::BoundaryNorm; use crate::widgets::predefined::widgets::ColorBar; use crate::widgets::render::RenderConfig; use crate::widgets::widget::{Widget, WidgetType}; use crate::widgets::WidgetFrame; use crate::{ coords::{proj::Mercator, Mapper}, widgets::dynamic_col::DynamicCol, widgets::render::{Layer, Render}, }; use geo::k_nearest_concave_hull; use glib::{clone, PropertyGet}; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::sync::{Arc, Mutex}; use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg}; use crate::coords::Range; use crate::map_tile::MapTile; use crate::map_tile_utils::lat_lon_to_zoom; use crate::pipeline::element::Target; use adw::prelude::*; use femtovg::ImageId; use quick_cache::unsync::Cache; use relm4::{component::Component, *}; use slippy_map_tiles::Tile; use tokio::task; use tracing::instrument::WithSubscriber; #[derive(Debug)] pub enum MonitorCommand { LoadTile(Tile, Vec, (f64, f64), (Range, Range)), None, } #[tracker::track] pub struct MonitorModel { render_cfg: RenderConfig, render_range: (f64, f64, f64, f64), sidebar_open: bool, sidebar_width: i32, zoom: u8, #[do_not_track] tile_cache: Cache, #[do_not_track] map_tile_getter: Arc>, #[do_not_track] tiles: Rc>>>, new_layer: i8, #[no_eq] widgets: Vec, #[no_eq] layers: Rc>>, #[no_eq] sidebar: Controller, } pub struct MonitorWidgets { paned: gtk::Paned, } #[relm4::component(pub)] impl Component for MonitorModel { type CommandOutput = MonitorCommand; type Input = MonitorInputMsg; type Init = Rc>>; type Output = MonitorOutputMsg; view! { #[root] adw::BreakpointBin { set_hexpand: true, set_vexpand: true, set_height_request: 500, set_width_request: 700, #[wrap(Some)] #[name="test"] set_child = &DynamicCol{ set_end_width: 300, set_hexpand: true, set_vexpand: true, #[wrap(Some)] #[name="paned"] set_child_paned=>k::Paned{ #[wrap(Some)] #[name="render"] set_start_child=>k::Frame{ add_css_class: "rb", set_margin_all: 5, #[name="widget_layer"] gtk::Overlay{ #[name = "renderer"] #[wrap(Some)] set_child = &Render{ #[track = "model.changed(MonitorModel::render_cfg())"] set_cfg: model.render_cfg, #[track = "model.changed(MonitorModel::render_range())"] set_view: model.render_range, connect_render_status_notify[sender] => move |r| { sender.output(MonitorOutputMsg::LayerRenderFinished); }, set_tiles: model.tiles.clone(), connect_range_changing_notify[sender] => move |r| { sender.input(MonitorInputMsg::RefreshTiles); }, connect_scale_notify[sender] => move |r| { let scale_factor = r.scale(); let zoom = scale_factor.log2().round() as u8; let new_zoom = model.zoom + zoom; if model.zoom != new_zoom { sender.input(MonitorInputMsg::ChangeZoom(new_zoom)); } }, set_interior_layers: model.layers.clone(), }, add_overlay=>k::Button{ set_label:"Add", set_margin_all:10, set_valign: gtk::Align::Start, set_halign: gtk::Align::End, }, #[track = "model.changed(MonitorModel::new_layer())"] #[iterate] add_overlay: &model.widgets }, }, #[wrap(Some)] set_end_child=model.sidebar.widget(), } } } } fn update_with_view( &mut self, widgets: &mut Self::Widgets, message: Self::Input, sender: ComponentSender, root: &Self::Root, ) { self.reset(); match message { MonitorInputMsg::RefreshLayerList => { self.sidebar.sender().send(SideBarInputMsg::RefreshList); widgets.renderer.queue_render(); } MonitorInputMsg::AddMetaItem(map) => { self.sidebar.emit(SideBarInputMsg::AddMetaItems(map)) } MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => { self.set_render_range((lat_start, lat_end, lon_start, lon_end)); } MonitorInputMsg::ClearMetaItems => self.sidebar.emit(SideBarInputMsg::ClearMetaItems), MonitorInputMsg::UpdateMetaItem(map) => { self.sidebar.emit(SideBarInputMsg::ClearMetaItems); self.sidebar.emit(SideBarInputMsg::AddMetaItems(map)) } MonitorInputMsg::RefreshTiles => { let ((x1 , x2), (y1,y2)) = widgets.renderer.render_range(); // Self::load_cache_tile(&mut self.tile_cache,&sender, &self.map_tile_getter, self.zoom, ((y1 as f32, y2 as f32),(x1 as f32,x2 as f32))); } MonitorInputMsg::AddWidget(widget) => match widget.widget_type() { WidgetType::Cairo => { let frame = WidgetFrame::new(); frame.set_widget(widget); self.widgets.push(frame); } WidgetType::OpenGl => {} _ => {} }, MonitorInputMsg::ChangeZoom(zoom) => { if self.zoom != zoom { self.zoom = zoom; sender.input(MonitorInputMsg::RefreshTiles); } } MonitorInputMsg::RemoveWidget => {} MonitorInputMsg::None => {} _ => {} } self.update_view(widgets, sender); } fn init( init: Self::Init, root: &Self::Root, sender: ComponentSender, ) -> ComponentParts { let sidebar_sender = sender.clone(); let sidebar: Controller = SideBarModel::builder() .launch(init.clone()) .forward(sender.input_sender(), move |msg| match msg { SideBarOutputMsg::SwitchToTimeSeries(layer) => { sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer)); MonitorInputMsg::None } _ => MonitorInputMsg::None, }); let render_cfg = RenderConfig { padding: [20.0, 40.0, 20.0, 40.0], }; let mut model = MonitorModel { render_range: (4.0, 53.3, 73.3, 135.0), new_layer: 0, widgets: vec![], tiles: Rc::new(RefCell::new(HashMap::new())), render_cfg, tile_cache: Cache::new(32), sidebar_open: true, sidebar_width: 400, layers: init, zoom: 4, map_tile_getter: Arc::new(tokio::sync::Mutex::new( MapTile::default())), sidebar, tracker: 0, }; let map_tile_getter = model.map_tile_getter.clone(); let tiles = model.tiles.clone(); let widgets = view_output! {}; ComponentParts { model, widgets } } fn update_cmd_with_view( &mut self, widgets: &mut Self::Widgets, message: Self::CommandOutput, sender: ComponentSender, root: &Self::Root, ) { self.reset(); match message { MonitorCommand::LoadTile(key, ref tile, origin, bounds) => { let target = widgets .renderer .load_img_mem(&tile, origin, (256f32, 256f32), bounds) .unwrap(); let mut binding = self.tiles.borrow_mut(); if !binding.contains_key(&key) { return ; } binding.entry(key).and_modify(|x| *x = Some(target)); widgets.renderer.queue_draw(); } _ => {} } self.update_view(widgets, sender); } } impl MonitorModel { fn load_tile(tiles: &Rc>>> ,sender: &ComponentSender, map_tile_getter: &Arc, zoom:u8, range:((f32,f32), (f32,f32))) { let map_tile_getter = map_tile_getter.clone(); let new_tiles = map_tile_getter.new_tiles(zoom ,range.0, range.1); let mut tiles = (*tiles).borrow_mut(); let new_tiles = new_tiles.filter(|x| !tiles.contains_key(x)).collect::>(); tiles.extend(new_tiles.iter().map(|x| (*x, None))); if new_tiles.len() > 0 { let new_sender = sender.clone(); sender.oneshot_command(async move { for tile in new_tiles { let map_tile_getter = map_tile_getter.clone(); let sender = new_sender.clone(); task::spawn(async move { let result = map_tile_getter.get_tile(&tile).await; if let Ok(result) = result { let origin = tile.nw_corner(); let rb = tile.se_corner(); let bounds = ((origin.lon() as f64..rb.lon() as f64).into(), (origin.lat() as f64..rb.lat() as f64).into()); sender.command_sender().emit(MonitorCommand::LoadTile(tile, result,(origin.lon() as f64, origin.lat() as f64), bounds)); } }).await; } MonitorCommand::None }); } } fn load_tiles(&self, sender: &ComponentSender,range:((f32,f32), (f32,f32))) { // sender.oneshot_command(async move { // MapTile::load_tiles(self.map_tile_getter.clone(), self.zoom, range).await; // MonitorCommand::None // }); } }