This commit is contained in:
Tsuki 2024-03-11 18:35:09 +08:00
parent caf8b68926
commit b21529c2b9
13 changed files with 176 additions and 59 deletions

1
Cargo.lock generated
View File

@ -550,6 +550,7 @@ dependencies = [
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"tracker", "tracker",
"url",
] ]
[[package]] [[package]]

View File

@ -92,6 +92,7 @@ imgref = "1.10.1"
rgb = "0.8.37" rgb = "0.8.37"
slippy-map-tiles = "0.16.0" slippy-map-tiles = "0.16.0"
reqwest = "0.11.25" reqwest = "0.11.25"
url = "2.5.0"
[build-dependencies] [build-dependencies]

View File

@ -10,12 +10,9 @@ pub enum MonitorInputMsg {
NewElement((ElementKey, ElementID)), NewElement((ElementKey, ElementID)),
AddWidget(Box<dyn Widget>), AddWidget(Box<dyn Widget>),
RemoveWidget, RemoveWidget,
AddLayer(Layer),
RemoveLayer(String),
AddMetaItem(HashMap<String, String>), AddMetaItem(HashMap<String, String>),
ClearMetaItems, ClearMetaItems,
UpdateMetaItem(HashMap<String, String>), UpdateMetaItem(HashMap<String, String>),
UpdateLayer((String, Box<dyn Fn(&mut Layer) + 'static>)),
RefreshLayerList, RefreshLayerList,
SetRenderRange(f64, f64, f64, f64), SetRenderRange(f64, f64, f64, f64),
None, None,
@ -27,9 +24,6 @@ impl Debug for MonitorInputMsg {
MonitorInputMsg::SetRenderRange(_, _, _, _) => write!(f, "MonitorInputMsg::SetRenderRange"), MonitorInputMsg::SetRenderRange(_, _, _, _) => write!(f, "MonitorInputMsg::SetRenderRange"),
MonitorInputMsg::RefreshLayerList => write!(f, "MonitorInputMsg::RefreshLayerList"), MonitorInputMsg::RefreshLayerList => write!(f, "MonitorInputMsg::RefreshLayerList"),
MonitorInputMsg::NewElement(_) => write!(f, "MonitorInputMsg::NewElement"), MonitorInputMsg::NewElement(_) => write!(f, "MonitorInputMsg::NewElement"),
MonitorInputMsg::AddLayer(_) => write!(f, "MonitorInputMsg::AddLayer"),
MonitorInputMsg::RemoveLayer(_) => write!(f, "MonitorInputMsg::RemoveLayer"),
MonitorInputMsg::UpdateLayer(_) => write!(f, "MonitorInputMsg::UpdateLayer"),
MonitorInputMsg::None => write!(f, "MonitorInputMsg::None"), MonitorInputMsg::None => write!(f, "MonitorInputMsg::None"),
MonitorInputMsg::AddWidget(_) => write!(f, "MonitorInputMsg::AddWidget"), MonitorInputMsg::AddWidget(_) => write!(f, "MonitorInputMsg::AddWidget"),
MonitorInputMsg::RemoveWidget => write!(f, "MonitorInputMsg::RemoveWidget"), MonitorInputMsg::RemoveWidget => write!(f, "MonitorInputMsg::RemoveWidget"),

View File

@ -18,13 +18,20 @@ use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tokio::task;
use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg}; use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg};
use crate::map_tile::MapTile;
use adw::prelude::*; use adw::prelude::*;
use femtovg::ImageId;
use relm4::{component::Component, *}; use relm4::{component::Component, *};
use slippy_map_tiles::Tile;
use tracing::instrument::WithSubscriber;
use crate::coords::Range;
use crate::pipeline::element::Target;
#[derive(Debug)] #[derive(Debug)]
pub enum MonitorCommand { pub enum MonitorCommand {
// NewLayer(Layer), LoadTile(Tile, Vec<u8>, (f64,f64), (Range, Range)),
None, None,
} }
#[tracker::track] #[tracker::track]
@ -33,6 +40,10 @@ pub struct MonitorModel {
render_range: (f64, f64, f64, f64), render_range: (f64, f64, f64, f64),
sidebar_open: bool, sidebar_open: bool,
sidebar_width: i32, sidebar_width: i32,
#[do_not_track]
map_tile_getter: Arc<MapTile>,
#[do_not_track]
tiles: Rc<RefCell<HashMap<Tile, Target>>>,
new_layer: i8, new_layer: i8,
#[no_eq] #[no_eq]
widgets: Vec<WidgetFrame>, widgets: Vec<WidgetFrame>,
@ -86,6 +97,37 @@ impl Component for MonitorModel {
connect_render_status_notify[sender] => move |r| { connect_render_status_notify[sender] => move |r| {
sender.output(MonitorOutputMsg::LayerRenderFinished); sender.output(MonitorOutputMsg::LayerRenderFinished);
}, },
set_tiles: model.tiles.clone(),
connect_range_changing_notify[sender] => move |r| {
let ((x1 , x2), (y1,y2))= r.render_range();
let w = r.width() as f32;
let h = r.height() as f32;
let map_tile_getter = map_tile_getter.clone();
let new_tiles = map_tile_getter.new_tiles(3 ,(y1 as f32,y2 as f32),(x1 as f32,x2 as f32));
let tiles = (*tiles).borrow();
let new_tiles = new_tiles.filter(|x| !tiles.contains_key(x)).collect::<Vec<_>>();
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));
}
});
}
MonitorCommand::None
});
}
},
set_interior_layers: model.layers.clone(), set_interior_layers: model.layers.clone(),
}, },
add_overlay=&gtk::Button{ add_overlay=&gtk::Button{
@ -107,15 +149,16 @@ impl Component for MonitorModel {
} }
} }
fn update_with_view(&mut self, widgets: &mut Self::Widgets, message: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) { fn update_with_view(
&mut self,
widgets: &mut Self::Widgets,
message: Self::Input,
sender: ComponentSender<Self>,
root: &Self::Root,
) {
self.reset(); self.reset();
match message { match message {
MonitorInputMsg::AddLayer(layer) => {
self.layers.borrow_mut().push(layer);
let raw_id = self.get_new_layer();
self.set_new_layer(*raw_id + 1);
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
}
MonitorInputMsg::RefreshLayerList => { MonitorInputMsg::RefreshLayerList => {
self.sidebar.sender().send(SideBarInputMsg::RefreshList); self.sidebar.sender().send(SideBarInputMsg::RefreshList);
widgets.renderer.queue_render(); widgets.renderer.queue_render();
@ -123,8 +166,8 @@ impl Component for MonitorModel {
MonitorInputMsg::AddMetaItem(map) => { MonitorInputMsg::AddMetaItem(map) => {
self.sidebar.emit(SideBarInputMsg::AddMetaItems(map)) self.sidebar.emit(SideBarInputMsg::AddMetaItems(map))
} }
MonitorInputMsg::SetRenderRange(lon_start,lon_end,lat_start,lat_end)=>{ MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => {
self.set_render_range((lat_start,lat_end,lon_start,lon_end)); self.set_render_range((lat_start, lat_end, lon_start, lon_end));
} }
MonitorInputMsg::ClearMetaItems => self.sidebar.emit(SideBarInputMsg::ClearMetaItems), MonitorInputMsg::ClearMetaItems => self.sidebar.emit(SideBarInputMsg::ClearMetaItems),
MonitorInputMsg::UpdateMetaItem(map) => { MonitorInputMsg::UpdateMetaItem(map) => {
@ -157,7 +200,6 @@ impl Component for MonitorModel {
let sidebar: Controller<SideBarModel> = SideBarModel::builder() let sidebar: Controller<SideBarModel> = SideBarModel::builder()
.launch(init.clone()) .launch(init.clone())
.forward(sender.input_sender(), move |msg| match msg { .forward(sender.input_sender(), move |msg| match msg {
SideBarOutputMsg::NewLayer(layer) => MonitorInputMsg::AddLayer(layer),
SideBarOutputMsg::SwitchToTimeSeries(layer) => { SideBarOutputMsg::SwitchToTimeSeries(layer) => {
sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer)); sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer));
MonitorInputMsg::None MonitorInputMsg::None
@ -173,27 +215,39 @@ impl Component for MonitorModel {
render_range: (4.0, 53.3, 73.3, 135.0), render_range: (4.0, 53.3, 73.3, 135.0),
new_layer: 0, new_layer: 0,
widgets: vec![], widgets: vec![],
tiles: Rc::new(RefCell::new(HashMap::new())),
render_cfg, render_cfg,
sidebar_open: true, sidebar_open: true,
sidebar_width: 400, sidebar_width: 400,
layers: init, layers: init,
map_tile_getter: Arc::new(MapTile::default()),
sidebar, sidebar,
tracker: 0, tracker: 0,
}; };
let map_tile_getter = model.map_tile_getter.clone();
let tiles = model.tiles.clone();
let widgets = view_output! {}; let widgets = view_output! {};
ComponentParts { model, widgets } ComponentParts { model, widgets }
} }
fn update_cmd( fn update_cmd_with_view(
&mut self, &mut self,
msg: Self::CommandOutput, widgets: &mut Self::Widgets,
_sender: ComponentSender<Self>, message: Self::CommandOutput,
_root: &Self::Root, sender: ComponentSender<Self>,
root: &Self::Root,
) { ) {
self.reset(); self.reset();
match msg { match message {
MonitorCommand::LoadTile(key, ref tile, origin, bounds) => {
let target = widgets.renderer.load_img_mem(&tile,origin,(256f32,256f32), bounds).unwrap();
self.tiles.borrow_mut().insert(key, target);
widgets.render.queue_draw();
}
_ => {} _ => {}
} }
self.update_view(widgets, sender);
} }
} }

View File

@ -28,6 +28,7 @@ mod widgets;
mod data_utils; mod data_utils;
mod predefined; mod predefined;
mod map_tile_utils; mod map_tile_utils;
mod map_tile;
const APP_ID: &str = "org.tsuki.radar_g"; const APP_ID: &str = "org.tsuki.radar_g";
static RUNTIME: SafeLazy<Runtime> = static RUNTIME: SafeLazy<Runtime> =

54
src/map_tile.rs Normal file
View File

@ -0,0 +1,54 @@
use crate::map_tile_utils::lat_lon_to_zoom;
use femtovg::ImageSource;
use reqwest::{Client, Error, Url};
use slippy_map_tiles::{BBox, merc_location_to_tile_coords, Tile};
use crate::coords::Range;
pub struct MapTile {
server: String,
api_key: String,
style: String,
client: Client,
}
impl Default for MapTile{
fn default() -> Self {
Self{
server: "https://tiles.stadiamaps.com/tiles/".to_string(),
api_key: "06f1aeed-5d91-48e3-9ce5-1e99063f7f73".to_string(),
style: "stamen_toner/".to_string(),
client: Client::new(),
}
}
}
impl MapTile {
pub fn new_tiles(&self, zoom: u8, lat_range: (f32, f32), lon_range: (f32, f32)) -> impl Iterator<Item = Tile> + Sized {
let bbox= BBox::new(lat_range.1 , lon_range.0, lat_range.0, lon_range.1).unwrap();
let tiles = bbox.tiles_for_zoom(zoom);
tiles
}
pub async fn get_tile(
&self,
tile: &Tile
) -> Result<Vec<u8>, Error>
{
let base_url = Url::parse(&self.server).unwrap();
// let zoom = lat_lon_to_zoom(lat_range, lat_range, w, h);
let mut request_url = base_url
.join(&self.style)
.unwrap()
.join(&format!("{}/{}/{}.png", tile.zoom(), tile.x(), tile.y()))
.unwrap();
let result = self
.client
.get(request_url)
.query(&[("api_key", &self.api_key)])
.send()
.await?;
let bytes = result.bytes().await?;
Ok(bytes.to_vec())
}
}

View File

@ -1 +1 @@
mod map_tile; pub mod map_tile;

View File

@ -3,7 +3,6 @@ use gtk::glib;
use gtk::subclass::prelude::*; use gtk::subclass::prelude::*;
use std::cell::RefCell; use std::cell::RefCell;
#[derive(Default)] #[derive(Default)]
pub struct ExteriorWidget { pub struct ExteriorWidget {
} }

View File

@ -3,14 +3,17 @@ use super::interior::InteriorWidget;
use super::{Layer, WindowCoord}; use super::{Layer, WindowCoord};
use crate::coords::proj::Mercator; use crate::coords::proj::Mercator;
use crate::coords::Mapper; use crate::coords::Mapper;
use crate::pipeline::element::Target;
use femtovg::{Canvas, Color, FontId, Renderer}; use femtovg::{Canvas, Color, FontId, Renderer};
use gtk::glib::{self, prelude::*, Properties}; use gtk::glib::{self, prelude::*, Properties};
use gtk::subclass::prelude::*; use gtk::subclass::prelude::*;
use gtk::traits::{GLAreaExt, WidgetExt}; use gtk::traits::{GLAreaExt, WidgetExt};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use slippy_map_tiles::Tile;
#[derive(Debug, Default, Clone, Copy, PartialEq)] #[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct RenderConfig { pub struct RenderConfig {
@ -48,10 +51,13 @@ pub struct RenderStatus {
pub struct Render { pub struct Render {
#[property(get, set)] #[property(get, set)]
render_status: Cell<i64>, render_status: Cell<i64>,
#[property(get, set)]
range_changing: Cell<f64>,
pub(super) exterior: RefCell<ExteriorWidget>, pub(super) exterior: RefCell<ExteriorWidget>,
pub(super) interior: RefCell<InteriorWidget>, pub(super) interior: RefCell<InteriorWidget>,
pub(super) canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>, pub(super) canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
pub(super) glow_context: RefCell<Option<glow::Context>>, pub(super) glow_context: RefCell<Option<glow::Context>>,
pub(super) tiles: RefCell<Rc<RefCell<HashMap<Tile, Target>>>>,
pub config: RefCell<RenderConfig>, pub config: RefCell<RenderConfig>,
pub status: RefCell<RenderStatus>, pub status: RefCell<RenderStatus>,
pub mapper: RefCell<Mapper>, pub mapper: RefCell<Mapper>,
@ -61,6 +67,7 @@ pub struct Render {
impl Default for Render { impl Default for Render {
fn default() -> Self { fn default() -> Self {
Self { Self {
range_changing: Cell::new(0.0),
render_status: Cell::new(0), render_status: Cell::new(0),
exterior: RefCell::new(ExteriorWidget::default()), exterior: RefCell::new(ExteriorWidget::default()),
interior: RefCell::new(InteriorWidget::default()), interior: RefCell::new(InteriorWidget::default()),
@ -68,6 +75,7 @@ impl Default for Render {
interior_layers: RefCell::new(Rc::new(RefCell::new(Vec::new()))), interior_layers: RefCell::new(Rc::new(RefCell::new(Vec::new()))),
config: RefCell::new(RenderConfig::default()), config: RefCell::new(RenderConfig::default()),
status: RefCell::new(RenderStatus::default()), status: RefCell::new(RenderStatus::default()),
tiles: RefCell::new(Rc::new(RefCell::new(HashMap::new()))),
mapper: RefCell::new(Mercator::default().into()), mapper: RefCell::new(Mercator::default().into()),
canvas: RefCell::new(None), canvas: RefCell::new(None),
} }
@ -152,6 +160,12 @@ impl GLAreaImpl for Render {
(w, h) (w, h)
}; };
let tiles = self.tiles.borrow().borrow();
for tile in tiles.values() {
}
let render_range = self.status.borrow().view_range.clone(); let render_range = self.status.borrow().view_range.clone();
if let Some(((lat1, lat2), (lon1, lon2))) = render_range { if let Some(((lat1, lat2), (lon1, lon2))) = render_range {
let mut status = self.status.borrow_mut(); let mut status = self.status.borrow_mut();

View File

@ -2,7 +2,7 @@ use super::super::WindowCoord;
use gtk::glib; use gtk::glib;
use gtk::subclass::prelude::*; use gtk::subclass::prelude::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use super::layers::Layer; use super::layers::Layer;
#[derive(Default)] #[derive(Default)]

View File

@ -1,30 +0,0 @@
pub(super) struct MapTile {
server: Option<String>,
lat_range: (f64, f64),
lon_range: (f64, f64),
zoom: u8,
}
impl MapTile{
pub fn new(server: Option<String>, lat_range: (f64, f64), lon_range: (f64, f64), zoom: u8) -> MapTile {
MapTile {
server,
lat_range,
lon_range,
zoom,
}
}
pub fn get_server(&self) -> Option<String> {
self.server.clone()
}
pub fn get_lat_range(&self) -> (f64, f64) {
self.lat_range
}
pub fn get_lon_range(&self) -> (f64, f64) {
self.lon_range
}
}

View File

@ -1,11 +1,18 @@
mod imp; mod imp;
mod layers; mod layers;
mod map_tile;
use gtk::subclass::prelude::ObjectSubclassIsExt;
use super::super::Render; use super::super::Render;
use femtovg::{renderer::OpenGl, Canvas}; use crate::RUNTIME;
pub use layers::{Layer, LayerImpl, LayerImplSync, AssoElement}; use core_extensions::SelfOps;
use femtovg::{renderer::OpenGl, Canvas, ImageFlags};
use femtovg::{Paint, Path};
pub use layers::{AssoElement, Layer, LayerImpl, LayerImplSync};
use relm4::channel;
use std::cell::Ref; use std::cell::Ref;
use tokio::task;
use tokio::task::LocalSet;
use super::imp::{RenderConfig, RenderStatus}; use super::imp::{RenderConfig, RenderStatus};

View File

@ -8,7 +8,7 @@ pub mod widget;
// pub use self::cms::CMS; // pub use self::cms::CMS;
use crate::coords::cms::CMS; use crate::coords::cms::CMS;
pub use self::imp::{RenderConfig, RenderMotion, RenderStatus}; pub use self::imp::{RenderConfig, RenderMotion, RenderStatus};
use crate::coords::Mapper; use crate::coords::{Mapper, Range};
use adw::prelude::{GLAreaExt, GestureDragExt}; use adw::prelude::{GLAreaExt, GestureDragExt};
use geo_types::LineString; use geo_types::LineString;
use glib::clone; use glib::clone;
@ -17,8 +17,13 @@ use gtk::traits::WidgetExt;
use gtk::{EventControllerScrollFlags, Inhibit}; use gtk::{EventControllerScrollFlags, Inhibit};
pub use interior::*; pub use interior::*;
use std::cell::{Ref, RefCell, RefMut}; use std::cell::{Ref, RefCell, RefMut};
use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use femtovg::ImageFlags;
use slippy_map_tiles::Tile;
use crate::errors::PipelineError;
use crate::pipeline::element::{Target, TargetType};
pub type WindowCoord = (f32, f32); pub type WindowCoord = (f32, f32);
@ -64,6 +69,7 @@ impl Render {
status.scale = y as f32; status.scale = y as f32;
status.motion = RenderMotion::Scale; status.motion = RenderMotion::Scale;
}); });
r.set_range_changing(0.0);
r.queue_render(); r.queue_render();
Inhibit(false) Inhibit(false)
} }
@ -78,6 +84,7 @@ impl Render {
s.translate = Some((-ox as f32 * dpi , oy as f32 * dpi)); s.translate = Some((-ox as f32 * dpi , oy as f32 * dpi));
s.motion = RenderMotion::Translate; s.motion = RenderMotion::Translate;
}); });
r.set_range_changing(0.0);
r.queue_render(); r.queue_render();
})); }));
@ -205,6 +212,21 @@ impl Render {
pub fn set_view(&self, range: (f64, f64, f64, f64)) { pub fn set_view(&self, range: (f64, f64, f64, f64)) {
self.imp().set_view(range); self.imp().set_view(range);
self.set_range_changing(range.0 + range.1);
// self.set_range(range);
self.queue_render(); self.queue_render();
} }
pub fn set_tiles(&self, tiles: Rc<RefCell<HashMap<Tile, Target>>>) {
self.imp().tiles.replace(tiles);
self.queue_render();
}
pub fn load_img_mem(&self, img: &[u8], origin:(f64,f64),size :(f32,f32), bounds: (Range, Range)) -> Result<Target, PipelineError> {
let mut canvas = self.get_canvas();
let cvs = canvas.as_mut().unwrap();
let img_id = cvs.load_image_mem(img, ImageFlags::empty()).unwrap();
let (width, height) = size;
Ok(Target::new(TargetType::ImageId(img_id), width, height, bounds, None, None))
}
} }