Compare commits

...

9 Commits

Author SHA1 Message Date
cd4c46f9c6 three d 2024-03-24 19:50:18 +08:00
tsuki
09b7e83669 refactor 2024-03-22 00:36:12 +08:00
tsuki
59a886f222 sync 2024-03-20 23:54:05 +08:00
b0458c34a5 sync 2024-03-20 18:38:24 +08:00
18b7d2d3d8 sync 2024-03-19 18:37:13 +08:00
1e8a90aebe Merge pull request 'windows_test' (#1) from windows_test into master
Reviewed-on: #1
2024-03-18 00:15:40 +08:00
Tsuki
5eee473c6b sync 2024-03-18 00:13:23 +08:00
tsuki
2e7e7f0a9f sync 2024-03-17 21:26:33 +08:00
Tsuki
ae324eef15 test 2024-03-17 21:14:18 +08:00
60 changed files with 862 additions and 1192 deletions

113
Cargo.lock generated
View File

@ -513,7 +513,6 @@ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-traits", "num-traits",
"serde",
"wasm-bindgen", "wasm-bindgen",
"windows-targets 0.52.0", "windows-targets 0.52.0",
] ]
@ -531,7 +530,6 @@ dependencies = [
"crossbeam", "crossbeam",
"dirs", "dirs",
"epoxy", "epoxy",
"etws_loader",
"euclid", "euclid",
"femtovg", "femtovg",
"flate2", "flate2",
@ -552,7 +550,7 @@ dependencies = [
"indexmap", "indexmap",
"lazy_static", "lazy_static",
"libadwaita", "libadwaita",
"libloading 0.8.0", "libloading 0.8.3",
"ndarray", "ndarray",
"npyz", "npyz",
"num-traits", "num-traits",
@ -988,7 +986,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [ dependencies = [
"libloading 0.8.0", "libloading 0.7.4",
] ]
[[package]] [[package]]
@ -1075,24 +1073,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "etws_loader"
version = "0.1.0"
dependencies = [
"abi_stable",
"anyhow",
"byteorder",
"chrono",
"flate2",
"nom",
"nom-derive",
"num-traits",
"radarg_plugin_interface",
"serde",
"serde_json",
"thiserror",
]
[[package]] [[package]]
name = "euclid" name = "euclid"
version = "0.22.9" version = "0.22.9"
@ -1135,19 +1115,20 @@ dependencies = [
[[package]] [[package]]
name = "femtovg" name = "femtovg"
version = "0.7.1" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3a2d0ff0df09856a5c1c89cc83863a1f0f994c55452186621bb57a01f270b3" checksum = "ad3cf7e8f8e3c684b418c2640c931afc8bbc7ebe547bed6bf64170f1f51d57c9"
dependencies = [ dependencies = [
"bitflags 2.4.2", "bitflags 2.4.2",
"fnv", "fnv",
"generational-arena",
"glow", "glow",
"image", "image",
"imgref", "imgref",
"log",
"lru", "lru",
"rgb", "rgb",
"rustybuzz", "rustybuzz",
"slotmap",
"unicode-bidi", "unicode-bidi",
"unicode-segmentation", "unicode-segmentation",
"wasm-bindgen", "wasm-bindgen",
@ -1763,9 +1744,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]] [[package]]
name = "glow" name = "glow"
version = "0.12.2" version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "807edf58b70c0b5b2181dd39fe1839dbdb3ba02645630dc5f753e23da307f762" checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"slotmap", "slotmap",
@ -2348,12 +2329,12 @@ dependencies = [
[[package]] [[package]]
name = "libloading" name = "libloading"
version = "0.8.0" version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-sys 0.48.0", "windows-targets 0.52.0",
] ]
[[package]] [[package]]
@ -2408,9 +2389,9 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.10.1" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
[[package]] [[package]]
name = "mach" name = "mach"
@ -2626,28 +2607,6 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "nom-derive"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ff943d68b88d0b87a6e0d58615e8fa07f9fd5a1319fa0a72efc1f62275c79a7"
dependencies = [
"nom",
"nom-derive-impl",
"rustversion",
]
[[package]]
name = "nom-derive-impl"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0b9a93a84b0d3ec3e70e02d332dc33ac6dfac9cde63e17fcb77172dededa62"
dependencies = [
"proc-macro2 1.0.76",
"quote 1.0.35",
"syn 1.0.109",
]
[[package]] [[package]]
name = "npyz" name = "npyz"
version = "0.8.1" version = "0.8.1"
@ -3698,25 +3657,19 @@ dependencies = [
"base64", "base64",
] ]
[[package]]
name = "rustversion"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]] [[package]]
name = "rustybuzz" name = "rustybuzz"
version = "0.7.0" version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162bdf42e261bee271b3957691018634488084ef577dddeb6420a9684cab2a6a" checksum = "88117946aa1bfb53c2ae0643ceac6506337f44887f8c9fbfb43587b1cc52ba49"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 2.4.2",
"bytemuck", "bytemuck",
"smallvec", "smallvec",
"ttf-parser 0.18.1", "ttf-parser 0.20.0",
"unicode-bidi-mirroring", "unicode-bidi-mirroring",
"unicode-ccc", "unicode-ccc",
"unicode-general-category", "unicode-properties",
"unicode-script", "unicode-script",
] ]
@ -3963,9 +3916,9 @@ dependencies = [
[[package]] [[package]]
name = "slotmap" name = "slotmap"
version = "1.0.6" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
dependencies = [ dependencies = [
"version_check", "version_check",
] ]
@ -4570,12 +4523,6 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff"
[[package]]
name = "ttf-parser"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633"
[[package]] [[package]]
name = "ttf-parser" name = "ttf-parser"
version = "0.20.0" version = "0.20.0"
@ -4608,21 +4555,15 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]] [[package]]
name = "unicode-bidi-mirroring" name = "unicode-bidi-mirroring"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
[[package]] [[package]]
name = "unicode-ccc" name = "unicode-ccc"
version = "0.1.2" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
[[package]]
name = "unicode-general-category"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@ -4639,6 +4580,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-properties"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291"
[[package]] [[package]]
name = "unicode-script" name = "unicode-script"
version = "0.5.5" version = "0.5.5"

View File

@ -20,11 +20,11 @@ quadtree_rs = "0.1.2"
proj-sys = "0.23.1" proj-sys = "0.23.1"
glib-macros = "0.19.2" glib-macros = "0.19.2"
svg = "0.13.1" svg = "0.13.1"
libloading = "0.8.0" libloading = "0.8.3"
glue = "0.8.7" glue = "0.8.7"
epoxy = "0.1.0" epoxy = "0.1.0"
femtovg = "0.7.1" femtovg = "0.9.0"
glow = "0.12.2" glow = "0.13.1"
proj = "0.27.2" proj = "0.27.2"
image = "0.24.7" image = "0.24.7"
anyhow = "1.0.72" anyhow = "1.0.72"
@ -82,10 +82,10 @@ path = "geo-macros"
[dependencies.radarg_plugin_interface] [dependencies.radarg_plugin_interface]
path = "radarg_plugin_interface" path = "radarg_plugin_interface"
[dependencies.etws_loader] #[dependencies.etws_loader]
path = "etws_loader" #path = "etws_loader"
[dependencies.adw] [dependencies.adw]
package = "libadwaita" package = "libadwaita"
version = "*" version = "0.6.0"
features = ["v1_4"] features = ["v1_4"]

View File

@ -29,6 +29,10 @@ paned>separator {
font-size: 14px; font-size: 14px;
} }
.tooltip {
font-size: 8px;
}
.lv { .lv {
background: transparent; background: transparent;
} }

View File

@ -89,15 +89,24 @@ impl Plugin for ETWSLoader {
CoordType::Other CoordType::Other
}; };
let lat = b.info.dimension_values.get(0).unwrap();
let lon = b.info.dimension_values.get(1).unwrap();
lat_range = [lat[0], lat[lat.len() - 1]];
lon_range = [lon[0], lon[lon.len() - 1]];
let shape = match b.info.dimension_size.len() { let shape = match b.info.dimension_size.len() {
1 => radarg_plugin_interface::DataShape::Vector, 1 => radarg_plugin_interface::DataShape::Vector,
2 => radarg_plugin_interface::DataShape::Matrix, 2 => {
_ => radarg_plugin_interface::DataShape::Scalar, let lat = b.info.dimension_values.get(0).unwrap();
let lon = b.info.dimension_values.get(1).unwrap();
lat_range = [lat[0], lat[lat.len() - 1]];
lon_range = [lon[0], lon[lon.len() - 1]];
radarg_plugin_interface::DataShape::Matrix
},
_ => {
let lat = b.info.dimension_values.get(1).unwrap();
let lon = b.info.dimension_values.get(2).unwrap();
lat_range = [lat[0], lat[lat.len() - 1]];
lon_range = [lon[0], lon[lon.len() - 1]];
radarg_plugin_interface::DataShape::Cube
},
}; };
let data_type = match b.info.value_name.as_str() { let data_type = match b.info.value_name.as_str() {
@ -115,6 +124,7 @@ impl Plugin for ETWSLoader {
"HCA" => PluginResultType::HCA, "HCA" => PluginResultType::HCA,
"QPE" => PluginResultType::QPE, "QPE" => PluginResultType::QPE,
"QPF" => PluginResultType::QPF, "QPF" => PluginResultType::QPF,
"FR" => PluginResultType::DBZ,
_ => PluginResultType::Unknown, _ => PluginResultType::Unknown,
}; };
@ -187,7 +197,7 @@ mod tests {
fn test() { fn test() {
let result = let result =
Record::parse_from_path("/Volumes/data2/RadarArray/HangZhou/radarData/OutputProducts/RadarProducts/BasicProductsX/20230624/20230624000800/ZJHZAA_20230624000800_VIL.dat.gz") Record::parse_from_path("/Volumes/data2/RadarArray/HangZhou/radarData/OutputProducts/RadarProducts/FuseDataX/20220727/ZJHZAA_20220727200000_FR.dat.gz")
.unwrap(); .unwrap();
} }
} }

View File

@ -15,4 +15,7 @@ icons = [
"home-filled", "home-filled",
"settings-filled", "settings-filled",
"save-filled", "save-filled",
"timer-filled",
"eye-filled",
"eye-off-filled",
] ]

View File

@ -1,5 +1,5 @@
use abi_stable::{ use abi_stable::{
std_types::{ RBoxError, RVec}, std_types::{RBoxError, RVec},
StableAbi, StableAbi,
}; };
@ -18,7 +18,6 @@ pub enum Error {
Many(RVec<Error>), Many(RVec<Error>),
} }
impl Display for Error { impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
@ -33,15 +32,15 @@ impl Display for Error {
)?; )?;
Ok(()) Ok(())
}, }
Error::Custom(e) => Display::fmt(e, f), Error::Custom(e) => Display::fmt(e, f),
Error::Many(list) => { Error::Many(list) => {
for e in list { for e in list {
writeln!(f, "{}", e)?; writeln!(f, "{}", e)?;
} }
Ok(()) Ok(())
}, }
_ => { Ok(())} _ => Ok(()),
} }
} }
} }

View File

@ -50,9 +50,9 @@ pub enum VecResult {
#[derive(StableAbi, Clone, Copy, Debug)] #[derive(StableAbi, Clone, Copy, Debug)]
#[sabi(impl_InterfaceType(Sync, Send, Debug))] #[sabi(impl_InterfaceType(Sync, Send, Debug))]
pub enum DataShape { pub enum DataShape {
Scalar,
Vector, Vector,
Matrix, Matrix,
Cube,
} }
#[repr(C)] #[repr(C)]

38
src/actions.rs Normal file
View File

@ -0,0 +1,38 @@
use std::cell::RefCell;
use std::rc::Rc;
use gtk::gio::SimpleAction;
use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt};
use relm4::actions::{AccelsPlus, ActionablePlus, RelmAction, RelmActionGroup};
use relm4::{ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
use crate::components::app::{AppModel, AppMsg, LayerMsg};
use crate::widgets::{AssoElement, Layer};
relm4::new_action_group!(pub LayerActionGroup, "layer");
relm4::new_stateless_action!(pub AddLayerAction, LayerActionGroup, "add");
relm4::new_stateless_action!(pub RemoveLayerAction, LayerActionGroup, "remove");
relm4::new_stateless_action!(pub MoveLayerAction, LayerActionGroup, "move");
pub fn register_layer_actions<W: AsRef<gtk::Widget>>(widget: W, sender: ComponentSender<AppModel>) {
let add_action: RelmAction<AddLayerAction> = {
let sender = sender.clone();
RelmAction::new_stateless(move |_| {
sender.input(AppMsg::LayerManager(LayerMsg::Add(Layer::new(
true,
"Test".to_string(),
AssoElement::Test,
))))
})
};
let remove_action: RelmAction<RemoveLayerAction> = {
let sender = sender.clone();
RelmAction::new_stateless(move |_| sender.input(AppMsg::LayerManager(LayerMsg::Remove(0))))
};
let mut group: RelmActionGroup<LayerActionGroup> = RelmActionGroup::new();
group.add_action(add_action);
group.add_action(remove_action);
group.register_for_widget(widget)
}

View File

@ -1,3 +1,4 @@
use super::sidebar::SideBarOutputMsg;
use super::{ use super::{
control_panel::{ControlPanelInputMsg, ControlPanelModel}, control_panel::{ControlPanelInputMsg, ControlPanelModel},
messages::{MonitorInputMsg, MonitorOutputMsg}, messages::{MonitorInputMsg, MonitorOutputMsg},
@ -5,12 +6,14 @@ use super::{
setting::SettingModel, setting::SettingModel,
ControlPanelOutputMsg, TimelineMsg, ControlPanelOutputMsg, TimelineMsg,
}; };
use crate::data_utils::plugin_result_impl; use crate::components::sidebar::{SideBarInputMsg, SideBarModel};
use crate::pipeline::element::{
Element, InstantElement, InstantElementDrawerType, TimeSeriesElement,
};
use crate::pipeline::{GridElementImpl, OffscreenRenderer}; use crate::pipeline::{GridElementImpl, OffscreenRenderer};
use crate::widgets::AssoElement; use crate::predefined::widgets::ColorBar;
use crate::widgets::{AssoElement, DynamicCol};
use crate::{
actions::register_layer_actions,
pipeline::element::{Element, InstantElement, InstantElementDrawerType, TimeSeriesElement},
};
use crate::{ use crate::{
coords::{ coords::{
cms::CMS, cms::CMS,
@ -24,6 +27,7 @@ use crate::{
widgets::render::Layer, widgets::render::Layer,
CONFIG, PLUGIN_MANAGER, CONFIG, PLUGIN_MANAGER,
}; };
use crate::{data_utils::plugin_result_impl, pipeline::element::DataTarget};
use abi_stable::std_types::RStr; use abi_stable::std_types::RStr;
use adw::prelude::*; use adw::prelude::*;
use chrono::{prelude::*, Duration}; use chrono::{prelude::*, Duration};
@ -59,15 +63,24 @@ pub static FILE_PATH_ROOT: Lazy<Mutex<PathBuf>> = Lazy::new(|| Mutex::new(PathBu
pub type ElementKey = String; pub type ElementKey = String;
#[derive(Debug)]
pub enum LayerMsg {
Add(Layer),
Remove(usize),
SwitchToTime(usize),
Select(Vec<usize>),
RemoveSelected,
}
#[derive(Debug)] #[derive(Debug)]
pub enum AppMsg { pub enum AppMsg {
CloseRequest, CloseRequest,
Close, Close,
OpenDialog, OpenDialog,
SwitchToTime(usize), LayerManager(LayerMsg),
Layer,
NewElement(Element), NewElement(Element),
DeleteElement(ElementKey), DeleteElement(ElementKey),
NewLayer(Layer),
} }
pub type Buffer = Rc<RefCell<HashMap<String, BTreeMap<DateTime<Utc>, Option<RenderResult>>>>>; pub type Buffer = Rc<RefCell<HashMap<String, BTreeMap<DateTime<Utc>, Option<RenderResult>>>>>;
type RcDispatcher = Rc<Dispatcher>; type RcDispatcher = Rc<Dispatcher>;
@ -85,6 +98,9 @@ pub struct AppModel {
#[do_not_track] #[do_not_track]
render: Controller<MonitorModel>, render: Controller<MonitorModel>,
#[do_not_track] #[do_not_track]
sidebar: Controller<SideBarModel>,
selected_layer: Vec<usize>,
#[do_not_track]
layers: Rc<RefCell<Vec<Layer>>>, layers: Rc<RefCell<Vec<Layer>>>,
#[do_not_track] #[do_not_track]
elements: Vec<Arc<Mutex<TimeSeriesElement>>>, elements: Vec<Arc<Mutex<TimeSeriesElement>>>,
@ -158,7 +174,21 @@ impl Component for AppModel {
adw::ToastOverlay{ adw::ToastOverlay{
set_hexpand: true, set_hexpand: true,
set_vexpand: true, set_vexpand: true,
model.render.widget(), DynamicCol {
set_end_width: 300,
set_hexpand: true,
set_vexpand: true,
#[wrap(Some)]
#[name="paned"]
set_child_paned = &gtk::Paned{
#[local_ref]
#[wrap(Some)]
set_start_child=render->gtk::Frame{},
#[local_ref]
#[wrap(Some)]
set_end_child=sidebar->gtk::Box{},
}
}
}, },
}, },
home_stack_page = view_stack.add_titled(&home_page, Some("home"), "Home") -> adw::ViewStackPage{ home_stack_page = view_stack.add_titled(&home_page, Some("home"), "Home") -> adw::ViewStackPage{
@ -192,61 +222,83 @@ impl Component for AppModel {
root: Self::Root, root: Self::Root,
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
let layers = Rc::new(RefCell::new(Vec::with_capacity(20))); let layers = Rc::new(RefCell::new(vec![
Layer::new(true, "Layer 1".to_string(), AssoElement::Test),
Layer::new(true, "Layer 2".to_string(), AssoElement::Test),
]));
let control = ControlPanelModel::builder().launch(layers.clone()).forward( let control = ControlPanelModel::builder().launch(layers.clone()).forward(
sender.input_sender(), sender.input_sender(),
|msg| match msg { |msg| match msg {
ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::Close, ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::Close,
}, },
); );
let sidebar =
SideBarModel::builder()
.launch(layers.clone())
.forward(sender.input_sender(), |msg| {
AppMsg::LayerManager(match msg {
SideBarOutputMsg::SelectLayer(idx) => LayerMsg::Select(idx),
SideBarOutputMsg::NewLayer(layer) => LayerMsg::Add(layer),
SideBarOutputMsg::SwitchToTimeSeries(idx) => LayerMsg::SwitchToTime(idx),
})
});
let render = let render =
MonitorModel::builder() MonitorModel::builder()
.launch(layers.clone()) .launch(layers.clone())
.forward(sender.input_sender(), |a| match a { .forward(sender.input_sender(), |a| match a {
MonitorOutputMsg::LayerRenderFinished => AppMsg::Close, MonitorOutputMsg::LayerRenderFinished => AppMsg::Close,
MonitorOutputMsg::LayerSwitchToTime(idx) => AppMsg::SwitchToTime(idx),
_ => AppMsg::Close, _ => AppMsg::Close,
}); });
let setting = SettingModel::builder() let setting = SettingModel::builder()
.launch(()) .launch(())
.forward(sender.input_sender(), |a| AppMsg::Close); .forward(sender.input_sender(), |a| AppMsg::Close);
let mut dispatcher = Rc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1))); let mut dispatcher = Rc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1)));
let cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0)); let cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
let dialog_dispatcher = dispatcher.clone();
let dialog_render_sender = render.sender().clone();
let dialog = OpenDialog::builder()
.transient_for_native(&root)
.launch(OpenDialogSettings::default())
.forward(sender.input_sender(), move |response| match response {
OpenDialogResponse::Accept(path) => {
*FILE_PATH_ROOT.lock().unwrap() = path.clone();
let data = Self::open_file_only(path);
let meta: MetaInfo = (&data.meta).clone().into();
let (lat_start, lat_end) = meta.lat_range.unwrap();
let (lon_start, lon_end) = meta.lon_range.unwrap();
let element_impl = plugin_result_impl(&data);
let mut renderer = OffscreenRenderer::new(3000, 3000).unwrap();
let mut canvas = renderer.create_canvas();
let mut dialog_cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
let mut data_target = element_impl.render(&data, &mut canvas, &mut dialog_cms);
data_target.data = Some(Arc::new(data) as Arc<dyn Any + Send + Sync + 'static>);
let element = Element::create_instant( let dialog = {
InstantElementDrawerType::Prepared((data_target, Arc::new(element_impl))), let dialog_dispatcher = dispatcher.clone();
dialog_dispatcher.clone(), let dialog_sidebar_sender = sidebar.sender().clone();
"ET".to_string(), let dialog_render_sender = render.sender().clone();
) OpenDialog::builder()
.get_instance(); .transient_for_native(&root)
let layer = .launch(OpenDialogSettings::default())
Layer::new(true, "New Layer".to_string(), AssoElement::Instant(element)); .forward(sender.input_sender(), move |response| match response {
dialog_render_sender.emit(MonitorInputMsg::AddMetaItem(meta.to_map())); OpenDialogResponse::Accept(path) => {
dialog_render_sender.emit(MonitorInputMsg::SetRenderRange( *FILE_PATH_ROOT.lock().unwrap() = path.clone();
lon_start, lon_end, lat_start, lat_end, let data = Self::open_file_only(path);
)); let meta: MetaInfo = (&data.meta).clone().into();
AppMsg::NewLayer(layer) let (lat_start, lat_end) = meta.lat_range.unwrap();
} let (lon_start, lon_end) = meta.lon_range.unwrap();
_ => AppMsg::Close, let element_impl = plugin_result_impl(&data);
}); let mut renderer = OffscreenRenderer::new(3000, 3000).unwrap();
let mut canvas = renderer.create_canvas();
let mut dialog_cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
let data_target = element_impl.render(&data, &mut canvas, &mut dialog_cms);
let data_target = DataTarget::new(Some(data), data_target);
let element = Element::create_instant(
InstantElementDrawerType::Prepared((data_target, element_impl)),
dialog_dispatcher.clone(),
"ET".to_string(),
)
.get_instance();
let layer = Layer::new(
true,
"New Layer".to_string(),
AssoElement::Instant(element),
);
dialog_sidebar_sender.emit(SideBarInputMsg::AddMetaItems(meta.to_map()));
dialog_render_sender.emit(MonitorInputMsg::SetRenderRange(
lon_start, lon_end, lat_start, lat_end,
));
AppMsg::LayerManager(LayerMsg::Add(layer))
}
_ => AppMsg::Close,
})
};
let buffer: Buffer = Rc::new(RefCell::new(HashMap::new())); let buffer: Buffer = Rc::new(RefCell::new(HashMap::new()));
let model = AppModel { let model = AppModel {
@ -255,15 +307,21 @@ impl Component for AppModel {
waiting_for: None, waiting_for: None,
elements: Vec::with_capacity(20), elements: Vec::with_capacity(20),
open_dialog: dialog, open_dialog: dialog,
selected_layer: vec![],
sidebar,
control, control,
render, render,
layers, layers,
setting, setting,
tracker: 0, tracker: 0,
}; };
let render = model.render.widget();
let sidebar = model.sidebar.widget();
let widgets = view_output!(); let widgets = view_output!();
let mut group = RelmActionGroup::<FileActionGroup>::new(); let mut group = RelmActionGroup::<FileActionGroup>::new();
relm4::main_application().set_accelerators_for_action::<OpenAction>(&["<primary>O"]); relm4::main_application().set_accelerators_for_action::<OpenAction>(&["<primary>O"]);
register_layer_actions(&widgets.main_window, sender.clone());
let action: RelmAction<OpenAction> = { let action: RelmAction<OpenAction> = {
RelmAction::new_stateless(move |_| { RelmAction::new_stateless(move |_| {
sender.input(AppMsg::OpenDialog); sender.input(AppMsg::OpenDialog);
@ -271,7 +329,6 @@ impl Component for AppModel {
}; };
group.add_action(action); group.add_action(action);
group.register_for_widget(&widgets.main_window); group.register_for_widget(&widgets.main_window);
ComponentParts { model, widgets } ComponentParts { model, widgets }
} }
@ -284,31 +341,56 @@ impl Component for AppModel {
) { ) {
self.reset(); self.reset();
match msg { match msg {
AppMsg::NewLayer(layer) => { AppMsg::LayerManager(msg) => match msg {
(*self.layers).borrow_mut().push(layer); LayerMsg::Add(layer) => {
self.render.sender().send(MonitorInputMsg::RefreshLayerList); (*self.layers).borrow_mut().push(layer);
} self.sidebar.sender().send(SideBarInputMsg::RefreshList);
}
LayerMsg::SwitchToTime(idx) => {
let mut layer = (*self.layers).borrow_mut();
let switched_layer = layer.get_mut(idx).unwrap();
let asso_element = switched_layer.pop_associated_element();
if let AssoElement::Instant(e) = asso_element {
let dispatcher = self.dispatcher.clone();
let cms = self.cms.clone();
let (mut series, start_time) = e.to_time_series(dispatcher, cms);
switched_layer.set_time(start_time);
series.register(start_time).unwrap();
let element = Arc::new(Mutex::new(series));
switched_layer
.set_associated_element(AssoElement::TimeSeries(element.clone()));
self.elements.push(element);
}
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
}
LayerMsg::Select(idx) => {
self.set_selected_layer(idx);
}
LayerMsg::RemoveSelected => {
let mut layer = (*self.layers).borrow_mut();
let selected = self.selected_layer.clone();
for idx in selected {
layer.remove(idx);
}
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
}
LayerMsg::Remove(idx) => {
let mut layers = (*self.layers).borrow_mut();
let mut layer = layers.remove(idx);
if let AssoElement::TimeSeries(e) = layer.pop_associated_element() {
let size = Arc::strong_count(&e);
}
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
}
},
AppMsg::CloseRequest => { AppMsg::CloseRequest => {
relm4::main_application().quit(); relm4::main_application().quit();
} }
AppMsg::Close => {} AppMsg::Close => {}
AppMsg::SwitchToTime(idx) => {
let mut layer = (*self.layers).borrow_mut();
let switched_layer = layer.get_mut(idx).unwrap();
let asso_element = switched_layer.pop_associated_element();
if let AssoElement::Instant(e) = asso_element {
let dispatcher = self.dispatcher.clone();
let cms = self.cms.clone();
let (mut series, start_time) = e.to_time_series(dispatcher, cms);
switched_layer.set_time(start_time);
series.register(start_time).unwrap();
let element = Arc::new(Mutex::new(series));
switched_layer.set_associated_element(AssoElement::TimeSeries(element.clone()));
self.elements.push(element);
}
self.render.sender().send(MonitorInputMsg::RefreshLayerList);
}
AppMsg::OpenDialog => { AppMsg::OpenDialog => {
self.open_dialog.emit(OpenDialogMsg::Open); self.open_dialog.emit(OpenDialogMsg::Open);
} }

View File

@ -2,7 +2,7 @@ use super::messages::*;
use super::thumbnail::{ImgItem, TypedListView}; use super::thumbnail::{ImgItem, TypedListView};
use crate::data::{CoordType, Radar2d, RadarData2d}; use crate::data::{CoordType, Radar2d, RadarData2d};
use crate::plugin_system::init_plugin; use crate::plugin_system::init_plugin;
use crate::widgets::render::predefined::color_mapper::BoundaryNorm; use crate::predefined::color_mapper::BoundaryNorm;
use crate::widgets::render::Layer; use crate::widgets::render::Layer;
use crate::widgets::timeline::{Selection, TimeLine}; use crate::widgets::timeline::{Selection, TimeLine};
use abi_stable::std_types::RStr; use abi_stable::std_types::RStr;

View File

@ -1,8 +1,8 @@
pub mod app; pub mod app;
mod control_panel; mod control_panel;
mod monitor; pub mod monitor;
mod setting; mod setting;
pub mod sidebar;
pub use control_panel::*; pub use control_panel::*;
pub use monitor::*; pub use monitor::*;

View File

@ -1,20 +1,12 @@
use std::{collections::HashMap, fmt::Debug}; use std::fmt::Debug;
use crate::{ use crate::components::monitor::widget::Widget;
components::app::ElementKey,
pipeline::element::ElementID,
widgets::{render::Layer, widget::Widget},
};
pub enum MonitorInputMsg { pub enum MonitorInputMsg {
RefreshRender, RefreshRender,
AddWidget(Box<dyn Widget>), AddWidget(Box<dyn Widget>),
RemoveWidget, RemoveWidget,
AddMetaItem(HashMap<String, String>),
ClearMetaItems,
UpdateMetaItem(HashMap<String, String>),
RefreshTiles, RefreshTiles,
RefreshLayerList,
SetRenderRange(f64, f64, f64, f64), SetRenderRange(f64, f64, f64, f64),
ChangeZoom(f64), ChangeZoom(f64),
None, None,
@ -29,23 +21,15 @@ impl Debug for MonitorInputMsg {
MonitorInputMsg::SetRenderRange(_, _, _, _) => { MonitorInputMsg::SetRenderRange(_, _, _, _) => {
write!(f, "MonitorInputMsg::SetRenderRange") write!(f, "MonitorInputMsg::SetRenderRange")
} }
MonitorInputMsg::RefreshLayerList => write!(f, "MonitorInputMsg::RefreshLayerList"),
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"),
MonitorInputMsg::AddMetaItem(_) => write!(f, "MonitorInputMsg::RemoveWidget"),
MonitorInputMsg::ClearMetaItems => write!(f, "MonitorInputMsg::ClearMetaItems"),
MonitorInputMsg::UpdateMetaItem(_) => write!(f, "MonitorInputMsg::UpdateMetaItem"),
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub enum MonitorOutputMsg { pub enum MonitorOutputMsg {
LayerAdded(usize),
LayerRemoved(usize),
LayerUpdated(usize),
LayerSwitchToTime(usize),
LayerRenderFinished, LayerRenderFinished,
} }

View File

@ -1,4 +1,6 @@
pub mod messages; pub mod messages;
pub mod monitor; pub mod monitor;
pub mod sidebar; pub mod widget;
pub use monitor::*; pub use monitor::*;
pub use widget::*;

View File

@ -1,10 +1,9 @@
use super::messages::{MonitorInputMsg, MonitorOutputMsg}; use super::messages::{MonitorInputMsg, MonitorOutputMsg};
use crate::components::monitor::widget::{Widget, WidgetType};
use crate::coords::cms::CMS; use crate::coords::cms::CMS;
use crate::pipeline::offscreen_renderer::OffscreenRenderer; use crate::pipeline::offscreen_renderer::OffscreenRenderer;
use crate::widgets::predefined::color_mapper::BoundaryNorm; use crate::predefined::color_mapper::BoundaryNorm;
use crate::widgets::predefined::widgets::ColorBar;
use crate::widgets::render::RenderConfig; use crate::widgets::render::RenderConfig;
use crate::widgets::widget::{Widget, WidgetType};
use crate::widgets::WidgetFrame; use crate::widgets::WidgetFrame;
use crate::{ use crate::{
coords::{proj::Mercator, Mapper}, coords::{proj::Mercator, Mapper},
@ -19,7 +18,7 @@ use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tracing::*; use tracing::*;
use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg}; // use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg};
use crate::coords::Range; use crate::coords::Range;
use crate::map_tile::MapTile; use crate::map_tile::MapTile;
use crate::map_tile_utils::lat_lon_to_zoom; use crate::map_tile_utils::lat_lon_to_zoom;
@ -54,8 +53,6 @@ pub struct MonitorModel {
widgets: Vec<WidgetFrame>, widgets: Vec<WidgetFrame>,
#[no_eq] #[no_eq]
layers: Rc<RefCell<Vec<Layer>>>, layers: Rc<RefCell<Vec<Layer>>>,
#[no_eq]
sidebar: Controller<SideBarModel>,
} }
pub struct MonitorWidgets { pub struct MonitorWidgets {
@ -71,71 +68,50 @@ impl Component for MonitorModel {
view! { view! {
#[root] #[root]
adw::BreakpointBin { gtk::Frame{
set_hexpand: true, add_css_class: "rb",
set_vexpand: true, set_margin_all: 5,
set_height_request: 500, #[name="widget_layer"]
set_width_request: 700, gtk::Overlay{
#[wrap(Some)] #[name = "renderer"]
#[name="test"]
set_child = &DynamicCol{
set_end_width: 300,
set_hexpand: true,
set_vexpand: true,
#[wrap(Some)]
#[name="paned"]
set_child_paned=&gtk::Paned{
#[wrap(Some)] #[wrap(Some)]
#[name="render"] set_child = &Render{
set_start_child=&gtk::Frame{ #[track = "model.changed(MonitorModel::render_cfg())"]
add_css_class: "rb", set_cfg: model.render_cfg,
set_margin_all: 5, #[track = "model.changed(MonitorModel::render_range())"]
#[name="widget_layer"] set_view: model.render_range,
gtk::Overlay{ set_tiles: model.map_tile_getter.clone(),
#[name = "renderer"] connect_render_status_notify[sender] => move |r| {
#[wrap(Some)] sender.output(MonitorOutputMsg::LayerRenderFinished);
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,
set_tiles: model.map_tile_getter.clone(),
connect_render_status_notify[sender] => move |r| {
sender.output(MonitorOutputMsg::LayerRenderFinished);
},
connect_range_changing_notify[sender] => move |r| {
sender.input(MonitorInputMsg::RefreshTiles);
},
connect_scale_notify[sender] => move |r| {
let scale = r.scale();
{
let initial = r.scale_rate();
let mut rate_start = initial_rate.lock().unwrap();
if rate_start.is_none() {
*rate_start = Some(scale);
}
}
debouncer.call(scale);
},
set_interior_layers: model.layers.clone(),
},
add_overlay=&gtk::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
}, },
connect_range_changing_notify[sender] => move |r| {
sender.input(MonitorInputMsg::RefreshTiles);
},
connect_scale_notify[sender] => move |r| {
let scale = r.scale();
{
let initial = r.scale_rate();
let mut rate_start = initial_rate.lock().unwrap();
if rate_start.is_none() {
*rate_start = Some(scale);
}
}
debouncer.call(scale);
},
set_interior_layers: model.layers.clone(),
}, },
#[wrap(Some)] add_overlay=&gtk::Button{
set_end_child=model.sidebar.widget(), 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
},
} }
}
} }
fn update_with_view( fn update_with_view(
@ -150,25 +126,13 @@ impl Component for MonitorModel {
MonitorInputMsg::RefreshRender => { MonitorInputMsg::RefreshRender => {
widgets.renderer.queue_render(); widgets.renderer.queue_render();
} }
MonitorInputMsg::RefreshLayerList => {
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
sender.input(MonitorInputMsg::RefreshRender);
}
MonitorInputMsg::AddMetaItem(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) => {
let current_rate = widgets.renderer.scale_rate(); let current_rate = widgets.renderer.scale_rate();
self.set_render_range((lat_start, lat_end, lon_start, lon_end)); self.set_render_range((lat_start, lat_end, lon_start, lon_end));
let new_rate = widgets.renderer.scale_rate(); let new_rate = widgets.renderer.scale_rate();
let zoom: f64 = (current_rate / new_rate).log2(); let zoom: f64 = (new_rate / current_rate).log2();
sender.input(MonitorInputMsg::ChangeZoom(zoom)); sender.input(MonitorInputMsg::ChangeZoom(zoom));
} }
MonitorInputMsg::ClearMetaItems => self.sidebar.emit(SideBarInputMsg::ClearMetaItems),
MonitorInputMsg::UpdateMetaItem(map) => {
self.sidebar.emit(SideBarInputMsg::ClearMetaItems);
self.sidebar.emit(SideBarInputMsg::AddMetaItems(map))
}
MonitorInputMsg::RefreshTiles => { MonitorInputMsg::RefreshTiles => {
let ((x1, x2), (y1, y2)) = widgets.renderer.render_range(); let ((x1, x2), (y1, y2)) = widgets.renderer.render_range();
self.load_tile(&sender, ((y1 as f32, y2 as f32), (x1 as f32, x2 as f32))); self.load_tile(&sender, ((y1 as f32, y2 as f32), (x1 as f32, x2 as f32)));
@ -203,16 +167,16 @@ impl Component for MonitorModel {
root: Self::Root, root: Self::Root,
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
let sidebar_sender = sender.clone(); // let sidebar_sender = sender.clone();
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::SwitchToTimeSeries(layer) => { // SideBarOutputMsg::SwitchToTimeSeries(layer) => {
sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer)); // sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer));
MonitorInputMsg::None // MonitorInputMsg::None
} // }
_ => MonitorInputMsg::None, // _ => MonitorInputMsg::None,
}); // });
let render_cfg = RenderConfig { let render_cfg = RenderConfig {
padding: [0.0, 0.0, 0.0, 0.0], padding: [0.0, 0.0, 0.0, 0.0],
@ -231,7 +195,6 @@ impl Component for MonitorModel {
layers: init, layers: init,
zoom: 4, zoom: 4,
map_tile_getter: Rc::new(MapTile::default()), map_tile_getter: Rc::new(MapTile::default()),
sidebar,
tracker: 0, tracker: 0,
}; };

View File

@ -11,8 +11,8 @@ pub enum WidgetType {
} }
pub trait Widget: 'static + Send + Sync { pub trait Widget: 'static + Send + Sync {
fn opengl_render(&self, canvas: &mut Canvas<OpenGl>, cms: CMS) {} fn opengl_render(&self, layers: &Vec<Layer>, canvas: &mut Canvas<OpenGl>, cms: CMS) {}
fn cairo_render(&self, canvas: &gtk::cairo::Context, w: f32, h: f32) {} fn cairo_render(&self, layers: &Vec<Layer>, canvas: &gtk::cairo::Context, w: f32, h: f32) {}
fn widget_type(&self) -> WidgetType; fn widget_type(&self) -> WidgetType;
fn size(&self) -> (f32, f32); fn size(&self) -> (f32, f32);

View File

@ -2,7 +2,8 @@ use super::dispatcher_list::PathItem;
use crate::{ use crate::{
config::PATH_FORMATS, config::PATH_FORMATS,
data::{self, CoordType, Radar2d}, data::{self, CoordType, Radar2d},
widgets::render::{predefined::color_mapper::BoundaryNorm, Layer}, predefined::color_mapper::BoundaryNorm,
widgets::render::Layer,
CONFIG, PLUGIN_MANAGER, CONFIG, PLUGIN_MANAGER,
}; };
use adw::prelude::*; use adw::prelude::*;

View File

@ -1,9 +1,7 @@
use crate::actions::*;
use gtk::prelude::*; use gtk::prelude::*;
use relm4::{ use relm4::{
factory::FactoryView, actions::traits::ActionablePlus, factory::FactoryView, gtk, prelude::*, FactorySender,
gtk,
prelude::{DynamicIndex, FactoryComponent},
FactorySender,
}; };
use super::SideBarInputMsg; use super::SideBarInputMsg;
@ -40,10 +38,9 @@ impl FactoryComponent for BottomBarModel {
view! { view! {
#[root] #[root]
gtk::Box{ gtk::Button{
gtk::Button{ set_icon_name=self.icon.as_str(),
set_icon_name=self.icon.as_str(), ActionablePlus::set_stateless_action::<RemoveLayerAction>: &(),
}
} }
} }

View File

@ -9,7 +9,7 @@ use relm4::{
}; };
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub(super) struct MyListItem { pub(in crate::components) struct MyListItem {
tag: String, tag: String,
info: String, info: String,
} }
@ -20,7 +20,7 @@ impl MyListItem {
} }
} }
pub(super) struct TagColumn; pub(in crate::components) struct TagColumn;
impl LabelColumn for TagColumn { impl LabelColumn for TagColumn {
type Item = MyListItem; type Item = MyListItem;
@ -38,7 +38,7 @@ impl LabelColumn for TagColumn {
} }
} }
pub(super) struct InfoColumn; pub(in crate::components) struct InfoColumn;
impl RelmColumn for InfoColumn { impl RelmColumn for InfoColumn {
type Root = gtk::Label; type Root = gtk::Label;

View File

@ -1,3 +1,4 @@
use crate::actions::*;
use crate::widgets::AssoElement; use crate::widgets::AssoElement;
use abi_stable::type_level::trait_marker::Hash; use abi_stable::type_level::trait_marker::Hash;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -20,11 +21,7 @@ use relm4::{
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use crate::components::app::AppMsg; use crate::components::app::AppMsg;
use crate::{ use crate::{chart::Chart, predefined::color_mapper::BoundaryNorm, widgets::render::Layer};
chart::Chart,
data::Npz,
widgets::render::{predefined::color_mapper::BoundaryNorm, Layer},
};
use super::{ use super::{
bottom_bar::BottomBarModel, bottom_bar::BottomBarModel,
@ -35,7 +32,7 @@ pub struct SideBarModel {
layers: Rc<RefCell<Vec<Layer>>>, layers: Rc<RefCell<Vec<Layer>>>,
selected_layer_idx: usize, selected_layer_idx: usize,
counter: u8, counter: u8,
list_view_wrapper: TypedListView<LayerItem, gtk::SingleSelection>, list_view_wrapper: TypedListView<LayerItem, gtk::MultiSelection>,
bottom_bar_vec: FactoryVecDeque<BottomBarModel>, bottom_bar_vec: FactoryVecDeque<BottomBarModel>,
meta_list_view: TypedColumnView<MyListItem, gtk::NoSelection>, meta_list_view: TypedColumnView<MyListItem, gtk::NoSelection>,
} }
@ -50,6 +47,7 @@ pub enum SideBarInputMsg {
#[derive(Debug)] #[derive(Debug)]
pub enum SideBarOutputMsg { pub enum SideBarOutputMsg {
SelectLayer(Vec<usize>),
NewLayer(Layer), NewLayer(Layer),
SwitchToTimeSeries(usize), SwitchToTimeSeries(usize),
} }
@ -104,7 +102,8 @@ impl SimpleComponent for SideBarModel {
.build() -> gtk::ScrolledWindow{ .build() -> gtk::ScrolledWindow{
#[wrap(Some)] #[wrap(Some)]
#[local_ref] #[local_ref]
set_child=my_view -> gtk::ListView{}, set_child=my_view -> gtk::ListView{
},
set_margin_horizontal:5, set_margin_horizontal:5,
set_margin_vertical:3 set_margin_vertical:3
}, },
@ -132,8 +131,19 @@ impl SimpleComponent for SideBarModel {
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
// Initialize the ListView wrapper // Initialize the ListView wrapper
let mut list_view_wrapper: TypedListView<LayerItem, gtk::SingleSelection> = let mut list_view_wrapper: TypedListView<LayerItem, gtk::MultiSelection> =
TypedListView::with_sorting(); TypedListView::with_sorting();
list_view_wrapper.selection_model.connect_selection_changed(
clone!(@strong sender => move |s,_, _| {
let selection = s.selection();
let (iter, first) = gtk::BitsetIter::init_first(&selection).unwrap();
let mut result = vec![first as usize];
result.extend(iter.map(|v| v as usize));
sender.output(SideBarOutputMsg::SelectLayer(result));
}),
);
// let mut bottom_bar_vec = FactoryVecDeque::new(gtk::Box::default(), sender.input_sender()); // let mut bottom_bar_vec = FactoryVecDeque::new(gtk::Box::default(), sender.input_sender());
let mut bottom_bar_vec = let mut bottom_bar_vec =
@ -270,11 +280,13 @@ impl LayerItem {
} }
struct Widgets { struct Widgets {
label: gtk::Label, label: gtk::EditableLabel,
screen_shot: gtk::Image, screen_shot: gtk::Image,
status: gtk::Label, status_icon: gtk::Image,
button: gtk::CheckButton, status_label: gtk::Label,
menu: gtk::PopoverMenu, visible: gtk::Button,
opacity: gtk::Label,
menu: gtk::Popover,
} }
impl RelmListItem for LayerItem { impl RelmListItem for LayerItem {
@ -282,13 +294,22 @@ impl RelmListItem for LayerItem {
type Widgets = Widgets; type Widgets = Widgets;
fn setup(_item: &gtk::ListItem) -> (gtk::Box, Widgets) { fn setup(_item: &gtk::ListItem) -> (gtk::Box, Widgets) {
let position = _item.position() as u8;
relm4::menu! { relm4::menu! {
main_menu: { main_menu: {
custom: "MyWidget",
"Remove" => RemoveLayerAction,
section!{
"test" => RemoveLayerAction,
"select" => AddLayerAction
}
} }
} }
relm4::view! { relm4::view! {
my_box = gtk::Box { my_box = gtk::Box {
set_valign: gtk::Align::Center,
gtk::Frame{ gtk::Frame{
set_margin_end: 10, set_margin_end: 10,
#[name = "screen_shot"] #[name = "screen_shot"]
@ -296,31 +317,46 @@ impl RelmListItem for LayerItem {
set_size_request: (65, 40), set_size_request: (65, 40),
} }
}, },
#[name = "label"] gtk::Grid{
gtk::Label{ set_row_homogeneous: true,
set_halign: gtk::Align::Start, attach[0,0,1,1] = &gtk::Box{
}, #[name="status_icon"]
#[name = "status"] gtk::Image{
gtk::Label{ inline_css: "-gtk-icon-transform: scale(0.8);",
set_halign: gtk::Align::Start },
}, #[name="status_label"]
gtk::Label{ gtk::Label{
set_hexpand: true, add_css_class:"tooltip",
}, set_halign: gtk::Align::Start,
#[name = "button"] }
gtk::CheckButton{ },
set_halign: gtk::Align::End, #[name="label"]
attach[0,1,1,1] = &gtk::EditableLabel{
set_hexpand: true,
},
#[name="opacity"]
attach[0,2,1,1] = &gtk::Label{
add_css_class:"tooltip",
set_halign: gtk::Align::Start
},
}, },
#[name="visible"]
gtk::Button{
set_vexpand: false,
set_hexpand: false,
},
#[name = "menu"] #[name = "menu"]
gtk::PopoverMenu::from_model(Some(&main_menu)){} gtk::Popover{}
} }
} }
let widgets = Widgets { let widgets = Widgets {
screen_shot, screen_shot,
label, label,
status, status_icon,
button, status_label,
opacity,
visible,
menu, menu,
}; };
@ -330,24 +366,28 @@ impl RelmListItem for LayerItem {
fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) { fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
let Widgets { let Widgets {
label, label,
button, visible,
screen_shot, screen_shot,
status, status_label,
status_icon,
opacity,
menu, menu,
} = widgets; } = widgets;
relm4::menu! { status_label.set_label(&match self.status {
main_menu: {
}
}
menu.set_menu_model(Some(&main_menu));
status.set_label(&match self.status {
LayerStatus::BindToTime(t) => format!("Bind To Time: {}", t), LayerStatus::BindToTime(t) => format!("Bind To Time: {}", t),
LayerStatus::Instance => "Instance".to_string(), LayerStatus::Instance => "Instance".to_string(),
LayerStatus::BindToOtherLayer(idx) => format!("Bind To Layer: {}", idx), LayerStatus::BindToOtherLayer(idx) => format!("Bind To Layer: {}", idx),
}); });
status_icon.set_icon_name(Some(match self.status {
LayerStatus::BindToTime(_) => "timer-filled",
LayerStatus::Instance => "timer-filled",
LayerStatus::BindToOtherLayer(_) => "timer-filled",
}));
opacity.set_markup(&format!("<b>Opacity:</b> 0.5"));
let gesture_click = gtk::GestureClick::new(); let gesture_click = gtk::GestureClick::new();
gesture_click.set_button(gtk::gdk::BUTTON_SECONDARY); gesture_click.set_button(gtk::gdk::BUTTON_SECONDARY);
screen_shot.set_paintable(self.img.as_ref()); screen_shot.set_paintable(self.img.as_ref());
@ -358,8 +398,10 @@ impl RelmListItem for LayerItem {
menu.popup(); menu.popup();
})); }));
visible.set_icon_name("eye-filled");
_root.add_controller(gesture_click); _root.add_controller(gesture_click);
label.set_label(&self.layer_name); label.set_text(&self.layer_name);
button.set_active(self.visiable); // button.set_active(self.visiable);
} }
} }

View File

@ -1,6 +1,6 @@
use std::ops::Range;
use geo_types::LineString;
use crate::coords::Mapper; use crate::coords::Mapper;
use geo_types::LineString;
use std::ops::Range;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CMS { pub struct CMS {

View File

@ -1,9 +1,9 @@
use num_traits::AsPrimitive; use num_traits::AsPrimitive;
use num_traits::Num; use num_traits::Num;
pub mod cms;
pub mod mapper; pub mod mapper;
pub mod proj; pub mod proj;
pub mod wgs84; pub mod wgs84;
pub mod cms;
pub use mapper::Mapper; pub use mapper::Mapper;
// pub use wgs84::LatLonCoord; // pub use wgs84::LatLonCoord;

View File

@ -1,3 +0,0 @@
button {
color: magenta;
}

View File

@ -2,18 +2,27 @@ pub mod meta;
use crate::errors::DataError; use crate::errors::DataError;
use async_trait::async_trait; use async_trait::async_trait;
pub use meta::MetaInfo; pub use meta::MetaInfo;
use ndarray::iter::Indices;
use ndarray::{ use ndarray::{
s, Array1, Array2, Array3, ArrayBase, DataMut, Dimension, Ix1, Ix2, OwnedRepr, RawDataClone, s, Array, Array1, Array2, Array3, ArrayBase, Axis, DataMut, Dimension, Ix1, Ix2, Ix3,
OwnedRepr, RawDataClone, Slice, ViewRepr,
}; };
use npyz::{npz::NpzArchive, Deserialize}; use npyz::{npz::NpzArchive, Deserialize};
use num_traits::{AsPrimitive, FromPrimitive, Num}; use num_traits::{AsPrimitive, FromPrimitive, Num};
use std::io::{Cursor, Seek};
use std::{self, f64::consts::PI, fmt::Debug, io::BufReader, path::Path}; use std::{self, f64::consts::PI, fmt::Debug, io::BufReader, path::Path};
use tokio::fs::File;
use tokio::io::{self, AsyncReadExt};
pub type Radar2d<T> = RadarData2d<T, OwnedRepr<T>>; pub type Radar2d<T> = RadarData2d<T, OwnedRepr<T>, OwnedRepr<f64>, OwnedRepr<f64>>;
pub type Radar3d<T> = RadarData3d<T, OwnedRepr<T>, OwnedRepr<f64>, OwnedRepr<f64>, OwnedRepr<f64>>;
pub type Radar2dRef<'a, T> = RadarData2d<T, ViewRepr<&'a T>, ViewRepr<&'a f64>, ViewRepr<&'a f64>>;
pub type Radar3dRef<'a, T> =
RadarData3d<T, ViewRepr<&'a T>, ViewRepr<&'a f64>, ViewRepr<&'a f64>, ViewRepr<&'a f64>>;
#[derive(Clone, Copy, Debug)]
pub enum CoordType {
Polar,
LatLon,
}
pub trait MultiDimensionData<T> pub trait MultiDimensionData<T>
where where
T: Num + Clone + PartialEq + PartialOrd, T: Num + Clone + PartialEq + PartialOrd,
@ -24,248 +33,84 @@ where
} }
#[derive(Clone)] #[derive(Clone)]
pub struct RadarData2d<T, Raw, X = f64, Y = f64, I = Ix1> pub struct RadarData2d<T, Raw, X, Y, I = Ix1>
where where
T: Num + Clone + PartialEq + PartialOrd, T: Num + Clone + PartialEq + PartialOrd,
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone, Raw: ndarray::Data<Elem = T> + ndarray::RawDataClone,
X: Num, X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Y: Num, Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
{ {
pub dim1: ArrayBase<OwnedRepr<X>, I>, pub dim1: ArrayBase<X, I>,
pub dim2: ArrayBase<OwnedRepr<Y>, I>, pub dim2: ArrayBase<Y, I>,
pub data: ArrayBase<Raw, Ix2>, pub data: ArrayBase<Raw, Ix2>,
pub fill_value: T, pub fill_value: T,
pub coord_type: CoordType, pub coord_type: CoordType,
} }
impl<T, Raw, X, Y, I> Debug for RadarData2d<T, Raw, X, Y, I> impl<T> Radar2d<T>
where where
T: Num + Clone + PartialEq + PartialOrd + Debug, T: Num + Clone + PartialEq + PartialOrd,
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
X: Num + Debug,
Y: Num + Debug,
I: Dimension,
{ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { pub fn as_ref<'a>(&'a self) -> Radar2dRef<'a, T> {
f.debug_struct("RadarData2d") RadarData2d {
.field("dim1", &self.dim1) dim1: self.dim1.view(),
.field("dim2", &self.dim2) dim2: self.dim2.view(),
.field("data", &self.data) data: self.data.view(),
.field("fill_value", &self.fill_value) fill_value: self.fill_value.clone(),
.field("coord_type", &self.coord_type) coord_type: self.coord_type.clone(),
.finish() }
} }
} }
impl<T: Num + Clone + PartialEq + PartialOrd> Radar2d<T> { #[derive(Clone)]
pub fn load(path: impl AsRef<Path>, meth: impl DataLoader<T, Self>) -> Result<Self, DataError> { pub struct RadarData3d<T, Raw, X, Y, Z, I = Ix1>
Ok(meth.load(path)?) where
} T: Num + Clone + PartialEq + PartialOrd,
Raw: ndarray::Data<Elem = T> + ndarray::RawDataClone,
X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Z: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
{
pub dim1: ArrayBase<X, I>,
pub dim2: ArrayBase<Y, I>,
pub dim3: ArrayBase<Z, I>,
pub data: ArrayBase<Raw, Ix3>,
pub fill_value: T,
pub coord_type: CoordType,
} }
pub struct RadarData3d<T, X = f64, Y = f64, Z = f64> impl<T, Raw, X, Y, Z> RadarData3d<T, Raw, X, Y, Z>
where where
T: Num, T: Num + Clone + PartialEq + PartialOrd,
X: Num, X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Y: Num, Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Z: Num, Z: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Raw: ndarray::Data<Elem = T> + ndarray::RawDataClone,
{ {
pub dim1: Array1<X>, pub fn index_axis(&self, axis: Axis, slice: usize) -> Radar2dRef<T> {
pub dim2: Array1<Y>,
pub dim3: Array1<Z>,
pub data: Array3<T>,
}
impl<T, Raw> RadarData2d<T, Raw>
where
T: Num + AsPrimitive<f64> + FromPrimitive + Clone + Debug + PartialOrd + PartialEq,
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
{
pub fn downsample(&self, output_shape: (usize, usize), meth: DownSampleMeth) -> Radar2d<T> {
let shape = self.data.shape(); let shape = self.data.shape();
assert!(shape[0] > output_shape.0 && shape[1] > output_shape.1); println!("shape: {:?}", shape);
let dim1 = self.dim1.view();
if shape[0] == output_shape.0 && shape[1] == output_shape.1 { let dim2 = self.dim2.view();
return Radar2d { let view = self.data.index_axis(axis, slice);
dim1: self.dim1.to_owned(), RadarData2d {
dim2: self.dim2.to_owned(),
data: self.data.to_owned(),
fill_value: self.fill_value.clone(),
coord_type: self.coord_type.clone(),
};
}
let scale_x = shape[1] as f64 / output_shape.1 as f64;
let scale_y = shape[0] as f64 / output_shape.0 as f64;
let mut output: Array2<T> = Array2::zeros(output_shape);
let dim1 = Array1::linspace(
*self.dim1.first().unwrap(),
*self.dim1.last().unwrap(),
output_shape.1,
);
let dim2 = Array1::linspace(
*self.dim2.first().unwrap(),
*self.dim2.last().unwrap(),
output_shape.0,
);
output.iter_mut().enumerate().for_each(|(s, vv)| {
let ri = s / output_shape.1;
let ci = s % output_shape.1;
let src_yf0 = scale_y * ri as f64;
let src_yf1 = src_yf0 + scale_y;
let src_y0 = src_yf0.floor() as usize;
let src_y1 = src_yf1.floor() as usize;
let src_xf0 = scale_x * ci as f64;
let src_xf1 = src_xf0 + scale_x;
let src_x0 = src_xf0.floor() as usize;
let src_x1 = src_xf1.floor() as usize;
match meth {
DownSampleMeth::MEAN => {}
DownSampleMeth::VAR | DownSampleMeth::STD => self.var(
src_yf0, src_yf1, src_xf0, src_xf1, src_y0, src_y1, src_x0, src_x1, vv,
),
}
});
if let DownSampleMeth::STD = meth {
output = output.mapv(|x| T::from_f64(f64::sqrt(x.as_())).unwrap());
}
return Radar2d {
dim1, dim1,
dim2, dim2,
data: view,
fill_value: self.fill_value.clone(), fill_value: self.fill_value.clone(),
data: output,
coord_type: self.coord_type.clone(), coord_type: self.coord_type.clone(),
};
}
fn var(
&self,
src_yf0: f64,
src_yf1: f64,
src_xf0: f64,
src_xf1: f64,
src_y0: usize,
mut src_y1: usize,
src_x0: usize,
mut src_x1: usize,
row: &mut T,
) {
let wy0 = 1f64 - (src_yf0 - src_y0 as f64);
let mut wy1 = src_yf1 - src_y1 as f64;
let wx0 = 1f64 - (src_xf0 - src_x0 as f64);
let mut wx1 = src_xf1 - src_x1 as f64;
if wy1 < f64::EPSILON {
wy1 = 1f64;
if src_y1 > src_y0 {
src_y1 -= 1;
}
} }
if wx1 < f64::EPSILON {
wx1 = 1f64;
if src_x1 > src_x0 {
src_x1 -= 1;
}
}
let mut v_sum: f64 = 0.0;
let mut w_sum = 0f64;
let mut wv_sum = 0f64;
let mut wvv_sum = 0f64;
for src_y in src_y0..=src_y1 {
let wy = if src_y == src_y0 {
wy0
} else {
if src_y == src_y1 {
wy1
} else {
1.0
}
};
for src_x in src_x0..=src_x1 {
let wx = if src_x == src_x0 {
wx0
} else {
if src_x == src_x1 {
wx1
} else {
1.0
}
};
let v = self.data[[src_y, src_x]].as_();
let w = wx * wy;
v_sum += v;
w_sum += w;
wv_sum += w * v;
wvv_sum += w * v * v;
}
if w_sum < f64::EPSILON {
return;
}
let v = ((wvv_sum * w_sum - wv_sum * wv_sum) / w_sum / w_sum).clamp(-125f64, 124f64);
*row = T::from_f64(v).unwrap();
}
}
fn split(&self) -> [RadarData2d<T, ndarray::ViewRepr<&T>>; 4] {
let middle_dim1 = self.dim1.len() / 2;
let middle_dim2 = self.dim2.len() / 2;
let lt = self.data.slice(s![..middle_dim1, ..middle_dim2]);
let rt = self.data.slice(s![middle_dim1.., ..middle_dim2]);
let lb = self.data.slice(s![..middle_dim1, middle_dim2..]);
let rb = self.data.slice(s![middle_dim1.., middle_dim2..]);
return [
RadarData2d {
dim1: self.dim1.slice(s![..middle_dim1]).to_owned(),
dim2: self.dim2.slice(s![..middle_dim2]).to_owned(),
data: lt,
fill_value: self.fill_value.clone(),
coord_type: self.coord_type,
},
RadarData2d {
dim1: self.dim1.slice(s![middle_dim1..]).to_owned(),
dim2: self.dim2.slice(s![..middle_dim2]).to_owned(),
data: rt,
fill_value: self.fill_value.clone(),
coord_type: self.coord_type,
},
RadarData2d {
dim1: self.dim1.slice(s![..middle_dim1]).to_owned(),
dim2: self.dim2.slice(s![middle_dim2..]).to_owned(),
data: lb,
fill_value: self.fill_value.clone(),
coord_type: self.coord_type,
},
RadarData2d {
dim1: self.dim1.slice(s![middle_dim1..]).to_owned(),
dim2: self.dim2.slice(s![middle_dim2..]).to_owned(),
data: rb,
fill_value: self.fill_value.clone(),
coord_type: self.coord_type,
},
];
} }
} }
impl<T, Raw> MultiDimensionData<T> for RadarData2d<T, Raw> impl<T, Raw, X, Y, Z> MultiDimensionData<T> for RadarData3d<T, Raw, X, Y, Z>
where where
T: Num + Clone, T: Num + Clone,
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Raw: ndarray::Data<Elem = T> + Clone + RawDataClone + DataMut, X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Z: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Raw: ndarray::Data<Elem = T> + RawDataClone + DataMut,
{ {
fn map_by_fn<F>(&mut self, f: F) fn map_by_fn<F>(&mut self, f: F)
where where
@ -275,285 +120,18 @@ where
} }
} }
pub trait DataLoader<V, T> impl<T, Raw, X, Y> MultiDimensionData<T> for RadarData2d<T, Raw, X, Y>
where where
V: Num + Clone + PartialEq + PartialOrd, T: Num + Clone,
T: MultiDimensionData<V>,
{
fn load<P: AsRef<Path>>(&self, path: P) -> Result<T, DataError>;
}
#[async_trait]
pub trait AsyncDataLoader<V, T>
where
V: Num + Clone + PartialEq + PartialOrd,
T: MultiDimensionData<V> + Send + Sync,
{
async fn load<P: AsRef<Path> + Send>(&self, path: P) -> Result<T, DataError>;
}
pub struct Npz;
impl Npz {
#[inline]
fn load_1d<T: Num + Deserialize, R: std::io::Read + Seek>(
&self,
data: &mut NpzArchive<R>,
name: &str,
) -> Result<Array1<T>, DataError> {
// let mut data = npyz::npz::NpzArchive::open(path)?;
let a = data.by_name(name)?.unwrap();
let b: Vec<T> = a.into_vec().unwrap();
Ok(Array1::from_shape_vec(b.len(), b).unwrap())
}
#[inline]
fn load_2d<T: Num + Deserialize, R: std::io::Read + Seek>(
&self,
// path: &Path,
data: &mut NpzArchive<R>,
name: &str,
) -> Result<Array2<T>, DataError> {
// let mut data = npyz::npz::NpzArchive::open(path)?;
let a = data.by_name(name)?.unwrap();
let shape = a.shape().to_vec();
let b: Vec<T> = a.into_vec().unwrap();
Ok(Array2::from_shape_vec((shape[0] as usize, shape[1] as usize), b).unwrap())
}
fn load_3d(&self, data: &mut NpzArchive<BufReader<std::fs::File>>) {}
}
impl<T> DataLoader<T, RadarData2d<T, OwnedRepr<T>>> for Npz
where
T: Num + Clone + Deserialize + FromPrimitive,
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
Raw: ndarray::Data<Elem = T> + RawDataClone + DataMut,
{ {
fn load<P: AsRef<Path>>(&self, path: P) -> Result<RadarData2d<T, OwnedRepr<T>>, DataError> { fn map_by_fn<F>(&mut self, f: F)
let mut data: NpzArchive<BufReader<std::fs::File>> = npyz::npz::NpzArchive::open(path)?;
let dim1 = self.load_1d::<f64, BufReader<std::fs::File>>(&mut data, "lon")?;
let dim2 = self.load_1d::<f64, BufReader<std::fs::File>>(&mut data, "lat")?;
let value = self.load_2d::<T, BufReader<std::fs::File>>(&mut data, "value")?;
Ok(RadarData2d {
dim1: dim1,
dim2: dim2,
fill_value: T::from_f64(-125.0).unwrap(),
data: value,
coord_type: CoordType::LatLon,
})
}
}
impl<T> AsyncDataLoader<T, RadarData2d<T, OwnedRepr<T>>> for Npz
where
T: Num + Clone + Deserialize + FromPrimitive,
T: PartialEq + PartialOrd + Send + Sync,
{
fn load<'life0, 'async_trait, P>(
&'life0 self,
path: P,
) -> core::pin::Pin<
Box<
dyn core::future::Future<Output = Result<RadarData2d<T, OwnedRepr<T>>, DataError>>
+ core::marker::Send
+ 'async_trait,
>,
>
where where
P: 'async_trait + AsRef<Path> + Send, F: FnMut(&mut T),
'life0: 'async_trait,
Self: 'async_trait,
{ {
Box::pin(async { self.data.map_inplace(f);
let mut f = File::open(path).await?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer).await?;
let mut data = npyz::npz::NpzArchive::new(Cursor::new(buffer))?;
let dim1 = self.load_1d::<f64, Cursor<Vec<u8>>>(&mut data, "lon")?;
let dim2 = self.load_1d::<f64, Cursor<Vec<u8>>>(&mut data, "lat")?;
let value = self.load_2d::<T, Cursor<Vec<u8>>>(&mut data, "value")?;
Ok(RadarData2d {
dim1: dim1,
dim2: dim2,
fill_value: T::from_f64(-125.0).unwrap(),
data: value,
coord_type: CoordType::LatLon,
})
})
} }
} }
// fn resample(
// &self,
// width_rate: f64,
// height_rate: f64,
// filter_len: f64,
// ) -> Result<RadarData2d<T, OwnedRepr<T>>, DataError> {
// let width_rate = width_rate.min(1.0);
// let height_rate = height_rate.min(1.0);
// match self.coord_type {
// CoordType::Polar => Err(DataError::FormatError),
// CoordType::LatLon => {
// let width_filtered: ArrayBase<ndarray::OwnedRepr<T>, Ix2> =
// Self::_resample(&self.data, width_rate, filter_len);
// let result: ArrayBase<OwnedRepr<T>, Ix2> =
// Self::_resample(&width_filtered.t(), height_rate, filter_len)
// .t()
// .to_owned();
// let new_dim1: ArrayBase<OwnedRepr<f64>, Ix1> = Self::_resample(
// &Array2::from_shape_vec((1, self.dim1.len()), self.dim1.to_vec()).unwrap(),
// width_rate,
// filter_len,
// )
// .slice(s![0, ..])
// .to_owned();
// let new_dim2: ArrayBase<OwnedRepr<f64>, Ix1> = Self::_resample(
// &Array2::from_shape_vec((1, self.dim2.len()), self.dim2.to_vec()).unwrap(),
// height_rate,
// filter_len,
// )
// .slice(s![0, ..])
// .to_owned();
// Ok(RadarData2d {
// dim1: new_dim1,
// dim2: new_dim2,
// data: result,
// coord_type: self.coord_type.to_owned(),
// })
// }
// }
// }
// fn _resample<'a, V, R: ndarray::Data<Elem = V>>(
// data: &'a ArrayBase<R, Ix2>,
// rate: f64,
// filter_len: f64,
// ) -> Array2<V>
// where
// V: Num + Clone + AsPrimitive<f64> + FromPrimitive,
// {
// let ori_width = data.ncols();
// let ori_height = data.nrows();
// let new_width = (ori_width as f64 * rate).ceil() as usize;
// let mut result: Array2<V> = Array2::zeros((ori_height, new_width));
// (0..ori_height).into_iter().for_each(|height| {
// for width in 0..new_width {
// let center_x = (width as f64 + 0.5) / new_width as f64 * ori_width as f64;
// let filter_start = center_x - filter_len / 2.0;
// let start_idx = (filter_start - 0.5).ceil() as usize;
// let mut value_sum = 0.0;
// let mut filter_sum = 0.0;
// for i in 0..filter_len as usize {
// let input_x = start_idx + i;
// let weight = windowed_sinc(
// (input_x as f64 + 0.5 - center_x) * rate,
// (input_x as f64 + 0.5 - filter_start) / filter_len,
// );
// value_sum += weight * data[[height, input_x.clamp(0, ori_width - 1)]].as_();
// filter_sum += weight;
// }
// result[[height, width]] = V::from_f64(value_sum / filter_sum).unwrap();
// }
// });
// result
// }
// pub struct LevelData<T: Num + Clone + PartialEq + PartialOrd>(
// pub Quadtree<i64, RadarData2d<T, OwnedRepr<T>>>,
// );
// impl<T> LevelData<T>
// where
// T: Num + Clone + AsPrimitive<f64> + FromPrimitive + Debug + PartialEq + PartialOrd,
// {
// fn value(
// level_data: Vec<RadarData2d<T, OwnedRepr<T>>>,
// level_num: usize,
// ) -> Vec<RadarData2d<T, OwnedRepr<T>>> {
// if level_num == 0 {
// return level_data;
// }
// let mut result: Vec<RadarData2d<T, OwnedRepr<T>>> =
// Vec::with_capacity(level_data.len() * 4);
// let results = level_data
// .iter()
// .flat_map(|v| v.split().into_iter().map(|x| x.value_to_owned()));
// result.extend(results);
// return Self::value(result, level_num - 1);
// }
// fn new(data: &RadarData2d<T, OwnedRepr<T>>, level: usize, rate: f64) -> Self {
// // let rate = 1.0 / level as f64;
// let resampled = data.resample(rate, rate, 2.0).unwrap();
// let blocks = Self::value(vec![resampled], level);
// let mut tree: Quadtree<i64, RadarData2d<T, OwnedRepr<T>>> =
// quadtree_rs::Quadtree::new(level);
// blocks.into_iter().for_each(|block| {
// tree.insert(
// AreaBuilder::default()
// .anchor(quadtree_rs::point::Point {
// x: *block.dim1.first().unwrap() as i64,
// y: *block.dim2.first().unwrap() as i64,
// })
// .dimensions((block.dim1.len() as i64, block.dim2.len() as i64))
// .build()
// .unwrap(),
// block,
// );
// });
// Self(tree)
// }
// }
// pub fn levels<T>(data: Radar2d<T>, levels: usize) -> Vec<LevelData<T>>
// where
// T: Num + Clone + AsPrimitive<f64> + FromPrimitive + Debug,
// T: PartialEq + PartialOrd,
// {
// let numerator = 1.0 / levels as f64;
// (0..levels)
// .into_iter()
// .map(|level| LevelData::new(&data, level + 1, 1.0 - level as f64 * numerator))
// .collect()
// }
#[inline]
fn windowed_sinc(x: f64, y: f64) -> f64 {
let x = x * PI;
let sinc = if x != 0.0 { f64::sin(x) / x } else { 1.0 };
let window = if 0f64 <= y && y <= 1.0 {
1.0 - (y - 0.5).abs() * 2.0
} else {
0f64
};
sinc * window
}
pub enum DownSampleMeth {
STD,
MEAN,
VAR,
}
#[derive(Clone, Copy, Debug)]
pub enum CoordType {
Polar,
LatLon,
}

View File

@ -1,14 +1,15 @@
use radarg_plugin_interface::{CoordType, PluginResult, PluginResultType};
use crate::pipeline::element::ElementImpl; use crate::pipeline::element::ElementImpl;
use crate::pipeline::GridElementImpl; use crate::pipeline::GridElementImpl;
use crate::utils::*; use crate::utils::*;
use radarg_plugin_interface::{CoordType, DataShape, PluginResult, PluginResultType};
use std::sync::Arc;
macro_rules! data_to_grid { macro_rules! data_to_grid {
($_type:ident,$(($branch:path ,$boundary_norm: expr)),+) => { ($_type:ident,$(($branch:path ,$boundary_norm: expr)),+) => {
match $_type { match $_type {
$( $(
$branch => { $branch => {
let element_impl = GridElementImpl::new($boundary_norm); let element_impl = GridElementImpl::new($boundary_norm);
Box::new(element_impl) Arc::new(element_impl)
} }
),+ ),+
_ => panic!("Invalid type") _ => panic!("Invalid type")
@ -16,8 +17,8 @@ macro_rules! data_to_grid {
}; };
} }
pub fn plugin_result_impl(a: &PluginResult) -> Box<dyn ElementImpl> { pub fn plugin_result_impl(a: &PluginResult) -> Arc<dyn ElementImpl> {
let block= a.blocks.first().unwrap(); let block = a.blocks.first().unwrap();
match block.coord_type { match block.coord_type {
CoordType::Cartesian => { CoordType::Cartesian => {
let _type = block.data_type; let _type = block.data_type;
@ -41,4 +42,3 @@ pub fn plugin_result_impl(a: &PluginResult) -> Box<dyn ElementImpl> {
} }
} }
} }

View File

@ -17,7 +17,7 @@ pub enum PipelineError {
source: ProjError, source: ProjError,
}, },
#[error("data error")] #[error("data error")]
DataError(String) DataError(String),
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
@ -50,7 +50,6 @@ pub enum PluginError {
PluginError, PluginError,
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum PoolError { pub enum PoolError {
#[error("")] #[error("")]
@ -61,7 +60,6 @@ pub enum PoolError {
PoolInitialized(&'static str), PoolInitialized(&'static str),
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum RenderError { pub enum RenderError {
#[error("")] #[error("")]
@ -70,5 +68,4 @@ pub enum RenderError {
None, None,
#[error("Canceled")] #[error("Canceled")]
Canceled, Canceled,
} }

View File

@ -12,6 +12,7 @@ use gtk::{
use plugin_system::{init_plugin, PluginManager}; use plugin_system::{init_plugin, PluginManager};
use std::{ptr, sync::Mutex}; use std::{ptr, sync::Mutex};
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
mod actions;
mod chart; mod chart;
mod components; mod components;
mod config; mod config;
@ -20,17 +21,21 @@ mod data;
mod errors; mod errors;
mod pipeline; mod pipeline;
mod plugin_system; mod plugin_system;
use crate::components::app::AppMsg;
use components::app::AppModel; use components::app::AppModel;
use once_cell::{sync::Lazy as SafeLazy, unsync::Lazy as UnsafeLazy}; use once_cell::{sync::Lazy as SafeLazy, unsync::Lazy as UnsafeLazy};
use relm4::RelmApp;
use surfman::declare_surfman; use surfman::declare_surfman;
use tracing::info; use tracing::info;
use tracing_subscriber; use tracing_subscriber;
mod data_utils; mod data_utils;
mod map_tile; mod map_tile;
mod map_tile_utils; mod map_tile_utils;
mod predefined; mod predefined;
mod widgets; mod widgets;
#[cfg(target_env = "msvc")]
declare_surfman!(); declare_surfman!();
const APP_ID: &str = "org.tsuki.radar_g"; const APP_ID: &str = "org.tsuki.radar_g";
@ -57,16 +62,19 @@ fn main() {
.unwrap(); .unwrap();
epoxy::load_with(|name| { epoxy::load_with(|name| {
unsafe { library.get(name.as_bytes()) } unsafe { library.get::<_>(name.as_bytes()) }
.map(|symbol| *symbol) .map(|symbol| *symbol)
.unwrap_or(ptr::null()) .unwrap_or(ptr::null())
}); });
} }
let relm = relm4::RelmApp::new(APP_ID);
let app = adw::Application::builder().application_id(APP_ID).build();
let relm: RelmApp<AppMsg> = relm4::RelmApp::from_app(app.clone());
relm4_icons::initialize_icons(); relm4_icons::initialize_icons();
initialize_custom_css();
info!("Init plugin system"); info!("Init plugin system");
let pluginmanager = PluginManager::new(); let pluginmanager = PluginManager::new();
initialize_custom_css();
relm.run::<AppModel>(()); relm.run::<AppModel>(());
} }

View File

@ -144,7 +144,7 @@ impl MapTile {
(origin.lon() as f64..rb.lon() as f64).into(), (origin.lon() as f64..rb.lon() as f64).into(),
(origin.lat() as f64..rb.lat() as f64).into(), (origin.lat() as f64..rb.lat() as f64).into(),
); );
let result = Target::new(TargetType::Mem(result), 256.0, 256.0, bounds, None, None); let result = Target::new(TargetType::Mem(result), 256.0, 256.0, bounds, None);
let cache = cache.lock().unwrap(); let cache = cache.lock().unwrap();
cache.insert(tile, Arc::new(std::sync::Mutex::new(result))); cache.insert(tile, Arc::new(std::sync::Mutex::new(result)));
} }

View File

@ -1,9 +1,8 @@
pub fn lat_lon_to_zoom(lat_range: (f64, f64), lon_range: (f64, f64), w: f32, h: f32) -> u8 {
pub fn lat_lon_to_zoom(lat_range: (f64, f64), lon_range: (f64, f64), w: f32, h:f32 ) -> u8 {
let lat_diff = lat_range.1 - lat_range.0; let lat_diff = lat_range.1 - lat_range.0;
let lon_diff = lon_range.1 - lon_range.0; let lon_diff = lon_range.1 - lon_range.0;
let z1 = ( (360.0 * w) as f64 / lon_diff / 256.0).log2(); let z1 = ((360.0 * w) as f64 / lon_diff / 256.0).log2();
let z2 = ( (180.0 * h) as f64 / lat_diff / 256.0).log2(); let z2 = ((180.0 * h) as f64 / lat_diff / 256.0).log2();
let z = z1.min(z2); let z = z1.min(z2);
z as u8 z as u8
} }

View File

@ -1,36 +1,32 @@
use super::{offscreen_renderer::CanvasWrapper, Dispatcher, Pipeline}; use super::{offscreen_renderer::CanvasWrapper, Dispatcher, Pipeline};
use crate::components::app::AppCommand; use crate::components::Widget;
use crate::components::ControlPanelInputMsg;
use crate::coords::cms::CMS; use crate::coords::cms::CMS;
use crate::coords::Range;
use crate::data::MetaInfo; use crate::data::MetaInfo;
use crate::errors::{PipelineError, RenderError}; use crate::errors::{PipelineError, RenderError};
use crate::predefined::color_mapper::ColorMapper;
use crate::widgets::Render; use crate::widgets::Render;
use crate::RUNTIME; use crate::RUNTIME;
use crate::{coords::Range, widgets::widget::Widget};
use chrono::{DateTime, TimeZone, Utc}; use chrono::{DateTime, TimeZone, Utc};
use core_extensions::SelfOps; use femtovg::{ImageFlags, ImageId};
use femtovg::rgb::alt::GRAY8;
use femtovg::{renderer::OpenGl, Canvas, ImageFlags, ImageId, ImageInfo, PixelFormat};
use futures::StreamExt; use futures::StreamExt;
use num_traits::{AsPrimitive, FromPrimitive, NumOps};
use radarg_plugin_interface::PluginResult; use radarg_plugin_interface::PluginResult;
use std::any::Any; use std::any::Any;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::fmt::Formatter; use std::fmt::Formatter;
use std::ops::{Deref, DerefMut};
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::{ use std::{
cell::{Ref, RefCell},
fmt::Debug, fmt::Debug,
future::Future,
pin::Pin,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use tokio::sync::{ use tokio::sync::{
oneshot::{channel, Receiver, Sender}, oneshot::{channel, Receiver},
Notify, Notify,
}; };
use tracing::Instrument;
pub type ElementID = usize; pub type ElementID = usize;
static ELEMENT_ID: AtomicUsize = AtomicUsize::new(0); static ELEMENT_ID: AtomicUsize = AtomicUsize::new(0);
@ -48,7 +44,7 @@ pub enum Element {
impl Element { impl Element {
pub fn create_time_series( pub fn create_time_series(
imp: Arc<Box<dyn ElementImpl>>, imp: Arc<dyn ElementImpl>,
dispatcher: Rc<Dispatcher>, dispatcher: Rc<Dispatcher>,
key: String, key: String,
cms: CMS, cms: CMS,
@ -95,7 +91,7 @@ pub struct TimeSeriesElement {
pub id: ElementID, pub id: ElementID,
pub key: String, pub key: String,
cms: CMS, cms: CMS,
imp: Arc<Box<dyn ElementImpl>>, imp: Arc<dyn ElementImpl>,
registers: Arc<Mutex<HashMap<DateTime<Utc>, Vec<Arc<Notify>>>>>, registers: Arc<Mutex<HashMap<DateTime<Utc>, Vec<Arc<Notify>>>>>,
pipeline: Pipeline, pipeline: Pipeline,
pub buffer: Buffer, pub buffer: Buffer,
@ -105,7 +101,7 @@ pub struct TimeSeriesElement {
#[derive(Clone)] #[derive(Clone)]
pub enum InstantElementDrawerType { pub enum InstantElementDrawerType {
Draw(DrawFunc), Draw(DrawFunc),
Prepared((Target, Arc<Box<dyn ElementImpl>>)), Prepared((DataTarget, Arc<dyn ElementImpl>)),
} }
impl Debug for InstantElementDrawerType { impl Debug for InstantElementDrawerType {
@ -121,6 +117,11 @@ pub struct InstantElement {
dispatcher: Rc<Dispatcher>, dispatcher: Rc<Dispatcher>,
} }
pub enum ElementImplMap {
ColorMap,
None,
}
pub trait ElementImpl: Debug + Send + Sync + 'static { pub trait ElementImpl: Debug + Send + Sync + 'static {
fn render(&self, data: &PluginResult, canvas: &mut CanvasWrapper, cms: &mut CMS) -> Target; fn render(&self, data: &PluginResult, canvas: &mut CanvasWrapper, cms: &mut CMS) -> Target;
} }
@ -141,51 +142,24 @@ impl InstantElement {
func(render); func(render);
} }
InstantElementDrawerType::Prepared((ref mut target, _)) => { InstantElementDrawerType::Prepared((ref mut target, _)) => {
let mut canvas = render.get_canvas(); render.draw_img(target);
let mut canvas = canvas.as_mut().unwrap();
let (ox, oy) = target.origin(render);
let (x, y) = target.size(render);
let result_id = match target.target {
TargetType::ImageId(id) => id,
TargetType::Mem(ref mem) => {
let gl_bind = render.get_context();
let gl = gl_bind.as_ref().unwrap();
let flags = ImageFlags::empty();
let texture = target.mem_to_native_texture(gl, flags);
let converted = canvas
.create_image_from_native_texture(
texture,
ImageInfo::new(flags, 3000, 3000, PixelFormat::Rgba8),
)
.unwrap();
target.set_target(TargetType::ImageId(converted));
converted
}
};
let paint = femtovg::Paint::image(result_id, ox, oy, x, y, 0.0, 1.0);
let mut path = femtovg::Path::new();
path.rect(ox, oy, x, y);
canvas.fill_path(&path, &paint);
} }
} }
} }
pub fn imp(&self) -> &InstantElementDrawerType {
&self.draw_type
}
pub fn to_time_series( pub fn to_time_series(
self, self,
dispatcher: Rc<Dispatcher>, dispatcher: Rc<Dispatcher>,
cms: CMS, cms: CMS,
) -> (TimeSeriesElement, DateTime<Utc>) { ) -> (TimeSeriesElement, DateTime<Utc>) {
// let imp = Arc::new(InstantElementImpl::new(self)); // let imp = Arc::new(InstantElementImpl::new(self));
if let InstantElementDrawerType::Prepared((target, imp)) = self.draw_type { if let InstantElementDrawerType::Prepared((mut target, imp)) = self.draw_type {
let mut time_series = TimeSeriesElement::new(imp, dispatcher, cms, self.key); let mut time_series = TimeSeriesElement::new(imp, dispatcher, cms, self.key);
let data = target let data = target.take_data().unwrap();
.data
.clone()
.unwrap()
.downcast::<PluginResult>()
.unwrap();
let time_stamp = data.blocks.first().unwrap().datetime; let time_stamp = data.blocks.first().unwrap().datetime;
let meta_info: MetaInfo = data.meta.clone().into(); let meta_info: MetaInfo = data.meta.clone().into();
use chrono::prelude::*; use chrono::prelude::*;
@ -202,12 +176,7 @@ impl InstantElement {
} }
impl TimeSeriesElement { impl TimeSeriesElement {
fn new( fn new(imp: Arc<dyn ElementImpl>, dispatcher: Rc<Dispatcher>, cms: CMS, key: String) -> Self {
imp: Arc<Box<dyn ElementImpl>>,
dispatcher: Rc<Dispatcher>,
cms: CMS,
key: String,
) -> Self {
let id = ELEMENT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let id = ELEMENT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let mut pipeline = Pipeline::new(20, key.clone()); let mut pipeline = Pipeline::new(20, key.clone());
pipeline.set_dispatcher(dispatcher.clone()); pipeline.set_dispatcher(dispatcher.clone());
@ -266,6 +235,10 @@ impl TimeSeriesElement {
self.cms = cms; self.cms = cms;
} }
pub fn imp(&self) -> &Arc<dyn ElementImpl> {
&self.imp
}
fn register_noti(&self, datetime: DateTime<Utc>, noti: Arc<Notify>) { fn register_noti(&self, datetime: DateTime<Utc>, noti: Arc<Notify>) {
self.registers self.registers
.lock() .lock()
@ -329,12 +302,12 @@ impl TimeSeriesElement {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RenderResult { pub struct RenderResult {
target: Target, target: DataTarget,
meta_info: MetaInfo, meta_info: MetaInfo,
} }
impl RenderResult { impl RenderResult {
pub fn new(target: Target, meta_info: MetaInfo) -> Self { pub fn new(target: DataTarget, meta_info: MetaInfo) -> Self {
Self { target, meta_info } Self { target, meta_info }
} }
@ -354,13 +327,14 @@ pub struct Target {
pub width: f32, pub width: f32,
pub height: f32, pub height: f32,
pub bounds: (Range, Range), pub bounds: (Range, Range),
pub data: Option<Arc<dyn Any + Send + Sync + 'static>>, // pub data: Option<Arc<dyn Any + Send + Sync + 'static>>,
} }
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum TargetType { pub enum TargetType {
ImageId(ImageId), ImageId(ImageId),
Mem(Vec<u8>), Mem(Vec<u8>),
NativeBuffer(Vec<u8>),
} }
impl Target { impl Target {
@ -370,7 +344,7 @@ impl Target {
height: f32, height: f32,
bounds: (Range, Range), bounds: (Range, Range),
thumbnail: Option<gtk::gdk::Texture>, thumbnail: Option<gtk::gdk::Texture>,
data: Option<Arc<dyn Any + Send + Sync + 'static>>, // data: Option<Arc<dyn Any + Send + Sync + 'static>>,
) -> Self { ) -> Self {
Self { Self {
target, target,
@ -378,7 +352,7 @@ impl Target {
height, height,
bounds, bounds,
thumbnail, thumbnail,
data, // data,
} }
} }
@ -394,12 +368,12 @@ impl Target {
((x2 - x1).abs(), (y2 - y1).abs()) ((x2 - x1).abs(), (y2 - y1).abs())
} }
pub fn mem_to_native_texture( pub fn native_buffer_to_native_texture(
&self, &self,
gl: &glow::Context, gl: &glow::Context,
flags: ImageFlags, flags: ImageFlags,
) -> glow::NativeTexture { ) -> glow::NativeTexture {
if let TargetType::Mem(ref mem) = self.target { if let TargetType::NativeBuffer(ref mem) = self.target {
use glow::*; use glow::*;
let texture = unsafe { let texture = unsafe {
let id = gl.create_texture().unwrap(); let id = gl.create_texture().unwrap();
@ -549,3 +523,41 @@ impl Target {
self.target = target; self.target = target;
} }
} }
#[derive(Debug, Clone)]
pub struct DataTarget {
data: Option<PluginResult>,
target: Target,
}
impl DataTarget {
pub fn new(data: Option<PluginResult>, target: Target) -> Self {
Self { data, target }
}
pub fn take_data(&mut self) -> Option<PluginResult> {
self.data.take()
}
pub fn data(&self) -> Option<&PluginResult> {
self.data.as_ref()
}
pub fn mut_data(&mut self) -> Option<&mut PluginResult> {
self.data.as_mut()
}
}
impl Deref for DataTarget {
type Target = Target;
fn deref(&self) -> &Self::Target {
&self.target
}
}
impl DerefMut for DataTarget {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.target
}
}

View File

@ -1,14 +1,15 @@
use super::predefined::GridFieldRenderer; use super::predefined::GridFieldRenderer;
use super::renders::DataRenderer; use super::renders::DataRenderer;
use crate::data::Radar2d; use crate::components::Widget;
use crate::pipeline::element::{ElementImpl, Target}; use crate::data::{Radar2d, Radar3d};
use crate::pipeline::element::{ElementImpl, ElementImplMap, Target};
use crate::pipeline::offscreen_renderer::CanvasWrapper; use crate::pipeline::offscreen_renderer::CanvasWrapper;
use crate::widgets::predefined::color_mapper::ColorMapper; use crate::predefined::color_mapper::ColorMapper;
use crate::widgets::{LayerImpl}; use crate::widgets::LayerImpl;
use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps}; use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps};
use radarg_plugin_interface::{DataShape, PluginResult};
use std::any::Any; use std::any::Any;
use std::fmt::Debug; use std::fmt::Debug;
use radarg_plugin_interface::PluginResult;
#[derive(Debug)] #[derive(Debug)]
pub struct GridElementImpl<CMAP, T> pub struct GridElementImpl<CMAP, T>
@ -52,8 +53,22 @@ where
cms: &mut crate::coords::cms::CMS, cms: &mut crate::coords::cms::CMS,
) -> crate::pipeline::element::Target { ) -> crate::pipeline::element::Target {
let first_block = data.blocks.first().unwrap(); let first_block = data.blocks.first().unwrap();
let data = first_block.clone().into(); match first_block.shape {
let result = self.renderer.render(canvas, cms, &data, (3000.0, 3000.0)); DataShape::Vector => {
result panic!("Vector data is not supported")
}
DataShape::Matrix => {
let data:Radar2d<T> = first_block.clone().into();
let data = data.as_ref();
let result = self.renderer.render(canvas, cms, &data, (3000.0, 3000.0));
result
}
DataShape::Cube => {
let data: Radar3d<T> = first_block.clone().into();
let data = data.index_axis(ndarray::Axis(0), 0);
let result = self.renderer.render(canvas, cms, &data, (3000.0, 3000.0));
result
}
}
} }
} }

View File

@ -8,7 +8,7 @@ mod renders;
pub mod utils; pub mod utils;
pub use dispatcher::Dispatcher; pub use dispatcher::Dispatcher;
pub use element::RenderResult; pub use element::*;
pub use element_impl::*;
pub use new_pipeline::Pipeline; pub use new_pipeline::Pipeline;
pub use offscreen_renderer::OffscreenRenderer; pub use offscreen_renderer::OffscreenRenderer;
pub use element_impl::*;

View File

@ -1,6 +1,6 @@
use super::{ use super::{
dispatcher::Dispatcher, dispatcher::Dispatcher,
element::RenderResult, element::{DataTarget, RenderResult},
offscreen_renderer::{CanvasWrapper, OffscreenRenderer}, offscreen_renderer::{CanvasWrapper, OffscreenRenderer},
utils::data_to_element, utils::data_to_element,
}; };
@ -16,6 +16,7 @@ use crate::{
use chrono::prelude::*; use chrono::prelude::*;
use femtovg::{renderer::OpenGl, Canvas, ImageId}; use femtovg::{renderer::OpenGl, Canvas, ImageId};
use futures::{future::BoxFuture, Future}; use futures::{future::BoxFuture, Future};
use radarg_plugin_interface::PluginResult;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::{ use std::{
@ -23,7 +24,6 @@ use std::{
rc::Rc, rc::Rc,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use radarg_plugin_interface::PluginResult;
use tokio::{ use tokio::{
sync::{mpsc, oneshot}, sync::{mpsc, oneshot},
task, task,
@ -84,7 +84,7 @@ impl Pipeline {
current_time: DateTime<Utc>, current_time: DateTime<Utc>,
check_existed: bool, check_existed: bool,
max_retry_time: usize, max_retry_time: usize,
task: Arc<dyn Fn(&PluginResult ,&mut CanvasWrapper, &mut CMS) -> Target + Send + Sync>, task: Arc<dyn Fn(&PluginResult, &mut CanvasWrapper, &mut CMS) -> Target + Send + Sync>,
cms: CMS, cms: CMS,
) -> Option<Vec<DateTime<Utc>>> { ) -> Option<Vec<DateTime<Utc>>> {
let paths = { let paths = {
@ -123,7 +123,7 @@ impl Pipeline {
fn worker( fn worker(
&self, &self,
datetime: DateTime<Utc>, datetime: DateTime<Utc>,
task: Arc<dyn Fn(&PluginResult ,&mut CanvasWrapper, &mut CMS) -> Target + Send + Sync>, task: Arc<dyn Fn(&PluginResult, &mut CanvasWrapper, &mut CMS) -> Target + Send + Sync>,
mut cms: CMS, mut cms: CMS,
path: impl AsRef<str> + Send + 'static, path: impl AsRef<str> + Send + 'static,
) -> BoxFuture<'static, RenderR> { ) -> BoxFuture<'static, RenderR> {
@ -135,8 +135,8 @@ impl Pipeline {
let handle = task::spawn_blocking(move || { let handle = task::spawn_blocking(move || {
let mut offscreen_renderer = OffscreenRenderer::new(3000, 3000).unwrap(); let mut offscreen_renderer = OffscreenRenderer::new(3000, 3000).unwrap();
let mut canvas_wrapper = offscreen_renderer.create_canvas(); let mut canvas_wrapper = offscreen_renderer.create_canvas();
let target = task(&loaded_data,&mut canvas_wrapper, &mut cms); let target = task(&loaded_data, &mut canvas_wrapper, &mut cms);
target DataTarget::new(Some(loaded_data), target)
}); });
let target = handle.await.unwrap(); let target = handle.await.unwrap();

View File

@ -1,7 +1,8 @@
use super::super::renders::DataRenderer; use super::super::renders::DataRenderer;
use crate::coords::cms::CMS; use crate::coords::cms::CMS;
use crate::data::{Radar2dRef, RadarData2d};
use crate::pipeline::element::{Target, TargetType}; use crate::pipeline::element::{Target, TargetType};
use crate::widgets::predefined::color_mapper::ColorMapper; use crate::predefined::color_mapper::ColorMapper;
use crate::{data::Radar2d, utils::meshgrid}; use crate::{data::Radar2d, utils::meshgrid};
use femtovg::ImageInfo; use femtovg::ImageInfo;
use femtovg::{ use femtovg::{
@ -9,11 +10,11 @@ use femtovg::{
}; };
use geo_types::LineString; use geo_types::LineString;
use gl::types::GLvoid; use gl::types::GLvoid;
use gtk::ResponseType::No;
use image::{imageops::resize, ImageBuffer, Rgba}; use image::{imageops::resize, ImageBuffer, Rgba};
use ndarray::ArrayView2; use ndarray::ArrayView2;
use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps}; use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps};
use std::{fmt::Debug, io::Cursor, marker::PhantomData}; use std::{fmt::Debug, io::Cursor, marker::PhantomData};
use gtk::ResponseType::No;
use tracing::info; use tracing::info;
#[derive(Debug)] #[derive(Debug)]
@ -22,7 +23,7 @@ where
T: NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64>, T: NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64>,
CMAP: ColorMapper<T>, CMAP: ColorMapper<T>,
{ {
cmap: CMAP, pub cmap: CMAP,
value_phantom: PhantomData<T>, value_phantom: PhantomData<T>,
} }
@ -61,15 +62,6 @@ impl<T: NumOps + PartialOrd + Copy + FromPrimitive + AsPrimitive<f64>, CMAP: Col
let rt_lat = dim2[[r + 1, c + 1]]; let rt_lat = dim2[[r + 1, c + 1]];
let rt_lon = dim1[[r + 1, c + 1]]; let rt_lon = dim1[[r + 1, c + 1]];
// let cell: LineString = vec![
// (lb_lon, lb_lat),
// (rt_lon + 0.001, lb_lat),
// (rt_lon + 0.001, rt_lat),
// (lb_lon, rt_lat + 0.001),
// (lb_lon, lb_lat + 0.001),
// ]
// .into();
let v = &data[[r, c]]; let v = &data[[r, c]];
let mapped_color = self.cmap.map_value_to_color(*v, fill_value); let mapped_color = self.cmap.map_value_to_color(*v, fill_value);
@ -77,32 +69,23 @@ impl<T: NumOps + PartialOrd + Copy + FromPrimitive + AsPrimitive<f64>, CMAP: Col
continue; continue;
} }
// let mapped_ring = cms.ring_map(&cell).unwrap(); let (ox, oy) = cms.map((lb_lon, lb_lat)).unwrap();
let (ox,oy) = cms.map((lb_lon, lb_lat)).unwrap();
let (rx, ry) = cms.map((rt_lon, rt_lat)).unwrap(); let (rx, ry) = cms.map((rt_lon, rt_lat)).unwrap();
let mut path = Path::new(); let mut path = Path::new();
// let mut points = mapped_ring.points();
// let first_point = points.next().unwrap();
// path.move_to(first_point.x(), first_point.y());
//
// for point in points {
// path.line_to(point.x(), point.y());
// }
path.rect(ox, oy, (rx - ox) * 1.5, (ry - oy) * 1.5); path.rect(ox, oy, (rx - ox) * 1.5, (ry - oy) * 1.5);
// path.close();
canvas.fill_path(&path, &Paint::color(mapped_color.unwrap())); canvas.fill_path(&path, &Paint::color(mapped_color.unwrap()));
} }
} }
} }
} }
impl<T, CMAP> DataRenderer for GridFieldRenderer<CMAP, T> impl<'a, T, CMAP> DataRenderer<'a> for GridFieldRenderer<CMAP, T>
where where
T: Num + NumOps + PartialOrd + Copy + Clone + FromPrimitive + AsPrimitive<f64>, T: Num + NumOps + PartialOrd + Copy + Clone + FromPrimitive + AsPrimitive<f64>,
CMAP: ColorMapper<T>, CMAP: ColorMapper<T>,
{ {
type Data = Radar2d<T>; type Data = Radar2dRef<'a, T>;
fn render( fn render(
&self, &self,
@ -156,25 +139,6 @@ where
debug_assert_eq!(gl::GetError(), gl::NO_ERROR); debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
} }
info!("Time to read pixels: {:?}", start.elapsed()); info!("Time to read pixels: {:?}", start.elapsed());
// let img: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::from_raw(w as u32, h as u32, pixels)
// .expect("Failed to create ImageBuffer");
// let thumbnail = resize(&img, 500, 500, image::imageops::FilterType::Lanczos3);
// let mut thumb_buffer = Cursor::new(Vec::new());
// img.write_to(&mut thumb_buffer, image::ImageOutputFormat::Png)
// .expect("Failed to write PNG buffer");
// info!("Time to write PNG: {:?}", start.elapsed());
// let thumb_data = thumb_buffer.into_inner();
// let thumbnail_tex =
// gtk::gdk::Texture::from_bytes(&gtk::glib::Bytes::from(&thumb_data)).unwrap();
// 将 ImageBuffer 编码为 PNG
// let mut png_buffer = Cursor::new(Vec::new());
// img.write_to(&mut png_buffer, image::ImageOutputFormat::Bmp)
// .expect("Failed to write PNG buffer");
// let png_data = png_buffer.into_inner();
let d1_start = (data.dim1.view()).first().unwrap().clone(); let d1_start = (data.dim1.view()).first().unwrap().clone();
let d1_end = (data.dim1.view()).last().unwrap().clone(); let d1_end = (data.dim1.view()).last().unwrap().clone();
@ -183,13 +147,11 @@ where
canvas.set_render_target(RenderTarget::Screen); canvas.set_render_target(RenderTarget::Screen);
Target::new( Target::new(
TargetType::Mem(pixels), TargetType::NativeBuffer(pixels),
w, w,
h, h,
((d1_start, d1_end).into(), (d2_start, d2_end).into()), ((d1_start, d1_end).into(), (d2_start, d2_end).into()),
// Some(thumbnail_tex),
None, None,
None
) )
} }
} }

View File

@ -2,7 +2,7 @@ use crate::coords::cms::CMS;
use crate::pipeline::element::Target; use crate::pipeline::element::Target;
use femtovg::{renderer::OpenGl, Canvas}; use femtovg::{renderer::OpenGl, Canvas};
pub trait DataRenderer { pub trait DataRenderer<'a> {
type Data; type Data;
fn render( fn render(
&self, &self,

View File

@ -1,6 +1,5 @@
use crate::{ use crate::{
coords::cms::CMS, data::Radar2d, errors::RenderError, coords::cms::CMS, data::Radar2d, errors::RenderError, predefined::color_mapper::BoundaryNorm,
widgets::predefined::color_mapper::BoundaryNorm,
}; };
use chrono::{prelude::*, Duration}; use chrono::{prelude::*, Duration};
use radarg_plugin_interface::*; use radarg_plugin_interface::*;
@ -25,7 +24,7 @@ macro_rules! match_in_macro {
match $block.data_type { match $block.data_type {
$( $(
$branch => { $branch => {
let element = Element::create_time_series(Arc::new(Box::new(GridElementImpl::new($color))), $dispatcher, $name.to_string(), $cms); let element = Element::create_time_series(Arc::new(GridElementImpl::new($color)), $dispatcher, $name.to_string(), $cms);
Some(element) Some(element)
}, },
)+ )+
@ -36,11 +35,7 @@ macro_rules! match_in_macro {
}; };
} }
pub fn data_to_element( pub fn data_to_element(block: &Block, dispatcher: Rc<Dispatcher>, cms: CMS) -> Option<Element> {
block: &Block,
dispatcher: Rc<Dispatcher>,
cms: CMS,
) -> Option<Element> {
use crate::utils::*; use crate::utils::*;
use radarg_plugin_interface::PluginResultType; use radarg_plugin_interface::PluginResultType;
match block.shape { match block.shape {

View File

@ -1,8 +1,42 @@
use crate::data::{CoordType, Radar2d}; use crate::data::{CoordType, Radar2d, Radar3d};
use ndarray::{Array1, Array2, Array3, ShapeBuilder}; use ndarray::{Array1, Array2, Array3, ShapeBuilder};
use num_traits::{AsPrimitive, FromPrimitive, Num}; use num_traits::{AsPrimitive, FromPrimitive, Num};
use radarg_plugin_interface::{Block, DataShape, PluginResult, VecResult}; use radarg_plugin_interface::{Block, DataShape, PluginResult, VecResult};
macro_rules! match_in_macro_3d {
// ($(($branch:path, $t:ty)),+, $block:ident)
($block:tt, $(($branch:path, $t:path)),+) => {
{
let data = $block.data;
let dim3: Vec<_> = $block.dimension_values.remove(0).into();
let dim2: Vec<_> = $block.dimension_values.remove(0).into();
let dim1: Vec<_> = $block.dimension_values.remove(0).into();
let fill_value = $block.fill_value;
let data_shape: Vec<usize> = $block.size.into();
let coord_type = match $block.coord_type{
radarg_plugin_interface::CoordType::Cartesian => CoordType::LatLon,
radarg_plugin_interface::CoordType::Polar => CoordType::Polar,
_ => panic!("Unsupported coord type")
};
match data {
$(
$branch(x) => {
let shape = [data_shape[0], data_shape[1], data_shape[2]];
Radar3d {
fill_value: T::from_f64(fill_value).unwrap(),
data: Array3::from_shape_vec(shape, x.into_iter().map(|x| $t(x).unwrap()).collect::<Vec<T>>()).unwrap(),
dim1: Array1::from_vec(dim1),
dim2: Array1::from_vec(dim2),
dim3: Array1::from_vec(dim3),
coord_type: coord_type,
}},
)+
_ => panic!("Unsupported data type"),
}
}
};
}
macro_rules! match_in_macro { macro_rules! match_in_macro {
// ($(($branch:path, $t:ty)),+, $block:ident) // ($(($branch:path, $t:ty)),+, $block:ident)
($block:tt, $(($branch:path, $t:path)),+) => { ($block:tt, $(($branch:path, $t:path)),+) => {
@ -61,3 +95,31 @@ where
} }
} }
} }
impl<T> From<Block> for Radar3d<T>
where
T: Num + Copy + PartialOrd + PartialEq,
T: FromPrimitive,
{
fn from(block: Block) -> Self {
let mut block = block;
let a = &block.dimension_values;
println!("{:?}", a);
if let DataShape::Cube = block.shape {
let result = match_in_macro_3d!(
block,
(VecResult::I8, T::from_i8),
(VecResult::I32, T::from_i32),
(VecResult::I64, T::from_i64),
(VecResult::U8, T::from_u8),
(VecResult::U32, T::from_u32),
(VecResult::U64, T::from_u64),
(VecResult::F32, T::from_f32),
(VecResult::F64, T::from_f64)
);
result
} else {
panic!("Expected matrix data shape");
}
}
}

View File

@ -1,16 +1,21 @@
use crate::map_tile_utils::*; use crate::map_tile_utils::*;
use slippy_map_tiles::{BBox, merc_location_to_tile_coords, size_bbox_zoom_metatiles, Tile}; use slippy_map_tiles::{merc_location_to_tile_coords, size_bbox_zoom_metatiles, BBox, Tile};
pub fn map_tile_layer(lat_range: (f64, f64), lon_range: (f64, f64), w: f32, h: f32) { pub fn map_tile_layer(lat_range: (f64, f64), lon_range: (f64, f64), w: f32, h: f32) {
let z = lat_lon_to_zoom(lat_range, lon_range, w, h); let z = lat_lon_to_zoom(lat_range, lon_range, w, h);
let bbox = BBox::new(lat_range.0 as f32, lon_range.0 as f32, lat_range.1 as f32, lon_range.1 as f32).unwrap(); let bbox = BBox::new(
lat_range.0 as f32,
lon_range.0 as f32,
lat_range.1 as f32,
lon_range.1 as f32,
)
.unwrap();
let size = size_bbox_zoom_metatiles(&bbox, z, 4); let size = size_bbox_zoom_metatiles(&bbox, z, 4);
let t = Tile::new(10, 547, 380).unwrap(); let t = Tile::new(10, 547, 380).unwrap();
println!("size: {:?}", size); println!("size: {:?}", size);
println!("tile: {:?}", t); println!("tile: {:?}", t);
} }
mod test { mod test {
use super::*; use super::*;
#[test] #[test]

View File

@ -1 +1,5 @@
pub mod map_tile; pub mod map_tile;
// pub mod grid_field_renderer;
// pub mod layers;
pub mod color_mapper;
pub mod widgets;

100
src/predefined/widgets.rs Normal file
View File

@ -0,0 +1,100 @@
use crate::components::widget::{Widget, WidgetType};
use crate::predefined::color_mapper::ColorMapper;
use crate::widgets::{AssoElement, Layer};
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
use num_traits::*;
use std::any::Any;
use topojson::Arc;
#[derive(Debug)]
pub struct ColorBar {
padding: [f32; 4],
width: f32,
height: f32,
margin: [i32; 4],
}
impl ColorBar {
pub fn new(padding: [f32; 4], size: (f32, f32), margin: [i32; 4]) -> Self {
Self {
padding,
width: size.0,
height: size.1,
margin,
}
}
}
impl Widget for ColorBar {
fn cairo_render(&self, layers: &Vec<Layer>, canvas: &gtk::cairo::Context, w: f32, h: f32) {
let bar_width = 10;
let bar_height = h - self.padding[0] - self.padding[2];
let (x, y) = (self.padding[3], self.padding[0]);
let length = layers.len();
if length == 0 {
return;
}
let first = layers.first().unwrap();
match first.get_associated_element() {
AssoElement::Instant(i) => {
let imp = i.imp();
}
AssoElement::TimeSeries(t) => {
let c = t.lock().unwrap();
let imp = c.imp();
}
_ => {}
}
// for ((i, color), label) in self
// .color_list
// .iter()
// .enumerate()
// .zip(self.color_mapper.labels())
// {
// let y = y + i as f32 * b_h;
//
// canvas.set_source_rgba(
// color.r as f64,
// color.g as f64,
// color.b as f64,
// color.a as f64,
// );
// canvas.rectangle(x as f64, y as f64, bar_width as f64, b_h as f64);
// canvas.fill();
//
// let extents = canvas.text_extents(&label).unwrap();
//
// canvas.move_to(
// (x + bar_width as f32 + 5.0) as f64,
// y as f64 + extents.height() / 2.0 as f64,
// );
// canvas.set_source_rgba(1.0, 1.0, 1.0, 1.0);
// canvas.show_text(&label);
// }
}
fn location(&self) -> (gtk::Align, gtk::Align) {
(gtk::Align::End, gtk::Align::End)
}
fn size(&self) -> (f32, f32) {
(self.width, self.height)
}
fn widget_type(&self) -> WidgetType {
WidgetType::Cairo
}
fn margin(&self) -> [i32; 4] {
self.margin
}
fn padding(&self) -> [i32; 4] {
[10, 10, 10, 10]
}
}

View File

@ -11,7 +11,7 @@ use surfman::{
GLVersion, NativeConnection, NativeContext, SurfaceAccess, SurfaceType, GLVersion, NativeConnection, NativeContext, SurfaceAccess, SurfaceType,
}; };
use crate::widgets::render::predefined::color_mapper::BoundaryNorm; use crate::predefined::color_mapper::BoundaryNorm;
use crate::RUNTIME; use crate::RUNTIME;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -31,47 +31,6 @@ where
(xx, yy) (xx, yy)
} }
pub fn creator() {
let connection = Connection::new().unwrap();
let adapter = connection.create_adapter().unwrap();
let mut device = connection.create_device(&adapter).unwrap();
let api = device.gl_api();
let descriptor = device
.create_context_descriptor(&ContextAttributes {
version: GLVersion::new(3, 3),
flags: ContextAttributeFlags::empty(),
})
.unwrap();
let mut context = device.create_context(&descriptor, None).unwrap();
let mut surface = device
.create_surface(
&context,
SurfaceAccess::GPUOnly,
SurfaceType::Generic {
size: Size2D::new(500, 500),
},
)
.unwrap();
let surface_info = device.surface_info(&surface);
device
.bind_surface_to_context(&mut context, surface)
.expect("Failed to bind surface to context");
device.make_context_current(&context).unwrap();
use femtovg::renderer::OpenGl;
static LOAD_FN: fn(&str) -> *const std::ffi::c_void = |s| epoxy::get_proc_addr(s) as *const _;
let (mut renderer, fbo) = unsafe {
let renderer = OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
let fbo =
glow::NativeFramebuffer(NonZeroU32::new(surface_info.framebuffer_object).unwrap());
(renderer, fbo)
};
renderer.set_screen_target(Some(fbo));
device.destroy_context(&mut context).unwrap();
}
pub fn create_dbz_boundarynorm() -> BoundaryNorm<i8> { pub fn create_dbz_boundarynorm() -> BoundaryNorm<i8> {
BoundaryNorm::new( BoundaryNorm::new(
vec![ vec![

View File

@ -1,6 +1,6 @@
use std::ops::Range;
use geo_types::LineString;
use crate::coords::Mapper; use crate::coords::Mapper;
use geo_types::LineString;
use std::ops::Range;
pub struct CMS { pub struct CMS {
mapper: Mapper, mapper: Mapper,

View File

@ -4,8 +4,7 @@ use gtk::subclass::prelude::*;
use std::cell::RefCell; use std::cell::RefCell;
#[derive(Default)] #[derive(Default)]
pub struct ExteriorWidget { pub struct ExteriorWidget {}
}
#[glib::object_subclass] #[glib::object_subclass]
impl ObjectSubclass for ExteriorWidget { impl ObjectSubclass for ExteriorWidget {
@ -13,4 +12,4 @@ impl ObjectSubclass for ExteriorWidget {
type Type = super::ExteriorWidget; type Type = super::ExteriorWidget;
} }
impl ObjectImpl for ExteriorWidget{} impl ObjectImpl for ExteriorWidget {}

View File

@ -98,7 +98,7 @@ impl ObjectImpl for Render {
fn constructed(&self) { fn constructed(&self) {
self.parent_constructed(); self.parent_constructed();
let area = self.obj(); let area = self.obj();
// area.set_has_stencil_buffer(true); area.set_has_stencil_buffer(true);
} }
fn properties() -> &'static [glib::ParamSpec] { fn properties() -> &'static [glib::ParamSpec] {
@ -114,7 +114,16 @@ impl ObjectImpl for Render {
} }
} }
impl WidgetImpl for Render {} impl WidgetImpl for Render {
fn realize(&self) {
self.parent_realize();
}
fn unrealize(&self) {
self.obj().make_current();
self.canvas.replace(None);
self.parent_unrealize();
}
}
impl GLAreaImpl for Render { impl GLAreaImpl for Render {
fn resize(&self, width: i32, height: i32) { fn resize(&self, width: i32, height: i32) {
@ -330,6 +339,20 @@ impl Render {
target.set_target(TargetType::ImageId(converted)); target.set_target(TargetType::ImageId(converted));
converted converted
} }
TargetType::NativeBuffer(ref mem) => {
let gl_bind = self.glow_context.borrow();
let gl = gl_bind.as_ref().unwrap();
let flags = femtovg::ImageFlags::empty();
let texture = target.native_buffer_to_native_texture(gl, flags);
let converted = canvas
.create_image_from_native_texture(
texture,
femtovg::ImageInfo::new(flags, 3000, 3000, femtovg::PixelFormat::Rgba8),
)
.unwrap();
target.set_target(TargetType::ImageId(converted));
converted
}
}; };
let painter = Paint::image(id, ox, oy, x, y, 0.0, 1.0); let painter = Paint::image(id, ox, oy, x, y, 0.0, 1.0);

View File

@ -1,9 +1,9 @@
use super::super::WindowCoord; use super::super::WindowCoord;
use super::layers::Layer;
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 std::sync::{Arc, Mutex};
use super::layers::Layer;
#[derive(Default)] #[derive(Default)]
pub struct InteriorWidget { pub struct InteriorWidget {

View File

@ -1,19 +1,14 @@
use super::super::Render; use super::super::Render;
use crate::coords::cms::CMS; use crate::coords::cms::CMS;
use crate::pipeline::element::{self, Element, ElementID, Target, TargetType}; use crate::errors::PipelineError;
use crate::pipeline::element::{self, Target};
use crate::pipeline::offscreen_renderer::CanvasWrapper; use crate::pipeline::offscreen_renderer::CanvasWrapper;
use crate::{coords::Range, widgets::widget::Widget};
use chrono::{prelude::*, DateTime}; use chrono::{prelude::*, DateTime};
use femtovg::{renderer::OpenGl, Canvas, ImageId}; use femtovg::{renderer::OpenGl, Canvas};
use std::{ use std::{
cell::{Ref, RefCell},
fmt::Debug, fmt::Debug,
future::Future,
pin::Pin,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use core_extensions::SelfOps;
use crate::errors::PipelineError;
type PrepareFunc = Arc< type PrepareFunc = Arc<
Mutex< Mutex<
@ -29,7 +24,7 @@ pub type LayerImplSync = Arc<Mutex<Box<dyn LayerImpl + Send + Sync>>>;
pub enum AssoElement { pub enum AssoElement {
TimeSeries(Arc<Mutex<element::TimeSeriesElement>>), TimeSeries(Arc<Mutex<element::TimeSeriesElement>>),
Instant(element::InstantElement), Instant(element::InstantElement),
Test Test,
} }
#[derive(Clone)] #[derive(Clone)]
@ -70,7 +65,7 @@ impl Layer {
pub fn draw(&mut self, render: &Render, window_size: (f32, f32)) -> Result<(), PipelineError> { pub fn draw(&mut self, render: &Render, window_size: (f32, f32)) -> Result<(), PipelineError> {
if self.visiable { if self.visiable {
match self.associated_element{ match self.associated_element {
AssoElement::Instant(ref mut e) => { AssoElement::Instant(ref mut e) => {
e.render(render); e.render(render);
} }
@ -79,34 +74,30 @@ impl Layer {
let mut buffer = e.buffer.lock().unwrap(); let mut buffer = e.buffer.lock().unwrap();
let mut result = buffer.get_mut(&self.time.unwrap()).map(|x| x.as_mut()); let mut result = buffer.get_mut(&self.time.unwrap()).map(|x| x.as_mut());
if result.is_none(){ if result.is_none() {
return Ok(()) ; return Ok(());
} }
if let Some(result) = result.unwrap(){ if let Some(result) = result.unwrap() {
let target = result.get_mut_target(); let target = result.get_mut_target();
let mut canvas = render.get_canvas(); // let mut canvas = render.get_canvas();
let mut canvas = canvas.as_mut().unwrap(); // let mut canvas = canvas.as_mut().unwrap();
let (ox, oy) = target.origin(render); // let (ox, oy) = target.origin(render);
let (x, y) = target.size(render); // let (x, y) = target.size(render);
let result_id = match target.target { render.draw_img(target);
TargetType::ImageId(id) => id,
TargetType::Mem(ref mem) => {
let converted = canvas
.load_image_mem(mem, femtovg::ImageFlags::empty())
.unwrap();
target.set_target(TargetType::ImageId(converted));
converted
}
};
let paint = femtovg::Paint::image(result_id, ox, oy, x, y, 0.0, self.alpha);
let mut path = femtovg::Path::new();
path.rect(ox, oy, x, y);
canvas.fill_path(&path, &paint);
// let result_id = match target.target {
// TargetType::ImageId(id) => id,
// TargetType::Mem(ref mem) => {
// let converted = canvas
// .load_image_mem(mem, femtovg::ImageFlags::empty())
// .unwrap();
// target.set_target(TargetType::ImageId(converted));
// converted
// }
// };
} }
} }
_ => {} _ => {}
@ -140,5 +131,4 @@ impl Layer {
pub fn set_associated_element(&mut self, element: AssoElement) { pub fn set_associated_element(&mut self, element: AssoElement) {
self.associated_element = element; self.associated_element = element;
} }
} }

View File

@ -1,13 +1,11 @@
use tracing::info;
mod cms; mod cms;
mod exterior; mod exterior;
mod imp; mod imp;
mod interior; mod interior;
pub mod predefined; pub mod predefined;
pub mod renders; pub mod renders;
pub mod widget;
pub use self::imp::{RenderConfig, RenderMotion, RenderStatus}; pub use self::imp::{RenderConfig, RenderMotion, RenderStatus};
use crate::components::messages::MonitorInputMsg;
use crate::coords::cms::CMS; use crate::coords::cms::CMS;
use crate::coords::{Mapper, Range}; use crate::coords::{Mapper, Range};
use crate::errors::PipelineError; use crate::errors::PipelineError;
@ -21,9 +19,7 @@ use gtk::prelude::*;
use gtk::subclass::prelude::ObjectSubclassIsExt; use gtk::subclass::prelude::ObjectSubclassIsExt;
use gtk::EventControllerScrollFlags; use gtk::EventControllerScrollFlags;
pub use interior::*; pub use interior::*;
use slippy_map_tiles::Tile;
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};
@ -251,7 +247,6 @@ impl Render {
height, height,
bounds, bounds,
None, None,
None,
)) ))
} }
@ -263,6 +258,10 @@ impl Render {
} }
} }
pub fn draw_img(&self, img: &mut Target) {
self.imp().draw_target(img);
}
pub fn scale_rate(&self) -> f64 { pub fn scale_rate(&self) -> f64 {
let status = self.imp().status.borrow(); let status = self.imp().status.borrow();
status.scale_rate.unwrap() status.scale_rate.unwrap()

View File

@ -13,8 +13,8 @@ use std::{fmt::Debug, io::Cursor, marker::PhantomData};
use super::super::renders::DataRenderer; use super::super::renders::DataRenderer;
use super::super::{LayerImpl, Render}; use super::super::{LayerImpl, Render};
use crate::{data::Radar2d, utils::meshgrid};
use crate::coords::cms::CMS; use crate::coords::cms::CMS;
use crate::{data::Radar2d, utils::meshgrid};
#[derive(Debug)] #[derive(Debug)]
pub struct GridFieldRenderer<CMAP, T> pub struct GridFieldRenderer<CMAP, T>
@ -175,7 +175,7 @@ where
let d2_end = data.dim2.view().last().unwrap().clone(); let d2_end = data.dim2.view().last().unwrap().clone();
Target::new( Target::new(
TargetType::Mem(png_data), TargetType::NativeBuffer(png_data),
w, w,
h, h,
((d1_start, d1_end).into(), (d2_start, d2_end).into()), ((d1_start, d1_end).into(), (d2_start, d2_end).into()),

View File

@ -1,5 +1 @@
pub mod color_mapper;
pub mod gis; pub mod gis;
// pub mod grid_field_renderer;
// pub mod layers;
pub mod widgets;

View File

@ -1,103 +0,0 @@
use super::{
super::widget::{Widget as WidgetTrait, WidgetType},
super::Layer,
color_mapper::ColorMapper,
};
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
use num_traits::*;
#[derive(Debug)]
pub struct ColorBar<V, T>
where
V: num_traits::NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64>,
T: ColorMapper<V>,
{
color_mapper: T,
padding: [f32; 4],
width: f32,
height: f32,
margin: [i32; 4],
color_list: Vec<femtovg::Color>,
phantom: std::marker::PhantomData<V>,
}
impl<V, T> ColorBar<V, T>
where
V: num_traits::NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64>,
T: ColorMapper<V>,
{
pub fn new(color_mapper: T, padding: [f32; 4], size: (f32, f32), margin: [i32; 4]) -> Self {
let (l, ll) = color_mapper.min_max();
let invalid = color_mapper.invalid();
let colors = color_mapper.map_min_to_max();
Self {
color_list: colors,
color_mapper,
padding,
width: size.0,
height: size.1,
margin,
phantom: std::marker::PhantomData,
}
}
}
impl<V, T> WidgetTrait for ColorBar<V, T>
where
V: num_traits::NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64> + Send + Sync,
T: ColorMapper<V> + 'static,
{
fn cairo_render(&self, canvas: &gtk::cairo::Context, w: f32, h: f32) {
let bar_width = 10;
let bar_height = h - self.padding[0] - self.padding[2];
let (x, y) = (self.padding[3], self.padding[0]);
let b_h = bar_height / self.color_list.len() as f32;
for ((i, color), label) in self
.color_list
.iter()
.enumerate()
.zip(self.color_mapper.labels())
{
let y = y + i as f32 * b_h;
canvas.set_source_rgba(
color.r as f64,
color.g as f64,
color.b as f64,
color.a as f64,
);
canvas.rectangle(x as f64, y as f64, bar_width as f64, b_h as f64);
canvas.fill();
let extents = canvas.text_extents(&label).unwrap();
canvas.move_to(
(x + bar_width as f32 + 5.0) as f64,
y as f64 + extents.height() / 2.0 as f64,
);
canvas.set_source_rgba(1.0, 1.0, 1.0, 1.0);
canvas.show_text(&label);
}
}
fn location(&self) -> (gtk::Align, gtk::Align) {
(gtk::Align::End, gtk::Align::End)
}
fn size(&self) -> (f32, f32) {
(self.width, self.height)
}
fn widget_type(&self) -> WidgetType {
WidgetType::Cairo
}
fn margin(&self) -> [i32; 4] {
self.margin
}
fn padding(&self) -> [i32; 4] {
[10, 10, 10, 10]
}
}

View File

@ -1,7 +1,7 @@
use super::Render; use super::Render;
use crate::coords::cms::CMS; use crate::coords::cms::CMS;
use femtovg::{renderer::OpenGl, Canvas};
use crate::pipeline::element::Target; use crate::pipeline::element::Target;
use femtovg::{renderer::OpenGl, Canvas};
pub trait DataRenderer { pub trait DataRenderer {
type Data; type Data;

View File

@ -1,4 +1,4 @@
use crate::widgets::widget::Widget; use crate::components::monitor::Widget;
use chrono::{prelude::*, Duration}; use chrono::{prelude::*, Duration};
use gtk::glib::{self, prelude::*, ParamSpec, Properties, Value}; use gtk::glib::{self, prelude::*, ParamSpec, Properties, Value};
use gtk::prelude::*; use gtk::prelude::*;

View File

@ -1,4 +1,4 @@
use self::widget::Widget; use crate::components::monitor::Widget;
pub use super::*; pub use super::*;
mod imp; mod imp;
@ -66,7 +66,7 @@ impl WidgetFrame {
drawing_area.set_draw_func(clone!(@strong widget => move |_, canvas, x, y| { drawing_area.set_draw_func(clone!(@strong widget => move |_, canvas, x, y| {
let widget = widget.borrow(); let widget = widget.borrow();
let widget = widget.as_ref().unwrap(); let widget = widget.as_ref().unwrap();
widget.cairo_render(canvas,x as f32,y as f32); // widget.cairo_render(canvas,x as f32,y as f32);
})); }));
drawing_area.queue_draw(); drawing_area.queue_draw();
} }