add more detail
This commit is contained in:
parent
6bcf8ccc84
commit
b70b86e71c
64
Cargo.lock
generated
64
Cargo.lock
generated
@ -285,6 +285,7 @@ dependencies = [
|
|||||||
"geo",
|
"geo",
|
||||||
"geo-macros",
|
"geo-macros",
|
||||||
"geo-types",
|
"geo-types",
|
||||||
|
"geojson 0.24.1",
|
||||||
"glib",
|
"glib",
|
||||||
"glib-build-tools",
|
"glib-build-tools",
|
||||||
"glib-macros",
|
"glib-macros",
|
||||||
@ -307,6 +308,7 @@ dependencies = [
|
|||||||
"shapefile",
|
"shapefile",
|
||||||
"svg",
|
"svg",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"topojson",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -900,6 +902,32 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "geojson"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3c1147be22f66284de4387c43e4abab872e525a528f4d0af4e4e0f231895ea4"
|
||||||
|
dependencies = [
|
||||||
|
"geo-types",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "geojson"
|
||||||
|
version = "0.24.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b"
|
||||||
|
dependencies = [
|
||||||
|
"geo-types",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
@ -1309,6 +1337,12 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.26"
|
version = "0.1.26"
|
||||||
@ -2050,6 +2084,7 @@ dependencies = [
|
|||||||
"fragile",
|
"fragile",
|
||||||
"futures",
|
"futures",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
|
"libadwaita",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"relm4-macros",
|
"relm4-macros",
|
||||||
"tokio",
|
"tokio",
|
||||||
@ -2141,6 +2176,12 @@ dependencies = [
|
|||||||
"unicode-script",
|
"unicode-script",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped_threadpool"
|
name = "scoped_threadpool"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@ -2179,6 +2220,17 @@ dependencies = [
|
|||||||
"syn 2.0.29",
|
"syn 2.0.29",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.99"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
@ -2480,6 +2532,18 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "topojson"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41ed0491c918a501d49a5da86736c9e27666b6f5794fadec0ff8d4842c642b73"
|
||||||
|
dependencies = [
|
||||||
|
"geojson 0.23.0",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.37"
|
version = "0.1.37"
|
||||||
|
|||||||
@ -32,10 +32,12 @@ proj = "0.27.2"
|
|||||||
image = "0.24.7"
|
image = "0.24.7"
|
||||||
anyhow = "1.0.72"
|
anyhow = "1.0.72"
|
||||||
proj5 = { version = "0.1.7", features = ["multithreading"] }
|
proj5 = { version = "0.1.7", features = ["multithreading"] }
|
||||||
relm4 = "0.6.1"
|
relm4 = { version = "0.6.1",features=["libadwaita"]}
|
||||||
relm4-components = "0.6.1"
|
relm4-components = "0.6.1"
|
||||||
rstar = "*"
|
rstar = "*"
|
||||||
geo = "0.26.0"
|
geo = "0.26.0"
|
||||||
|
topojson = "0.5.1"
|
||||||
|
geojson = "0.24.1"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -48,3 +50,4 @@ path = "geo-macros"
|
|||||||
[dependencies.adw]
|
[dependencies.adw]
|
||||||
package = "libadwaita"
|
package = "libadwaita"
|
||||||
version = "*"
|
version = "*"
|
||||||
|
features= ["v1_4"]
|
||||||
|
|||||||
79
back.txt
Normal file
79
back.txt
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
Layer::geojson_layer_with_path(
|
||||||
|
"/Users/tsuki/Downloads/new_zhejiang.json",
|
||||||
|
true,
|
||||||
|
|_self, c, render, _| {
|
||||||
|
if let Some(json_resources) = _self.get_resources() {
|
||||||
|
if let Resources::GeoJson(geojson) = json_resources.deref() {
|
||||||
|
MapRender::test(&geojson, c, render);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Layer::new(true, None, |s, c, render, _| {
|
||||||
|
if let Some(target) = s.render_target() {
|
||||||
|
if let Ok(_) = c.image_size(target.target) {
|
||||||
|
let (x, y) = target.size(render);
|
||||||
|
let (ox, oy) = target.origin(render);
|
||||||
|
let painter = Paint::image(target.target, ox, oy, x, y, 0.0, 1.0);
|
||||||
|
let mut path = Path::new();
|
||||||
|
path.rect(ox, oy, x, y);
|
||||||
|
c.fill_path(&path, &painter);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let renderer = RadarEchoRenderer::new(BoundaryNorm::new(
|
||||||
|
vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65],
|
||||||
|
vec![
|
||||||
|
Color::rgb(0, 172, 164),
|
||||||
|
Color::rgb(192, 192, 254),
|
||||||
|
Color::rgb(122, 114, 238),
|
||||||
|
Color::rgb(30, 38, 208),
|
||||||
|
Color::rgb(166, 252, 168),
|
||||||
|
Color::rgb(0, 234, 0),
|
||||||
|
Color::rgb(16, 146, 26),
|
||||||
|
Color::rgb(252, 244, 100),
|
||||||
|
Color::rgb(200, 200, 2),
|
||||||
|
Color::rgb(140, 140, 0),
|
||||||
|
Color::rgb(254, 172, 172),
|
||||||
|
Color::rgb(254, 100, 92),
|
||||||
|
Color::rgb(238, 2, 48),
|
||||||
|
Color::rgb(212, 142, 254),
|
||||||
|
Color::rgb(170, 36, 250),
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
let loader = Npz;
|
||||||
|
let data: RadarData2d<i8, OwnedRepr<i8>> = loader
|
||||||
|
.load("/Users/tsuki/projects/radar-g/test2.npz")
|
||||||
|
.unwrap();
|
||||||
|
let img = renderer.render(render, c, &data);
|
||||||
|
if let Ok(_) = c.image_size(img.target) {
|
||||||
|
let (x, y) = img.size(render);
|
||||||
|
let (ox, oy) = img.origin(render);
|
||||||
|
println!("{} {} {} {}", x, y, ox, oy);
|
||||||
|
let painter = Paint::image(img.target, ox, oy, x, y, 0.0, 1.0);
|
||||||
|
let mut path = Path::new();
|
||||||
|
path.rect(ox, oy, x, y);
|
||||||
|
s.set_render_target(img);
|
||||||
|
c.fill_path(&path, &painter);
|
||||||
|
c.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############# TEMPLATES ###################
|
||||||
|
// Monitor::new(
|
||||||
|
// Render::new(
|
||||||
|
// Some(Mapper::new(
|
||||||
|
// Proj::new(Mercator::default().build().as_str()).unwrap(),
|
||||||
|
// (119.539..121.135).into(),
|
||||||
|
// (29.13..30.164).into(),
|
||||||
|
// )),
|
||||||
|
// RenderConfig { padding: [50.0;4] }
|
||||||
|
// )
|
||||||
|
// )
|
||||||
9
check.py
Normal file
9
check.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
data = np.load("/Users/tsuki/projects/radar-g/test2.npz")
|
||||||
|
|
||||||
|
print(data['lat'][0])
|
||||||
|
print(data['lat'][-1])
|
||||||
|
|
||||||
|
print(data['lon'][0])
|
||||||
|
print(data['lon'][-1])
|
||||||
@ -1,9 +1,12 @@
|
|||||||
use super::{control_panel::ControlPanelModel, render_panel::RenderPanelModel};
|
use super::{control_panel::ControlPanelModel, render_panel::RenderPanelModel};
|
||||||
use gtk::{prelude::{
|
|
||||||
ApplicationExt, ButtonExt, DialogExt, GtkWindowExt, ToggleButtonExt, WidgetExt,
|
|
||||||
}, traits::OrientableExt};
|
|
||||||
use gtk::prelude::GtkApplicationExt;
|
use gtk::prelude::GtkApplicationExt;
|
||||||
|
use gtk::{
|
||||||
|
prelude::{ApplicationExt, ButtonExt, DialogExt, GtkWindowExt, ToggleButtonExt, WidgetExt},
|
||||||
|
traits::OrientableExt,
|
||||||
|
};
|
||||||
use relm4::*;
|
use relm4::*;
|
||||||
|
use adw::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AppMode {
|
pub enum AppMode {
|
||||||
@ -35,14 +38,19 @@ impl SimpleComponent for AppModel {
|
|||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
main_window = gtk::ApplicationWindow {
|
main_window=adw::ApplicationWindow {
|
||||||
set_default_width: 1200,
|
set_default_width: 1200,
|
||||||
set_default_height: 700,
|
set_default_height: 900,
|
||||||
set_focus_on_click:true,
|
set_focus_on_click:true,
|
||||||
set_titlebar: Some(>k::HeaderBar::new()),
|
// set_titlebar: Some(>k::HeaderBar::new()),
|
||||||
gtk::Box{
|
gtk::Box{
|
||||||
set_orientation: gtk::Orientation::Horizontal,
|
set_orientation: gtk::Orientation::Vertical,
|
||||||
set_valign:gtk::Align::Fill,
|
set_valign:gtk::Align::Fill,
|
||||||
|
adw::HeaderBar {
|
||||||
|
#[wrap(Some)]
|
||||||
|
set_title_widget = &adw::WindowTitle {
|
||||||
|
}
|
||||||
|
},
|
||||||
model.control.widget(),
|
model.control.widget(),
|
||||||
model.render.widget(),
|
model.render.widget(),
|
||||||
},
|
},
|
||||||
@ -51,6 +59,21 @@ impl SimpleComponent for AppModel {
|
|||||||
gtk::Inhibit(true)
|
gtk::Inhibit(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// main_window = gtk::ApplicationWindow {
|
||||||
|
// set_default_width: 1200,
|
||||||
|
// set_default_height: 900,
|
||||||
|
// set_focus_on_click:true,
|
||||||
|
// set_titlebar: Some(>k::HeaderBar::new()),
|
||||||
|
// gtk::Box{
|
||||||
|
// set_orientation: gtk::Orientation::Vertical,
|
||||||
|
// set_valign:gtk::Align::Fill,
|
||||||
|
|
||||||
|
// },
|
||||||
|
// connect_close_request[sender] => move |_| {
|
||||||
|
// sender.input(AppMsg::CloseRequest);
|
||||||
|
// gtk::Inhibit(true)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(
|
fn init(
|
||||||
@ -63,7 +86,7 @@ impl SimpleComponent for AppModel {
|
|||||||
.forward(sender.input_sender(), |msg| AppMsg::Close);
|
.forward(sender.input_sender(), |msg| AppMsg::Close);
|
||||||
|
|
||||||
let render = RenderPanelModel::builder()
|
let render = RenderPanelModel::builder()
|
||||||
.launch(0)
|
.launch(())
|
||||||
.forward(sender.input_sender(), |a| AppMsg::Close);
|
.forward(sender.input_sender(), |a| AppMsg::Close);
|
||||||
|
|
||||||
relm4::menu! {
|
relm4::menu! {
|
||||||
|
|||||||
0
src/components/control_panel/messages.rs
Normal file
0
src/components/control_panel/messages.rs
Normal file
3
src/components/control_panel/mod.rs
Normal file
3
src/components/control_panel/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod control_panel;
|
||||||
|
mod messages;
|
||||||
|
pub use control_panel::*;
|
||||||
@ -1,3 +1,6 @@
|
|||||||
pub mod app;
|
pub mod app;
|
||||||
mod control_panel;
|
mod control_panel;
|
||||||
mod render_panel;
|
mod render_panel;
|
||||||
|
|
||||||
|
pub use control_panel::*;
|
||||||
|
pub use render_panel::*;
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
use crate::monitor::Monitor;
|
|
||||||
use crate::coords::proj::Mercator;
|
|
||||||
use crate::render::{
|
|
||||||
BackgroundConfig, BackgroundWidget, ForegroundConfig, ForegroundWidget, Render,
|
|
||||||
};
|
|
||||||
use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, ToggleButtonExt};
|
|
||||||
use relm4::*;
|
|
||||||
|
|
||||||
pub struct RenderPanelModel;
|
|
||||||
|
|
||||||
#[relm4::component(pub)]
|
|
||||||
impl SimpleComponent for RenderPanelModel {
|
|
||||||
type Init = i8;
|
|
||||||
type Output = ();
|
|
||||||
type Input = ();
|
|
||||||
|
|
||||||
view! {
|
|
||||||
#[root]
|
|
||||||
Monitor::new(
|
|
||||||
Render::new(
|
|
||||||
Some(Mercator::new().into())
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(
|
|
||||||
init: Self::Init,
|
|
||||||
root: &Self::Root,
|
|
||||||
sender: relm4::ComponentSender<Self>,
|
|
||||||
) -> relm4::ComponentParts<Self> {
|
|
||||||
let model = RenderPanelModel {};
|
|
||||||
// Insert the macro code generation here
|
|
||||||
let widgets = view_output!();
|
|
||||||
|
|
||||||
ComponentParts { model, widgets }
|
|
||||||
}
|
|
||||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {}
|
|
||||||
}
|
|
||||||
12
src/components/render_panel/messages.rs
Normal file
12
src/components/render_panel/messages.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use crate::render::Layer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RenderInputMsg {
|
||||||
|
AddLayer(Layer),
|
||||||
|
RemoveLayer(usize),
|
||||||
|
MoveLayer(usize, usize),
|
||||||
|
SetLayerVisibility(usize, bool),
|
||||||
|
EditLayer(usize),
|
||||||
|
}
|
||||||
4
src/components/render_panel/mod.rs
Normal file
4
src/components/render_panel/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
mod render_panel;
|
||||||
|
mod messages;
|
||||||
|
mod monitor;
|
||||||
|
pub use render_panel::RenderPanelModel;
|
||||||
4
src/components/render_panel/monitor/mod.rs
Normal file
4
src/components/render_panel/monitor/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod monitor;
|
||||||
|
pub mod sidebar;
|
||||||
|
pub mod render;
|
||||||
|
pub use monitor::MonitorModel;
|
||||||
46
src/components/render_panel/monitor/monitor.rs
Normal file
46
src/components/render_panel/monitor/monitor.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use super::{render::render::RenderModel, sidebar::sidebar::SideBarModel};
|
||||||
|
use relm4::*;
|
||||||
|
pub struct MonitorModel {
|
||||||
|
render: Controller<RenderModel>,
|
||||||
|
sidebar: Controller<SideBarModel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[relm4::component(pub)]
|
||||||
|
impl SimpleComponent for MonitorModel {
|
||||||
|
type Init = ();
|
||||||
|
type Output = ();
|
||||||
|
type Input = ();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
adw::OverlaySplitView {
|
||||||
|
set_sidebar_position:gtk::PackType::End,
|
||||||
|
#[wrap(Some)]
|
||||||
|
set_sidebar = &adw::NavigationPage::builder().title("sidebar").child(model.sidebar.widget()).build(),
|
||||||
|
#[wrap(Some)]
|
||||||
|
set_content = &adw::NavigationPage::builder().title("content").child(model.render.widget()).build(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
init: Self::Init,
|
||||||
|
root: &Self::Root,
|
||||||
|
sender: relm4::ComponentSender<Self>,
|
||||||
|
) -> relm4::ComponentParts<Self> {
|
||||||
|
let sidebar: Controller<SideBarModel> = SideBarModel::builder()
|
||||||
|
.launch(())
|
||||||
|
.forward(sender.input_sender(), |msg| {});
|
||||||
|
let render: Controller<RenderModel> = RenderModel::builder()
|
||||||
|
.launch((None, None))
|
||||||
|
.forward(sender.input_sender(), |msg| {});
|
||||||
|
|
||||||
|
let model = MonitorModel { render, sidebar };
|
||||||
|
|
||||||
|
|
||||||
|
let widgets = view_output!();
|
||||||
|
|
||||||
|
|
||||||
|
ComponentParts { model, widgets }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {}
|
||||||
|
}
|
||||||
2
src/components/render_panel/monitor/render/mod.rs
Normal file
2
src/components/render_panel/monitor/render/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod render;
|
||||||
|
pub use render::*;
|
||||||
40
src/components/render_panel/monitor/render/render.rs
Normal file
40
src/components/render_panel/monitor/render/render.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use crate::coords::proj::Mercator;
|
||||||
|
use crate::coords::Mapper;
|
||||||
|
use crate::render::{Render, RenderConfig};
|
||||||
|
use relm4::*;
|
||||||
|
|
||||||
|
pub struct RenderModel {
|
||||||
|
config: RenderConfig,
|
||||||
|
mapper: Mapper,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[relm4::component(pub)]
|
||||||
|
impl SimpleComponent for RenderModel {
|
||||||
|
type Init = (Option<Mapper>, Option<RenderConfig>);
|
||||||
|
type Output = ();
|
||||||
|
type Input = ();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
Render{
|
||||||
|
set_mapper = model.mapper.clone(),
|
||||||
|
set_cfg = model.config.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
init: Self::Init,
|
||||||
|
root: &Self::Root,
|
||||||
|
sender: relm4::ComponentSender<Self>,
|
||||||
|
) -> relm4::ComponentParts<Self> {
|
||||||
|
let (mapper, config) = init;
|
||||||
|
let config = config.unwrap_or(RenderConfig::default());
|
||||||
|
let mapper = mapper.unwrap_or(Mercator::default().into());
|
||||||
|
|
||||||
|
let model = RenderModel { config, mapper };
|
||||||
|
let widgets = view_output!();
|
||||||
|
|
||||||
|
ComponentParts { model, widgets }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {}
|
||||||
|
}
|
||||||
2
src/components/render_panel/monitor/sidebar/mod.rs
Normal file
2
src/components/render_panel/monitor/sidebar/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod sidebar;
|
||||||
|
pub use sidebar::*;
|
||||||
171
src/components/render_panel/monitor/sidebar/sidebar.rs
Normal file
171
src/components/render_panel/monitor/sidebar/sidebar.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
use gtk::prelude::*;
|
||||||
|
use relm4::{
|
||||||
|
binding::{Binding, U8Binding},
|
||||||
|
prelude::*,
|
||||||
|
typed_list_view::{RelmListItem, TypedListView},
|
||||||
|
RelmObjectExt,
|
||||||
|
};
|
||||||
|
pub struct SideBarModel {
|
||||||
|
counter: u8,
|
||||||
|
list_view_wrapper: TypedListView<MyListItem, gtk::SingleSelection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Msg {
|
||||||
|
Append,
|
||||||
|
Remove,
|
||||||
|
OnlyShowEven(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[relm4::component(pub)]
|
||||||
|
impl SimpleComponent for SideBarModel {
|
||||||
|
type Init = ();
|
||||||
|
type Output = ();
|
||||||
|
type Input = Msg;
|
||||||
|
|
||||||
|
view! {
|
||||||
|
gtk::Box {
|
||||||
|
set_orientation: gtk::Orientation::Vertical,
|
||||||
|
set_spacing: 5,
|
||||||
|
set_margin_all: 5,
|
||||||
|
gtk::Notebook {
|
||||||
|
set_vexpand: true,
|
||||||
|
append_page:(&page_1, Some(&label)),
|
||||||
|
},
|
||||||
|
gtk::Button {
|
||||||
|
set_label: "Append 10 items",
|
||||||
|
connect_clicked => Msg::Append,
|
||||||
|
},
|
||||||
|
|
||||||
|
gtk::Button {
|
||||||
|
set_label: "Remove second item",
|
||||||
|
connect_clicked => Msg::Remove,
|
||||||
|
},
|
||||||
|
gtk::ScrolledWindow {
|
||||||
|
set_vexpand: true,
|
||||||
|
|
||||||
|
#[local_ref]
|
||||||
|
my_view -> gtk::ListView {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
init: Self::Init,
|
||||||
|
root: &Self::Root,
|
||||||
|
sender: ComponentSender<Self>,
|
||||||
|
) -> ComponentParts<Self> {
|
||||||
|
// Initialize the ListView wrapper
|
||||||
|
let mut list_view_wrapper: TypedListView<MyListItem, gtk::SingleSelection> =
|
||||||
|
TypedListView::with_sorting();
|
||||||
|
|
||||||
|
// Add a filter and disable it
|
||||||
|
list_view_wrapper.add_filter(|item| item.value % 2 == 0);
|
||||||
|
list_view_wrapper.set_filter_status(0, false);
|
||||||
|
|
||||||
|
let model = SideBarModel {
|
||||||
|
counter: 0,
|
||||||
|
list_view_wrapper,
|
||||||
|
};
|
||||||
|
let my_view = &model.list_view_wrapper.view;
|
||||||
|
|
||||||
|
let page_1 = gtk::Box::new(gtk::Orientation::Vertical, 5);
|
||||||
|
let label = gtk::Label::new(Some("Page 1"));
|
||||||
|
|
||||||
|
let widgets = view_output!();
|
||||||
|
ComponentParts { model, widgets }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
||||||
|
match message {
|
||||||
|
Msg::Append => {
|
||||||
|
// Add 10 items
|
||||||
|
for _ in 0..10 {
|
||||||
|
self.counter = self.counter.wrapping_add(1);
|
||||||
|
self.list_view_wrapper.append(MyListItem::new(self.counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count up the first item
|
||||||
|
let first_item = self.list_view_wrapper.get(0).unwrap();
|
||||||
|
let first_binding = &mut first_item.borrow_mut().binding;
|
||||||
|
let mut guard = first_binding.guard();
|
||||||
|
*guard += 1;
|
||||||
|
}
|
||||||
|
Msg::Remove => {
|
||||||
|
// Remove the second item
|
||||||
|
self.list_view_wrapper.remove(1);
|
||||||
|
}
|
||||||
|
Msg::OnlyShowEven(show_only_even) => {
|
||||||
|
// Disable or enable the first filter
|
||||||
|
self.list_view_wrapper.set_filter_status(0, show_only_even);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
struct MyListItem {
|
||||||
|
value: u8,
|
||||||
|
binding: U8Binding,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyListItem {
|
||||||
|
fn new(value: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
value,
|
||||||
|
binding: U8Binding::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Widgets {
|
||||||
|
label: gtk::Label,
|
||||||
|
label2: gtk::Label,
|
||||||
|
button: gtk::CheckButton,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Widgets {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
dbg!(self.label.label());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelmListItem for MyListItem {
|
||||||
|
type Root = gtk::Box;
|
||||||
|
type Widgets = Widgets;
|
||||||
|
|
||||||
|
fn setup(_item: >k::ListItem) -> (gtk::Box, Widgets) {
|
||||||
|
relm4::view! {
|
||||||
|
my_box = gtk::Box {
|
||||||
|
#[name = "label"]
|
||||||
|
gtk::Label,
|
||||||
|
|
||||||
|
#[name = "label2"]
|
||||||
|
gtk::Label,
|
||||||
|
|
||||||
|
#[name = "button"]
|
||||||
|
gtk::CheckButton,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let widgets = Widgets {
|
||||||
|
label,
|
||||||
|
label2,
|
||||||
|
button,
|
||||||
|
};
|
||||||
|
|
||||||
|
(my_box, widgets)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
|
||||||
|
let Widgets {
|
||||||
|
label,
|
||||||
|
label2,
|
||||||
|
button,
|
||||||
|
} = widgets;
|
||||||
|
|
||||||
|
label.set_label(&format!("Value: {} ", self.value));
|
||||||
|
label2.add_write_only_binding(&self.binding, "label");
|
||||||
|
button.set_active(self.value % 2 == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/components/render_panel/render_panel.rs
Normal file
39
src/components/render_panel/render_panel.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use super::messages::RenderInputMsg;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use super::monitor::MonitorModel;
|
||||||
|
use relm4::*;
|
||||||
|
pub struct RenderPanelModel {
|
||||||
|
monitor: Controller<MonitorModel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[relm4::component(pub)]
|
||||||
|
impl SimpleComponent for RenderPanelModel {
|
||||||
|
type Init = ();
|
||||||
|
type Output = ();
|
||||||
|
type Input = ();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
gtk::Box{
|
||||||
|
set_orientation: gtk::Orientation::Horizontal,
|
||||||
|
set_hexpand:true,
|
||||||
|
set_vexpand:true,
|
||||||
|
model.monitor.widget(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
init: Self::Init,
|
||||||
|
root: &Self::Root,
|
||||||
|
sender: relm4::ComponentSender<Self>,
|
||||||
|
) -> relm4::ComponentParts<Self> {
|
||||||
|
let monitor: Controller<MonitorModel> = MonitorModel::builder()
|
||||||
|
.launch(())
|
||||||
|
.forward(sender.input_sender(), |msg| {});
|
||||||
|
|
||||||
|
let model = RenderPanelModel { monitor };
|
||||||
|
let widgets = view_output!();
|
||||||
|
ComponentParts { model, widgets }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::render::WindowCoord;
|
use crate::render::WindowCoord;
|
||||||
|
use std::borrow::ToOwned;
|
||||||
|
|
||||||
use super::{proj::ProjectionS, Range};
|
use super::{proj::ProjectionS, Range};
|
||||||
use geo_types::{coord, Coord as GCoord, LineString};
|
use geo_types::{coord, Coord as GCoord, LineString};
|
||||||
@ -9,12 +10,25 @@ pub struct Mapper {
|
|||||||
pub range: (Range, Range),
|
pub range: (Range, Range),
|
||||||
bounds: (f64, f64, f64, f64),
|
bounds: (f64, f64, f64, f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Mapper {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let c = self.proj.proj_info();
|
||||||
|
let new_proj = Proj::new(c.definition.unwrap().as_str()).unwrap();
|
||||||
|
Self {
|
||||||
|
proj: new_proj,
|
||||||
|
range: self.range,
|
||||||
|
bounds: self.bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Proj> for Mapper {
|
impl From<Proj> for Mapper {
|
||||||
fn from(proj: Proj) -> Self {
|
fn from(proj: Proj) -> Self {
|
||||||
let default_range: (Range, Range) = ((-180.0..180.0).into(), (-81.0..81.0).into());
|
let default_range: (Range, Range) = ((-180.0..180.0).into(), (-81.0..81.0).into());
|
||||||
let bounds = Self::bound(&proj, default_range.clone()).unwrap();
|
let bounds = Self::bound(&proj, default_range.clone()).unwrap();
|
||||||
Self {
|
Self {
|
||||||
proj: proj,
|
proj,
|
||||||
range: (default_range.0.into(), default_range.1.into()),
|
range: (default_range.0.into(), default_range.1.into()),
|
||||||
bounds,
|
bounds,
|
||||||
}
|
}
|
||||||
@ -33,24 +47,22 @@ impl Mapper {
|
|||||||
proj: Proj,
|
proj: Proj,
|
||||||
lon_range: std::ops::Range<f64>,
|
lon_range: std::ops::Range<f64>,
|
||||||
lat_range: std::ops::Range<f64>,
|
lat_range: std::ops::Range<f64>,
|
||||||
scale: f32,
|
|
||||||
translate: (f32, f32),
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let bounds =
|
let bounds =
|
||||||
Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap();
|
Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap();
|
||||||
let range = (lon_range.into(), lat_range.into());
|
let range = (lon_range.into(), lat_range.into());
|
||||||
Self {
|
Self {
|
||||||
proj: proj,
|
proj,
|
||||||
range,
|
range,
|
||||||
bounds,
|
bounds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fore_map(&self, coord: WindowCoord) -> Result<(f64, f64), ProjError> {
|
pub fn inverse_map(&self, coord: (f64, f64)) -> Result<(f64, f64), ProjError> {
|
||||||
let (x, y) = coord;
|
let (x, y) = coord;
|
||||||
let c = (x as f64) * (self.bounds.1 - self.bounds.0) + self.bounds.0;
|
// let c = (x as f64) * (self.bounds.1 - self.bounds.0) + self.bounds.0;
|
||||||
let d = ((1.0 - y) as f64) * (self.bounds.3 - self.bounds.2) + self.bounds.2;
|
// let d = ((1.0 - y) as f64) * (self.bounds.3 - self.bounds.2) + self.bounds.2;
|
||||||
let (lon, lat) = self.proj.project((c, d), true)?;
|
let (lon, lat) = self.proj.project((x, y), true)?;
|
||||||
Ok((lon.to_degrees(), lat.to_degrees()))
|
Ok((lon.to_degrees(), lat.to_degrees()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,22 +72,28 @@ impl Mapper {
|
|||||||
return (x <= lon_range.1 && x >= lon_range.0) && (y <= lat_range.1 && y >= lat_range.0);
|
return (x <= lon_range.1 && x >= lon_range.0) && (y <= lat_range.1 && y >= lat_range.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_bounds(&self) -> (f64, f64, f64, f64) {
|
||||||
|
self.bounds
|
||||||
|
}
|
||||||
|
|
||||||
pub fn map(&self, point: (f64, f64)) -> Result<(f64, f64), ProjError> {
|
pub fn map(&self, point: (f64, f64)) -> Result<(f64, f64), ProjError> {
|
||||||
let mut point = point;
|
let mut point = point;
|
||||||
if !self.point_in_bound(point) {
|
// if !self.point_in_bound(point) {
|
||||||
point = (
|
// point = (
|
||||||
point.0.clamp(self.range.0 .0, self.range.0 .1),
|
// point.0.clamp(self.range.0 .0, self.range.0 .1),
|
||||||
point.1.clamp(self.range.1 .0, self.range.1 .1),
|
// point.1.clamp(self.range.1 .0, self.range.1 .1),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
let (p1, p2) = self
|
let (p1, p2) = self
|
||||||
.proj
|
.proj
|
||||||
.convert((point.0.to_radians(), point.1.to_radians()))?;
|
.convert((point.0.to_radians(), point.1.to_radians()))?;
|
||||||
|
|
||||||
let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0);
|
// let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0);
|
||||||
let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2);
|
// let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2);
|
||||||
|
|
||||||
Ok((x, (1.0 - y)))
|
// Ok((x, (1.0 - y)))
|
||||||
|
//
|
||||||
|
Ok((p1, p2))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_lon_range(&mut self, range: std::ops::Range<f64>) -> &mut Self {
|
pub fn set_lon_range(&mut self, range: std::ops::Range<f64>) -> &mut Self {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ pub trait Coord<T: Num> {
|
|||||||
fn dim2_range(&self) -> (T, T);
|
fn dim2_range(&self) -> (T, T);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Range(pub f64, pub f64);
|
pub struct Range(pub f64, pub f64);
|
||||||
|
|
||||||
impl Range {
|
impl Range {
|
||||||
|
|||||||
@ -25,7 +25,23 @@ fn proj_string<'a>(vs: Vec<(&'a str, &'a str)>) -> String {
|
|||||||
|
|
||||||
impl Mercator {
|
impl Mercator {
|
||||||
/// Creates a new instance of the `Mercator` projection with default values.
|
/// Creates a new instance of the `Mercator` projection with default values.
|
||||||
pub fn new() -> Self {
|
pub fn new(
|
||||||
|
central_lon: f64,
|
||||||
|
false_easting: f64,
|
||||||
|
false_northing: f64,
|
||||||
|
latitude_true_scale: f64,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
central_lon,
|
||||||
|
false_easting,
|
||||||
|
false_northing,
|
||||||
|
latitude_true_scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Mercator {
|
||||||
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
central_lon: 0.0,
|
central_lon: 0.0,
|
||||||
false_easting: 0.0,
|
false_easting: 0.0,
|
||||||
@ -54,5 +70,4 @@ impl ProjectionS for Mercator {
|
|||||||
let _proj_string = proj_string(input);
|
let _proj_string = proj_string(input);
|
||||||
_proj_string
|
_proj_string
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use crate::errors::DataError;
|
use crate::errors::DataError;
|
||||||
use image::RgbImage;
|
use image::RgbImage;
|
||||||
use ndarray::{
|
use ndarray::{
|
||||||
s, Array, Array1, Array2, Array3, ArrayBase, Axis, DataMut, Ix1, Ix2, OwnedRepr, RawDataClone,
|
s, Array, Array1, Array2, Array3, ArrayBase, Axis, DataMut, Dimension, Ix1, Ix2, OwnedRepr,
|
||||||
ViewRepr,
|
RawDataClone, ViewRepr,
|
||||||
};
|
};
|
||||||
use npyz::{npz::NpzArchive, Deserialize};
|
use npyz::{npz::NpzArchive, Deserialize};
|
||||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||||
@ -30,9 +30,29 @@ where
|
|||||||
pub dim1: ArrayBase<OwnedRepr<X>, I>,
|
pub dim1: ArrayBase<OwnedRepr<X>, I>,
|
||||||
pub dim2: ArrayBase<OwnedRepr<Y>, I>,
|
pub dim2: ArrayBase<OwnedRepr<Y>, I>,
|
||||||
pub data: ArrayBase<Raw, Ix2>,
|
pub data: ArrayBase<Raw, Ix2>,
|
||||||
|
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>
|
||||||
|
where
|
||||||
|
T: Num + Clone + PartialEq + PartialOrd + Debug,
|
||||||
|
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 {
|
||||||
|
f.debug_struct("RadarData2d")
|
||||||
|
.field("dim1", &self.dim1)
|
||||||
|
.field("dim2", &self.dim2)
|
||||||
|
.field("data", &self.data)
|
||||||
|
.field("fill_value", &self.fill_value)
|
||||||
|
.field("coord_type", &self.coord_type)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Num + Clone + PartialEq + PartialOrd> Radar2d<T> {
|
impl<T: Num + Clone + PartialEq + PartialOrd> Radar2d<T> {
|
||||||
pub fn load(path: impl AsRef<Path>, meth: impl DataLoader<T, Self>) -> Result<Self, DataError> {
|
pub fn load(path: impl AsRef<Path>, meth: impl DataLoader<T, Self>) -> Result<Self, DataError> {
|
||||||
Ok(meth.load(path)?)
|
Ok(meth.load(path)?)
|
||||||
@ -66,6 +86,7 @@ where
|
|||||||
dim1: self.dim1.to_owned(),
|
dim1: self.dim1.to_owned(),
|
||||||
dim2: self.dim2.to_owned(),
|
dim2: self.dim2.to_owned(),
|
||||||
data: self.data.to_owned(),
|
data: self.data.to_owned(),
|
||||||
|
fill_value: self.fill_value.clone(),
|
||||||
coord_type: self.coord_type.clone(),
|
coord_type: self.coord_type.clone(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -115,6 +136,7 @@ where
|
|||||||
return Radar2d {
|
return Radar2d {
|
||||||
dim1,
|
dim1,
|
||||||
dim2,
|
dim2,
|
||||||
|
fill_value: self.fill_value.clone(),
|
||||||
data: output,
|
data: output,
|
||||||
coord_type: self.coord_type.clone(),
|
coord_type: self.coord_type.clone(),
|
||||||
};
|
};
|
||||||
@ -208,24 +230,28 @@ where
|
|||||||
dim1: self.dim1.slice(s![..middle_dim1]).to_owned(),
|
dim1: self.dim1.slice(s![..middle_dim1]).to_owned(),
|
||||||
dim2: self.dim2.slice(s![..middle_dim2]).to_owned(),
|
dim2: self.dim2.slice(s![..middle_dim2]).to_owned(),
|
||||||
data: lt,
|
data: lt,
|
||||||
|
fill_value: self.fill_value.clone(),
|
||||||
coord_type: self.coord_type,
|
coord_type: self.coord_type,
|
||||||
},
|
},
|
||||||
RadarData2d {
|
RadarData2d {
|
||||||
dim1: self.dim1.slice(s![middle_dim1..]).to_owned(),
|
dim1: self.dim1.slice(s![middle_dim1..]).to_owned(),
|
||||||
dim2: self.dim2.slice(s![..middle_dim2]).to_owned(),
|
dim2: self.dim2.slice(s![..middle_dim2]).to_owned(),
|
||||||
data: rt,
|
data: rt,
|
||||||
|
fill_value: self.fill_value.clone(),
|
||||||
coord_type: self.coord_type,
|
coord_type: self.coord_type,
|
||||||
},
|
},
|
||||||
RadarData2d {
|
RadarData2d {
|
||||||
dim1: self.dim1.slice(s![..middle_dim1]).to_owned(),
|
dim1: self.dim1.slice(s![..middle_dim1]).to_owned(),
|
||||||
dim2: self.dim2.slice(s![middle_dim2..]).to_owned(),
|
dim2: self.dim2.slice(s![middle_dim2..]).to_owned(),
|
||||||
data: lb,
|
data: lb,
|
||||||
|
fill_value: self.fill_value.clone(),
|
||||||
coord_type: self.coord_type,
|
coord_type: self.coord_type,
|
||||||
},
|
},
|
||||||
RadarData2d {
|
RadarData2d {
|
||||||
dim1: self.dim1.slice(s![middle_dim1..]).to_owned(),
|
dim1: self.dim1.slice(s![middle_dim1..]).to_owned(),
|
||||||
dim2: self.dim2.slice(s![middle_dim2..]).to_owned(),
|
dim2: self.dim2.slice(s![middle_dim2..]).to_owned(),
|
||||||
data: rb,
|
data: rb,
|
||||||
|
fill_value: self.fill_value.clone(),
|
||||||
coord_type: self.coord_type,
|
coord_type: self.coord_type,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -289,7 +315,7 @@ impl Npz {
|
|||||||
|
|
||||||
impl<T> DataLoader<T, RadarData2d<T, OwnedRepr<T>>> for Npz
|
impl<T> DataLoader<T, RadarData2d<T, OwnedRepr<T>>> for Npz
|
||||||
where
|
where
|
||||||
T: Num + Clone + Deserialize,
|
T: Num + Clone + Deserialize + FromPrimitive,
|
||||||
T: PartialEq + PartialOrd,
|
T: PartialEq + PartialOrd,
|
||||||
{
|
{
|
||||||
fn load<P: AsRef<Path>>(&self, path: P) -> Result<RadarData2d<T, OwnedRepr<T>>, DataError> {
|
fn load<P: AsRef<Path>>(&self, path: P) -> Result<RadarData2d<T, OwnedRepr<T>>, DataError> {
|
||||||
@ -301,6 +327,7 @@ where
|
|||||||
Ok(RadarData2d {
|
Ok(RadarData2d {
|
||||||
dim1: dim1,
|
dim1: dim1,
|
||||||
dim2: dim2,
|
dim2: dim2,
|
||||||
|
fill_value: T::from_f64(-125.0).unwrap(),
|
||||||
data: value,
|
data: value,
|
||||||
coord_type: CoordType::LatLon,
|
coord_type: CoordType::LatLon,
|
||||||
})
|
})
|
||||||
@ -473,7 +500,7 @@ pub enum DownSampleMeth {
|
|||||||
VAR,
|
VAR,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum CoordType {
|
pub enum CoordType {
|
||||||
Polar,
|
Polar,
|
||||||
LatLon,
|
LatLon,
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use coords::proj::Mercator;
|
use coords::proj::Mercator;
|
||||||
use coords::Mapper;
|
use coords::Mapper;
|
||||||
use data::{Npz, Radar2d};
|
use data::{Npz, Radar2d};
|
||||||
use femtovg::{Color, Paint};
|
use femtovg::{Color, Paint};
|
||||||
|
mod utils;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{gio, glib, Application, ApplicationWindow};
|
use gtk::{gio, glib, Application, ApplicationWindow};
|
||||||
|
use relm4::menu;
|
||||||
use relm4::RelmApp;
|
use relm4::RelmApp;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use relm4::menu;
|
|
||||||
mod components;
|
mod components;
|
||||||
mod coords;
|
mod coords;
|
||||||
mod data;
|
mod data;
|
||||||
|
|||||||
@ -19,6 +19,11 @@ impl ObjectSubclass for Monitor {
|
|||||||
|
|
||||||
impl ObjectImpl for Monitor {}
|
impl ObjectImpl for Monitor {}
|
||||||
|
|
||||||
impl WidgetImpl for Monitor {}
|
impl WidgetImpl for Monitor {
|
||||||
|
fn size_allocate(&self, width: i32, height: i32, baseline: i32) {
|
||||||
|
println!("new width {}", width);
|
||||||
|
self.parent_size_allocate(width, height, baseline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BoxImpl for Monitor {}
|
impl BoxImpl for Monitor {}
|
||||||
|
|||||||
@ -1,15 +1,11 @@
|
|||||||
mod imp;
|
mod imp;
|
||||||
use crate::data::RadarData2d;
|
use crate::render::{Render, RenderMotion, RenderConfig};
|
||||||
use crate::render::Render;
|
use adw::prelude::{ButtonExt, GLAreaExt, GestureDragExt};
|
||||||
use adw::prelude::{GLAreaExt, GestureDragExt};
|
|
||||||
use glib::clone;
|
|
||||||
use glib::subclass::prelude::*;
|
use glib::subclass::prelude::*;
|
||||||
use gtk::glib;
|
use glib::{clone, ObjectExt};
|
||||||
use gtk::traits::WidgetExt;
|
use gtk::traits::WidgetExt;
|
||||||
|
use gtk::{glib, AspectFrame};
|
||||||
use gtk::{EventControllerScrollFlags, Inhibit};
|
use gtk::{EventControllerScrollFlags, Inhibit};
|
||||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
|
||||||
use proj::ProjError;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct Monitor(ObjectSubclass<imp::Monitor>)
|
pub struct Monitor(ObjectSubclass<imp::Monitor>)
|
||||||
@ -17,13 +13,18 @@ glib::wrapper! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Monitor {
|
impl Monitor {
|
||||||
pub fn new(render: Render) -> Self {
|
pub fn new(config:RenderConfig) -> Self {
|
||||||
let this: Self = glib::Object::new();
|
let this: Self = glib::Object::new();
|
||||||
let pointer_location_detecture = gtk::EventControllerMotion::new();
|
let pointer_location_detecture = gtk::EventControllerMotion::new();
|
||||||
|
|
||||||
|
let render = Render::new(None, config);
|
||||||
|
|
||||||
pointer_location_detecture.connect_motion(
|
pointer_location_detecture.connect_motion(
|
||||||
clone!(@weak this as s, @weak render as r => move |_context, x, y| {
|
clone!(@weak this as s, @weak render as r => move |_context, x, y| {
|
||||||
r.update_status(|s| {
|
r.update_status(|s| {
|
||||||
s.pointer_location = (x as f32, y as f32);
|
let dpi = r.scale_factor();
|
||||||
|
let (_,h) = s.window_size;
|
||||||
|
s.pointer_location = (x as f32 * dpi as f32, h as f32 - y as f32 * dpi as f32);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -31,13 +32,9 @@ impl Monitor {
|
|||||||
let scale_detecture = gtk::EventControllerScroll::new(EventControllerScrollFlags::VERTICAL);
|
let scale_detecture = gtk::EventControllerScroll::new(EventControllerScrollFlags::VERTICAL);
|
||||||
scale_detecture.connect_scroll(clone!(
|
scale_detecture.connect_scroll(clone!(
|
||||||
@weak this as s, @weak render as r => @default-panic,move |_context, _x, y| {
|
@weak this as s, @weak render as r => @default-panic,move |_context, _x, y| {
|
||||||
let renderer = s.imp().renderer.borrow();
|
|
||||||
let mut scale = 1.0;
|
|
||||||
r.update_status(|status|{
|
r.update_status(|status|{
|
||||||
let current_scale = status.scale;
|
status.scale = y as f32;
|
||||||
let s = (current_scale - y as f32 / 5.0).max(1.0).min(5.0);
|
status.motion = RenderMotion::Scale;
|
||||||
status.scale = s;
|
|
||||||
scale = s;
|
|
||||||
});
|
});
|
||||||
r.queue_render();
|
r.queue_render();
|
||||||
Inhibit(false)
|
Inhibit(false)
|
||||||
@ -49,44 +46,47 @@ impl Monitor {
|
|||||||
@weak render as r => move |this, _, _| {
|
@weak render as r => move |this, _, _| {
|
||||||
let (ox, oy) = this.offset().unwrap_or((0.0,0.0));
|
let (ox, oy) = this.offset().unwrap_or((0.0,0.0));
|
||||||
r.update_status(|s| {
|
r.update_status(|s| {
|
||||||
s.updated_translate = ( - ox as f32, -oy as f32);
|
s.translate = Some((-ox as f32 * 1.35, oy as f32 * 1.35));
|
||||||
|
s.motion = RenderMotion::Translate;
|
||||||
});
|
});
|
||||||
r.queue_render();
|
r.queue_render();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
drag_detecture.connect_drag_end(clone!(
|
drag_detecture.connect_drag_end(clone!(
|
||||||
@weak render as r => move |this,_,_|{
|
@weak render as r => move |_,_,_|{
|
||||||
let t = r.imp().translate();
|
|
||||||
r.update_status(|cfg| {
|
r.update_status(|cfg| {
|
||||||
cfg.translate = t ;
|
cfg.translate = None;
|
||||||
cfg.updated_translate = (0.0,0.0);
|
cfg.motion = RenderMotion::Translate;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
r.queue_render();
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let split_view = adw::OverlaySplitView::new();
|
||||||
|
let sidebar = adw::NavigationPage::builder().title("sidebar").child(>k::ListView::builder().build()).build();
|
||||||
|
let content = adw::NavigationPage::builder().title("content").child(&render).build();
|
||||||
|
|
||||||
|
split_view.set_sidebar_position(gtk::PackType::End);
|
||||||
|
|
||||||
|
split_view.set_sidebar(Some(&sidebar));
|
||||||
|
split_view.set_content(Some(&content));
|
||||||
|
|
||||||
render.add_controller(pointer_location_detecture);
|
render.add_controller(pointer_location_detecture);
|
||||||
render.add_controller(scale_detecture);
|
render.add_controller(scale_detecture);
|
||||||
render.add_controller(drag_detecture);
|
render.add_controller(drag_detecture);
|
||||||
render.set_hexpand(true);
|
// render.set_hexpand(true);
|
||||||
render.set_vexpand(true);
|
// render.set_vexpand(true);
|
||||||
render.set_parent(&this);
|
|
||||||
|
split_view.set_parent(&this);
|
||||||
|
split_view.set_hexpand(true);
|
||||||
|
split_view.set_vexpand(true);
|
||||||
|
|
||||||
|
// paned.set_parent(&this);
|
||||||
|
// render.set_parent(&this);
|
||||||
|
// aspect_frame.set_hexpand(true);
|
||||||
|
// aspect_frame.set_vexpand(true);
|
||||||
|
// aspect_frame.set_parent(&this);
|
||||||
this.imp().renderer.replace(render);
|
this.imp().renderer.replace(render);
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_data_2d<T, Raw>(&self, data: RadarData2d<T, Raw>) -> Result<(), ProjError>
|
|
||||||
where
|
|
||||||
T: Num
|
|
||||||
+ Clone
|
|
||||||
+ PartialEq
|
|
||||||
+ PartialOrd
|
|
||||||
+ AsPrimitive<i8>
|
|
||||||
+ AsPrimitive<f64>
|
|
||||||
+ Debug
|
|
||||||
+ FromPrimitive,
|
|
||||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
|
||||||
{
|
|
||||||
let renderer = self.imp().renderer.borrow();
|
|
||||||
renderer.load_data_2d(data)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/render/cms.rs
Normal file
45
src/render/cms.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use geo_types::LineString;
|
||||||
|
|
||||||
|
use crate::coords::Mapper;
|
||||||
|
|
||||||
|
pub struct CMS {
|
||||||
|
mapper: Mapper,
|
||||||
|
window_size: (f32, f32),
|
||||||
|
bounds: (f64, f64, f64, f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CMS {
|
||||||
|
pub fn new(mapper: Mapper, window_size: (f32, f32)) -> Self {
|
||||||
|
let bounds = mapper.get_bounds();
|
||||||
|
Self {
|
||||||
|
mapper,
|
||||||
|
window_size,
|
||||||
|
bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
||||||
|
self.mapper.map(loc).ok().map(|(x, y)| {
|
||||||
|
// println!("x: {}, y: {}", x, y);
|
||||||
|
let (w, h) = self.window_size;
|
||||||
|
let (w, h) = (w as f64, h as f64);
|
||||||
|
let (x, y) = (x - self.bounds.0, y - self.bounds.2);
|
||||||
|
let (x, y) = (
|
||||||
|
x / (self.bounds.1 - self.bounds.0),
|
||||||
|
1.0 - y / (self.bounds.3 - self.bounds.2),
|
||||||
|
);
|
||||||
|
let (x, y) = (x * w, y * h);
|
||||||
|
(x as f32, y as f32)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ring_map(&self, line: &LineString) -> Option<LineString<f32>> {
|
||||||
|
Some(
|
||||||
|
line.points()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| self.map((p.x(), p.y())).unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,40 +0,0 @@
|
|||||||
mod imp;
|
|
||||||
use crate::coords::{Mapper, Range};
|
|
||||||
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
|
||||||
use geo_types::{line_string, LineString, Point};
|
|
||||||
use glib::subclass::types::ObjectSubclassIsExt;
|
|
||||||
use std::cell::Ref;
|
|
||||||
|
|
||||||
use super::imp::{RenderStatus, RenderConfig};
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct ExteriorWidget(ObjectSubclass<imp::ExteriorWidget>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ExteriorWidget {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExteriorWidget {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let this: Self = glib::Object::new();
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn draw(&self, canvas: &mut Canvas<OpenGl>, mapper: Ref<'_, Mapper>, status:Ref<'_, RenderStatus>, _c:Ref<'_, RenderConfig>) {
|
|
||||||
let imp = self.imp();
|
|
||||||
let canvas_width = canvas.width();
|
|
||||||
let canvas_height = canvas.height();
|
|
||||||
let padding = [30.0,30.0,30.0,30.0];
|
|
||||||
|
|
||||||
let w = canvas_width - padding[1] - padding[3];
|
|
||||||
let h = canvas_height - padding[0] - padding[2];
|
|
||||||
|
|
||||||
let paint = Paint::color(Color::white());
|
|
||||||
let mut path = Path::new();
|
|
||||||
path.rect(padding[3], padding[0], w, h);
|
|
||||||
canvas.stroke_path(&path, &paint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,193 +0,0 @@
|
|||||||
mod imp;
|
|
||||||
use crate::coords::Mapper;
|
|
||||||
use femtovg::{renderer::OpenGl, Canvas, Path};
|
|
||||||
use femtovg::{Color, FontId, ImageFlags, ImageId, Paint};
|
|
||||||
use geo_types::{LineString, Point};
|
|
||||||
use glib::subclass::types::ObjectSubclassIsExt;
|
|
||||||
use gtk::glib;
|
|
||||||
use ndarray::{s, Array2, Axis, Zip};
|
|
||||||
use std::cell::Ref;
|
|
||||||
|
|
||||||
pub use self::imp::ForegroundConfig;
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct ForegroundWidget(ObjectSubclass<imp::ForegroundWidget>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ForegroundWidget {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(ForegroundConfig::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ForegroundWidget {
|
|
||||||
pub fn new(config: ForegroundConfig) -> Self {
|
|
||||||
let this: Self = glib::Object::new();
|
|
||||||
let imp = this.imp();
|
|
||||||
imp.config.replace(config);
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, scale: f32, dpi: i32, mapper: Ref<'_, Mapper>) {
|
|
||||||
let canvas_width = canvas.width();
|
|
||||||
let canvas_height = canvas.height();
|
|
||||||
let config = self.imp().config.borrow();
|
|
||||||
|
|
||||||
let colors = self.imp().color.borrow();
|
|
||||||
let data = self.imp().data.borrow();
|
|
||||||
|
|
||||||
if self.imp().image.borrow().is_none() {
|
|
||||||
println!("rebuild image");
|
|
||||||
let img_id = canvas
|
|
||||||
.create_image_empty(
|
|
||||||
canvas_width as usize,
|
|
||||||
canvas_height as usize,
|
|
||||||
femtovg::PixelFormat::Rgb8,
|
|
||||||
ImageFlags::empty(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
if let Ok(v) = canvas.image_size(img_id) {
|
|
||||||
println!("create image: {:?}", v);
|
|
||||||
canvas.set_render_target(femtovg::RenderTarget::Image(img_id));
|
|
||||||
let colors = self.imp().color.borrow();
|
|
||||||
let data = self.imp().data.borrow();
|
|
||||||
if colors.is_some() && data.is_some() {
|
|
||||||
for (i, c) in colors
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.t()
|
|
||||||
.iter()
|
|
||||||
.zip(data.as_ref().unwrap().iter())
|
|
||||||
{
|
|
||||||
if i.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let pts = c.points().collect::<Vec<Point>>();
|
|
||||||
let first_point = pts[0];
|
|
||||||
let mut path = Path::new();
|
|
||||||
path.move_to(
|
|
||||||
first_point.x() as f32 * v.0 as f32 + 0.8,
|
|
||||||
first_point.y() as f32 * v.1 as f32 + 0.8,
|
|
||||||
);
|
|
||||||
|
|
||||||
pts[1..].into_iter().for_each(|p| {
|
|
||||||
path.line_to(p.x() as f32 * v.0 as f32, p.y() as f32 * v.1 as f32)
|
|
||||||
});
|
|
||||||
let c = i.unwrap();
|
|
||||||
canvas.fill_path(&path, &Paint::color(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.imp().image.replace(Some(img_id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.set_render_target(femtovg::RenderTarget::Screen);
|
|
||||||
|
|
||||||
let mut path = Path::new();
|
|
||||||
path.rect(0.0, 0.0, canvas_width, canvas_height);
|
|
||||||
|
|
||||||
canvas.fill_path(
|
|
||||||
&path,
|
|
||||||
&Paint::image(
|
|
||||||
self.imp().image.borrow().as_ref().unwrap().to_owned(),
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
canvas_width,
|
|
||||||
canvas_height,
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn set_dims(&self, dims: (Array2<f64>, Array2<f64>)) {
|
|
||||||
self.imp().dim1.replace(Some(dims.0));
|
|
||||||
self.imp().dim2.replace(Some(dims.1));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn set_image(&self, image: ImageId) {
|
|
||||||
self.imp().image.replace(Some(image));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_colors(&self, colors: Array2<Option<Color>>) {
|
|
||||||
self.imp().color.replace(Some(colors));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_data(&self, data: Array2<LineString>) {
|
|
||||||
self.imp().data.replace(Some(data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
|
|
||||||
// let colors = vec![
|
|
||||||
// Color::rgb(0, 172, 164),
|
|
||||||
// Color::rgb(192, 192, 254),
|
|
||||||
// Color::rgb(122, 114, 238),
|
|
||||||
// Color::rgb(30, 38, 208),
|
|
||||||
// Color::rgb(166, 252, 168),
|
|
||||||
// Color::rgb(0, 234, 0),
|
|
||||||
// Color::rgb(16, 146, 26),
|
|
||||||
// Color::rgb(252, 244, 100),
|
|
||||||
// Color::rgb(200, 200, 2),
|
|
||||||
// Color::rgb(140, 140, 0),
|
|
||||||
// Color::rgb(254, 172, 172),
|
|
||||||
// Color::rgb(254, 100, 92),
|
|
||||||
// Color::rgb(238, 2, 48),
|
|
||||||
// Color::rgb(212, 142, 254),
|
|
||||||
// Color::rgb(170, 36, 250),
|
|
||||||
// ];
|
|
||||||
|
|
||||||
// Zip::from(c).and(d).and(e).for_each(|lon, lat, d| {
|
|
||||||
// if *d < -5 || *d > 70 {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let left_bottom = mapper.map((*lon, *lat)).unwrap();
|
|
||||||
// let right_top = mapper.map((lon + 0.005, lat + 0.005)).unwrap();
|
|
||||||
|
|
||||||
// // let (lon1, lat1) = (
|
|
||||||
// // left_bottom.0 * canvas_width as f64,
|
|
||||||
// // left_bottom.1 * canvas_height as f64,
|
|
||||||
// // );
|
|
||||||
|
|
||||||
// let color = get(&levels, &colors, *d);
|
|
||||||
// //
|
|
||||||
// // let (lon2, lat2) = (
|
|
||||||
// // right_top.0 * canvas_width as f64,
|
|
||||||
// // right_top.1 * canvas_height as f64,
|
|
||||||
// // );
|
|
||||||
|
|
||||||
// let line_string: LineString = vec![
|
|
||||||
// (left_bottom.0, left_bottom.1),
|
|
||||||
// (right_top.0, left_bottom.1),
|
|
||||||
// (right_top.0, right_top.1),
|
|
||||||
// (left_bottom.0, right_top.1),
|
|
||||||
// (left_bottom.0, left_bottom.1),
|
|
||||||
// ]
|
|
||||||
// .into();
|
|
||||||
|
|
||||||
// // let line_string: LineString = vec![
|
|
||||||
// // (lon1, lat1),
|
|
||||||
// // (lon2, lat1),
|
|
||||||
// // (lon2, lat2),
|
|
||||||
// // (lon1, lat2),
|
|
||||||
// // (lon1, lat1),
|
|
||||||
// // ]
|
|
||||||
// // .into();
|
|
||||||
|
|
||||||
// let res = mapper.ring_map(&line_string).unwrap();
|
|
||||||
|
|
||||||
// let mut path = Path::new();
|
|
||||||
|
|
||||||
// path.move_to(
|
|
||||||
// left_bottom.0 as f32 * canvas_width,
|
|
||||||
// left_bottom.1 as f32 * canvas_height,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// for p in line_string.points() {
|
|
||||||
// // path.move_to(p.x() as f32 * canvas_width, p.y() as f32 * canvas_height);
|
|
||||||
// path.line_to(p.x() as f32 * canvas_width, p.y() as f32 * canvas_height);
|
|
||||||
// }
|
|
||||||
// canvas.fill_path(&path, &Paint::color(color));
|
|
||||||
// });
|
|
||||||
@ -1,13 +1,10 @@
|
|||||||
use super::background::BackgroundWidget;
|
use super::layer::foreground::exterior::ExteriorWidget;
|
||||||
use super::exterior::ExteriorWidget;
|
use super::layer::foreground::interior::InteriorWidget;
|
||||||
use super::interior::InteriorWidget;
|
|
||||||
use super::foreground::ForegroundWidget;
|
|
||||||
use super::WindowCoord;
|
use super::WindowCoord;
|
||||||
use crate::coords::proj::Mercator;
|
use crate::coords::proj::Mercator;
|
||||||
use crate::coords::Mapper;
|
use crate::coords::Mapper;
|
||||||
use femtovg::{Color, FontId, Paint, Path, Transform2D};
|
use femtovg::{Color, FontId, Paint, Path, Transform2D};
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
use gtk::prelude::WidgetExtManual;
|
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
use gtk::traits::{GLAreaExt, WidgetExt};
|
use gtk::traits::{GLAreaExt, WidgetExt};
|
||||||
use ndarray::Array2;
|
use ndarray::Array2;
|
||||||
@ -16,18 +13,32 @@ use std::num::NonZeroU32;
|
|||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct RenderConfig {
|
pub struct RenderConfig {
|
||||||
pub dim1: Option<Array2<f64>>,
|
|
||||||
pub dim2: Option<Array2<f64>>,
|
|
||||||
pub padding: [f32; 4],
|
pub padding: [f32; 4],
|
||||||
// pub pointer_lon_lat: (f64, f64),
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum RenderMotion {
|
||||||
|
Translate,
|
||||||
|
Scale,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RenderMotion {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct RenderStatus{
|
pub struct RenderStatus {
|
||||||
pub scale: f32,
|
pub window_size: (i32, i32),
|
||||||
|
pub(super) scale_rate: Option<f64>,
|
||||||
|
translation: Option<(f64, f64)>,
|
||||||
|
init_translation: (f64, f64),
|
||||||
pub pointer_location: WindowCoord,
|
pub pointer_location: WindowCoord,
|
||||||
pub translate: WindowCoord,
|
pub motion: RenderMotion,
|
||||||
pub updated_translate: WindowCoord,
|
pub scale: f32,
|
||||||
|
pub translate: Option<(f32, f32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Fonts {
|
struct Fonts {
|
||||||
@ -52,7 +63,7 @@ impl Default for Render {
|
|||||||
interior: RefCell::new(InteriorWidget::default()),
|
interior: RefCell::new(InteriorWidget::default()),
|
||||||
config: RefCell::new(RenderConfig::default()),
|
config: RefCell::new(RenderConfig::default()),
|
||||||
status: RefCell::new(RenderStatus::default()),
|
status: RefCell::new(RenderStatus::default()),
|
||||||
mapper: RefCell::new(Mercator::new().into()),
|
mapper: RefCell::new(Mercator::default().into()),
|
||||||
canvas: RefCell::new(None),
|
canvas: RefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,10 +82,6 @@ impl ObjectImpl for Render {
|
|||||||
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);
|
||||||
// area.add_tick_callback(|area, _| {
|
|
||||||
// area.queue_render();
|
|
||||||
// glib::Continue(true)
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +90,7 @@ impl WidgetImpl for Render {}
|
|||||||
|
|
||||||
impl GLAreaImpl for Render {
|
impl GLAreaImpl for Render {
|
||||||
fn resize(&self, width: i32, height: i32) {
|
fn resize(&self, width: i32, height: i32) {
|
||||||
|
self.status.borrow_mut().window_size = (width, height);
|
||||||
self.ensure_canvas();
|
self.ensure_canvas();
|
||||||
let mut canvas = self.canvas.borrow_mut();
|
let mut canvas = self.canvas.borrow_mut();
|
||||||
let canvas = canvas.as_mut().unwrap();
|
let canvas = canvas.as_mut().unwrap();
|
||||||
@ -91,16 +99,33 @@ impl GLAreaImpl for Render {
|
|||||||
height as u32,
|
height as u32,
|
||||||
self.obj().scale_factor() as f32,
|
self.obj().scale_factor() as f32,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let translation = self.status.borrow().translation;
|
||||||
|
let scale_rate = self.status.borrow().scale_rate;
|
||||||
|
let mapper = self.mapper.borrow();
|
||||||
|
|
||||||
|
if let None = translation {
|
||||||
|
let mut status = self.status.borrow_mut();
|
||||||
|
status.translation = Some((mapper.get_bounds().0, mapper.get_bounds().2));
|
||||||
|
status.init_translation = (mapper.get_bounds().0, mapper.get_bounds().2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let None = scale_rate {
|
||||||
|
let scale = (mapper.get_bounds().3 - mapper.get_bounds().2) / canvas.height() as f64;
|
||||||
|
self.status.borrow_mut().scale_rate = Some(scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, context: >k::gdk::GLContext) -> bool {
|
fn render(&self, context: >k::gdk::GLContext) -> bool {
|
||||||
self.ensure_canvas();
|
self.ensure_canvas();
|
||||||
|
|
||||||
let mut canvas = self.canvas.borrow_mut();
|
let mut canvas = self.canvas.borrow_mut();
|
||||||
let canvas = canvas.as_mut().unwrap();
|
let canvas = canvas.as_mut().unwrap();
|
||||||
|
|
||||||
let dpi = self.obj().scale_factor();
|
let dpi = self.obj().scale_factor();
|
||||||
let w = canvas.width();
|
let w = canvas.width();
|
||||||
let h = canvas.height();
|
let h = canvas.height();
|
||||||
|
|
||||||
let configs = self.config.borrow();
|
let configs = self.config.borrow();
|
||||||
|
|
||||||
canvas.clear_rect(
|
canvas.clear_rect(
|
||||||
@ -111,34 +136,44 @@ impl GLAreaImpl for Render {
|
|||||||
Color::rgba(0, 0, 0, 255),
|
Color::rgba(0, 0, 0, 255),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mapper = self.mapper.borrow();
|
{
|
||||||
|
let mut status = self.status.borrow_mut();
|
||||||
|
match status.motion {
|
||||||
|
RenderMotion::Translate => {
|
||||||
|
if let Some((x, y)) = status.translate {
|
||||||
|
let (ix, iy) = status.init_translation;
|
||||||
|
status.translation = Some((
|
||||||
|
ix + x as f64 * status.scale_rate.unwrap(),
|
||||||
|
iy + y as f64 * status.scale_rate.unwrap(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
status.init_translation = status.translation.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RenderMotion::Scale => {
|
||||||
|
let scale_rate = status.scale_rate.unwrap();
|
||||||
|
let scale_flag = status.scale as f64;
|
||||||
|
let step = scale_rate * 0.1;
|
||||||
|
|
||||||
self.interior.borrow().draw(canvas, mapper, self.status.borrow(), configs);
|
let (tx, ty) = status.translation.unwrap();
|
||||||
|
let (px, py) = status.pointer_location;
|
||||||
|
|
||||||
// let (lon_range, lat_range) = mapper.range.clone();
|
let scaled = scale_rate + scale_flag * step;
|
||||||
// {
|
status.scale_rate = Some(scaled);
|
||||||
// let background_widget = self.background.borrow_mut();
|
let sx = scale_flag * step * px as f64;
|
||||||
// background_widget.set_lat_lines(lat_range.key_points(9));
|
let sy = scale_flag * step * py as f64;
|
||||||
// }
|
status.translation = Some((tx - sx, ty - sy));
|
||||||
// {
|
status.init_translation = status.translation.unwrap();
|
||||||
// let background_widget = self.background.borrow_mut();
|
}
|
||||||
// background_widget.set_lon_lines(lon_range.key_points(20));
|
RenderMotion::None => {}
|
||||||
// }
|
}
|
||||||
// let translate = self.translate();
|
}
|
||||||
|
|
||||||
// self.exterior.borrow().draw(canvas,self.mapper.borrow());
|
self.interior
|
||||||
// self.foreground
|
.borrow()
|
||||||
// .borrow()
|
.draw(canvas, &self.obj(), self.status.borrow(), configs);
|
||||||
// .draw(canvas, configs.scale, dpi, self.mapper.borrow());
|
|
||||||
|
|
||||||
// self.background.borrow().draw(
|
self.exterior.borrow().draw(canvas, &self.obj());
|
||||||
// canvas,
|
|
||||||
// configs.scale,
|
|
||||||
// dpi,
|
|
||||||
// translate,
|
|
||||||
// self.mapper.borrow(),
|
|
||||||
// (lon_range, lat_range),
|
|
||||||
// );
|
|
||||||
canvas.flush();
|
canvas.flush();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -178,11 +213,74 @@ impl Render {
|
|||||||
self.canvas.replace(Some(canvas));
|
self.canvas.replace(Some(canvas));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn translate(&self) -> WindowCoord {
|
pub(super) fn window_size(&self) -> Option<(i32, i32)> {
|
||||||
let cfg = self.status.borrow();
|
Some(self.status.borrow().window_size)
|
||||||
let (rx, ry) = cfg.translate;
|
}
|
||||||
let (ux, uy) = cfg.updated_translate;
|
|
||||||
|
|
||||||
return (rx + ux, ry + uy);
|
pub(super) fn window_range(&self) -> Option<((f64, f64), (f64, f64))> {
|
||||||
|
let padding = self.padding();
|
||||||
|
let (w, h) = self.window_size().unwrap();
|
||||||
|
let (w, h) = (
|
||||||
|
w as f32 - padding[1] - padding[3],
|
||||||
|
h as f32 - padding[0] - padding[2],
|
||||||
|
);
|
||||||
|
let (w, h) = (w as f64, h as f64);
|
||||||
|
|
||||||
|
let mapper = self.mapper.borrow();
|
||||||
|
|
||||||
|
let status = self.status.borrow();
|
||||||
|
status.translation.and_then(|(tx, ty)| {
|
||||||
|
status.scale_rate.and_then(|scale| {
|
||||||
|
let (x1, y1) = (tx + padding[3] as f64, ty + padding[2] as f64);
|
||||||
|
let (x2, y2) = (tx + w * scale + padding[3] as f64, ty + h * scale + padding[2] as f64);
|
||||||
|
let (x1, y1) = mapper.inverse_map((x1, y1)).unwrap();
|
||||||
|
let (x2, y2) = mapper.inverse_map((x2, y2)).unwrap();
|
||||||
|
Some(((x1, x2), (y1, y2)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn inverse_map(&self, loc: (f32, f32)) -> Option<(f64, f64)> {
|
||||||
|
let (x, y) = loc;
|
||||||
|
// let (_, h) = self.window_size().unwrap();
|
||||||
|
let status = self.status.borrow();
|
||||||
|
status.translation.and_then(|(tx, ty)| {
|
||||||
|
status.scale_rate.and_then(|scale| {
|
||||||
|
let (x, y) = (x as f64, y as f64);
|
||||||
|
Some((tx + x * scale, (ty + y * scale)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn padding(&self) -> [f32; 4] {
|
||||||
|
self.config.borrow().padding
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
||||||
|
// let padding = self.padding();
|
||||||
|
// let left_padding = padding[3];
|
||||||
|
// let bottom_padding = padding[2];
|
||||||
|
let (x, y) = loc;
|
||||||
|
let (_, h) = self.window_size().unwrap();
|
||||||
|
let status = self.status.borrow();
|
||||||
|
status.translation.and_then(|(tx, ty)| {
|
||||||
|
status.scale_rate.and_then(|scale| {
|
||||||
|
Some((
|
||||||
|
(x - tx as f64) as f32 / scale as f32,
|
||||||
|
h as f32 - (y - ty as f64) as f32 / scale as f32,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_translation(&self, translation: (f64, f64)) {
|
||||||
|
let mut status = self.status.borrow_mut();
|
||||||
|
status.translation = Some(translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_loc(&self) -> (f32, f32) {
|
||||||
|
let (x, y) = self.status.borrow().pointer_location.clone();
|
||||||
|
let (_, h) = self.window_size().unwrap();
|
||||||
|
(x, h as f32 - y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
mod imp;
|
|
||||||
use crate::coords::{Mapper, Range};
|
|
||||||
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
|
||||||
use geo_types::{line_string, LineString, Point};
|
|
||||||
use glib::subclass::types::ObjectSubclassIsExt;
|
|
||||||
use std::cell::Ref;
|
|
||||||
|
|
||||||
use super::imp::{RenderConfig, RenderStatus};
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct InteriorWidget(ObjectSubclass<imp::InteriorWidget>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for InteriorWidget {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InteriorWidget {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let this: Self = glib::Object::new();
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn draw(
|
|
||||||
&self,
|
|
||||||
canvas: &mut Canvas<OpenGl>,
|
|
||||||
mapper: Ref<'_, Mapper>,
|
|
||||||
status: Ref<'_, RenderStatus>,
|
|
||||||
_c: Ref<'_, RenderConfig>,
|
|
||||||
) {
|
|
||||||
let imp = self.imp();
|
|
||||||
let canvas_width = canvas.width();
|
|
||||||
let canvas_height = canvas.height();
|
|
||||||
|
|
||||||
let padding = _c.padding;
|
|
||||||
|
|
||||||
let w = canvas_width - padding[1] - padding[3];
|
|
||||||
let h = canvas_height - padding[0] - padding[2];
|
|
||||||
let scale = status.scale;
|
|
||||||
let dpi = 2;
|
|
||||||
|
|
||||||
let (tx, ty) = status.translate;
|
|
||||||
let range = mapper.range.clone();
|
|
||||||
|
|
||||||
self.imp().background.borrow().draw(
|
|
||||||
canvas,
|
|
||||||
scale,
|
|
||||||
dpi,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
(tx + padding[3], ty + padding[0]),
|
|
||||||
mapper,
|
|
||||||
range,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -80,7 +80,7 @@ impl BackgroundWidget {
|
|||||||
.map(|p| {
|
.map(|p| {
|
||||||
(
|
(
|
||||||
p.x() as f32 * canvas_width * scale - translate.0,
|
p.x() as f32 * canvas_width * scale - translate.0,
|
||||||
(1.0 - p.y() as f32) * canvas_height * scale - translate.1,
|
p.y() as f32 * canvas_height * scale - translate.1,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
@ -136,7 +136,7 @@ impl BackgroundWidget {
|
|||||||
.map(|p| {
|
.map(|p| {
|
||||||
(
|
(
|
||||||
p.x() as f32 * canvas_width * scale - translate.0,
|
p.x() as f32 * canvas_width * scale - translate.0,
|
||||||
(1.0 - p.y()) as f32 * canvas_height * scale - translate.1,
|
p.y() as f32 * canvas_height * scale - translate.1,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
@ -222,7 +222,7 @@ impl BackgroundWidget {
|
|||||||
let (x, y) = mapper.map(point).unwrap();
|
let (x, y) = mapper.map(point).unwrap();
|
||||||
(
|
(
|
||||||
x as f32 * canvas_width * scale - translate.0,
|
x as f32 * canvas_width * scale - translate.0,
|
||||||
(1.0 - y as f32) * canvas_height * scale - translate.1,
|
y as f32 * canvas_height * scale - translate.1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
89
src/render/layer/foreground/exterior/mod.rs
Normal file
89
src/render/layer/foreground/exterior/mod.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
mod imp;
|
||||||
|
use crate::{coords::Range, render::Render};
|
||||||
|
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
||||||
|
use glib::subclass::types::ObjectSubclassIsExt;
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct ExteriorWidget(ObjectSubclass<imp::ExteriorWidget>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ExteriorWidget {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExteriorWidget {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let this: Self = glib::Object::new();
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render) {
|
||||||
|
let padding = render.imp().config.borrow().padding;
|
||||||
|
let (w, h) = render.window_size();
|
||||||
|
let (w, h) = (w as f32, h as f32);
|
||||||
|
let origins = [
|
||||||
|
(0.0, 0.0),
|
||||||
|
(w - padding[1], 0.0),
|
||||||
|
(0.0, h - padding[2]),
|
||||||
|
(0.0, 0.0),
|
||||||
|
];
|
||||||
|
let whs = [
|
||||||
|
(w, padding[0]),
|
||||||
|
(padding[1], h),
|
||||||
|
(w, padding[2]),
|
||||||
|
(padding[3], h),
|
||||||
|
];
|
||||||
|
|
||||||
|
let painter = Paint::color(Color::rgb(0, 0, 0));
|
||||||
|
|
||||||
|
for edge in 0..4 {
|
||||||
|
let mut path = Path::new();
|
||||||
|
let (ox, oy) = origins[edge];
|
||||||
|
let (w, h) = whs[edge];
|
||||||
|
path.rect(ox, oy, w, h);
|
||||||
|
canvas.fill_path(&path, &painter);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (lon_range, lat_range) = render.render_range();
|
||||||
|
let (lon_range, lat_range): (Range, Range) = (lon_range.into(), lat_range.into());
|
||||||
|
|
||||||
|
let lon_keypoints = lon_range.key_points(10);
|
||||||
|
let lat_keypoints = lat_range.key_points(5);
|
||||||
|
let mut paint = Paint::color(Color::white()); // 黑色字体
|
||||||
|
paint.set_font_size(25.0);
|
||||||
|
|
||||||
|
for lon in lon_keypoints.iter() {
|
||||||
|
let (x, _) = render.map((*lon, lat_range.0)).unwrap();
|
||||||
|
|
||||||
|
let text = format!("{:.2}", lon);
|
||||||
|
let metrics = canvas
|
||||||
|
.measure_text(x, 0.0, text.as_str(), &paint)
|
||||||
|
.expect("Cannot measure text");
|
||||||
|
|
||||||
|
let text_x = x - metrics.width() / 2.0;
|
||||||
|
let text_y = h - metrics.height() / 2.0;
|
||||||
|
|
||||||
|
canvas
|
||||||
|
.fill_text(text_x, text_y, text.as_str(), &paint)
|
||||||
|
.expect("Cannot draw text");
|
||||||
|
}
|
||||||
|
|
||||||
|
for lat in lat_keypoints.iter() {
|
||||||
|
let (_, y) = render.map((lon_range.0, *lat)).unwrap();
|
||||||
|
|
||||||
|
let text = format!("{:.2}", lat);
|
||||||
|
let metrics = canvas
|
||||||
|
.measure_text(0.0, y, text.as_str(), &paint)
|
||||||
|
.expect("Cannot measure text");
|
||||||
|
|
||||||
|
let text_x = 0.0;
|
||||||
|
let text_y = y - metrics.height() / 2.0;
|
||||||
|
|
||||||
|
canvas
|
||||||
|
.fill_text(text_x, text_y, text.as_str(), &paint)
|
||||||
|
.expect("Cannot draw text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,12 @@
|
|||||||
use crate::render::{imp, WindowCoord};
|
use crate::render::{imp, WindowCoord};
|
||||||
use femtovg::{ImageId, Paint, Color};
|
use femtovg::{Color, ImageId, Paint, RenderTarget};
|
||||||
use geo_macros::Prj;
|
use geo_macros::Prj;
|
||||||
use geo_types::LineString;
|
use geo_types::LineString;
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
use ndarray::Array2;
|
use ndarray::Array2;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Prj, Default)]
|
#[derive(Prj, Default)]
|
||||||
pub struct ForegroundConfig {
|
pub struct ForegroundConfig {
|
||||||
@ -1,14 +1,20 @@
|
|||||||
|
use crate::render::layer::background::BackgroundWidget;
|
||||||
|
use crate::render::layer::foreground::ForegroundWidget;
|
||||||
|
use crate::render::{imp, WindowCoord};
|
||||||
|
use femtovg::RenderTarget;
|
||||||
use gtk::glib;
|
use gtk::glib;
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use crate::render::background::BackgroundWidget;
|
|
||||||
use crate::render::foreground::ForegroundWidget;
|
|
||||||
|
|
||||||
|
use super::layers::Layer;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct InteriorWidget {
|
pub struct InteriorWidget {
|
||||||
pub(super) background: RefCell<BackgroundWidget>,
|
pub(super) background: RefCell<BackgroundWidget>,
|
||||||
pub(super) foreground: RefCell<ForegroundWidget>,
|
pub(super) foreground: RefCell<ForegroundWidget>,
|
||||||
|
pub(super) trans: RefCell<WindowCoord>,
|
||||||
|
pub(super) update_trans: RefCell<WindowCoord>,
|
||||||
|
pub(super) layers: RefCell<Vec<Layer>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
@ -17,4 +23,4 @@ impl ObjectSubclass for InteriorWidget {
|
|||||||
type Type = super::InteriorWidget;
|
type Type = super::InteriorWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for InteriorWidget{}
|
impl ObjectImpl for InteriorWidget {}
|
||||||
107
src/render/layer/foreground/interior/layers.rs
Normal file
107
src/render/layer/foreground/interior/layers.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use geojson::GeoJson;
|
||||||
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
cell::{Ref, RefCell}, fmt::Debug,
|
||||||
|
};
|
||||||
|
use topojson::TopoJson;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
coords::{Coord, Mapper, Range},
|
||||||
|
render::{renders::DataRenderer, Render},
|
||||||
|
};
|
||||||
|
use femtovg::{renderer::OpenGl, Canvas, ImageId};
|
||||||
|
|
||||||
|
pub struct Layer {
|
||||||
|
pub visiable: bool,
|
||||||
|
target: RefCell<Option<Target>>,
|
||||||
|
imp: RefCell<Option<Box<dyn LayerImpl>>>,
|
||||||
|
draw: Box<dyn Fn(&Self, &mut Canvas<OpenGl>, &Render, (f32, f32))>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Layer {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Layer")
|
||||||
|
.field("visiable", &self.visiable)
|
||||||
|
.field("target", &self.target)
|
||||||
|
.field("imp", &self.imp)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LayerImpl:Debug {
|
||||||
|
fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render) -> Option<Target>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layer {
|
||||||
|
pub fn new<
|
||||||
|
F: 'static + Fn(&Self, &mut Canvas<OpenGl>, &Render, (f32, f32)),
|
||||||
|
IMP: LayerImpl + 'static,
|
||||||
|
>(
|
||||||
|
visiable: bool,
|
||||||
|
draw: F,
|
||||||
|
imp: Option<IMP>,
|
||||||
|
) -> Self {
|
||||||
|
Layer {
|
||||||
|
visiable,
|
||||||
|
target: RefCell::new(None),
|
||||||
|
draw: Box::new(draw),
|
||||||
|
imp: RefCell::new(imp.map(|x| Box::new(x) as Box<dyn LayerImpl>)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render, window_size: (f32, f32)) {
|
||||||
|
if self.visiable {
|
||||||
|
(self.draw)(self, canvas, render, window_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_render_target(&self, target: Target) {
|
||||||
|
*self.target.borrow_mut() = Some(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_target(&self) -> Option<Target> {
|
||||||
|
self.target.borrow().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_imp(&self) -> Ref<Option<Box<dyn LayerImpl>>> {
|
||||||
|
let im = self.imp.borrow();
|
||||||
|
im
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Target {
|
||||||
|
pub target: ImageId,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
bounds: (Range, Range),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Target {
|
||||||
|
pub fn new(target: ImageId, width: f32, height: f32, bounds: (Range, Range)) -> Self {
|
||||||
|
Self {
|
||||||
|
target,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self, render: &Render) -> (f32, f32) {
|
||||||
|
let (x, y) = self.bounds;
|
||||||
|
|
||||||
|
let p1 = (x.0, y.0);
|
||||||
|
let p2 = (x.1, y.1);
|
||||||
|
|
||||||
|
let (x1, y1) = render.map(p1).unwrap();
|
||||||
|
let (x2, y2) = render.map(p2).unwrap();
|
||||||
|
|
||||||
|
((x2 - x1).abs(), (y2 - y1).abs())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn origin(&self, render: &Render) -> (f32, f32) {
|
||||||
|
let (x, y) = self.bounds;
|
||||||
|
let p1 = (x.0, y.1);
|
||||||
|
render.map(p1).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/render/layer/foreground/interior/mod.rs
Normal file
56
src/render/layer/foreground/interior/mod.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
mod imp;
|
||||||
|
mod layers;
|
||||||
|
use crate::coords::{Mapper, Range};
|
||||||
|
use crate::data::Npz;
|
||||||
|
use crate::data::{DataLoader, RadarData2d};
|
||||||
|
use crate::render::predefined::color_mapper::BoundaryNorm;
|
||||||
|
use crate::render::predefined::gis::MapRender;
|
||||||
|
use crate::render::renders::DataRenderer;
|
||||||
|
use crate::render::Render;
|
||||||
|
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
||||||
|
use glib::subclass::types::ObjectSubclassIsExt;
|
||||||
|
pub use layers::{Layer, LayerImpl, Target};
|
||||||
|
use ndarray::OwnedRepr;
|
||||||
|
use std::cell::Ref;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use crate::render::imp::{RenderConfig, RenderStatus};
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct InteriorWidget(ObjectSubclass<imp::InteriorWidget>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InteriorWidget {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteriorWidget {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let this: Self = glib::Object::new();
|
||||||
|
this.imp().layers.replace(vec![
|
||||||
|
// Layer::grid_render_layer_with_path(
|
||||||
|
// "/users/tsuki/projects/radar-g/test2.npz",
|
||||||
|
// Npz,
|
||||||
|
// BoundaryNorm::default(),
|
||||||
|
// )
|
||||||
|
]);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(
|
||||||
|
&self,
|
||||||
|
canvas: &mut Canvas<OpenGl>,
|
||||||
|
render: &Render,
|
||||||
|
status: Ref<'_, RenderStatus>,
|
||||||
|
_c: Ref<'_, RenderConfig>,
|
||||||
|
) {
|
||||||
|
let mut layers = self.imp().layers.borrow_mut();
|
||||||
|
|
||||||
|
let (x, y) = (canvas.width(), canvas.height());
|
||||||
|
for layer in layers.iter_mut().filter(|x| x.visiable) {
|
||||||
|
layer.draw(canvas, &render, (x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/render/layer/foreground/mod.rs
Normal file
34
src/render/layer/foreground/mod.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
pub mod exterior;
|
||||||
|
mod imp;
|
||||||
|
pub mod interior;
|
||||||
|
use crate::coords::Mapper;
|
||||||
|
use femtovg::{renderer::OpenGl, Canvas, Path};
|
||||||
|
use glib::subclass::types::ObjectSubclassIsExt;
|
||||||
|
use gtk::glib;
|
||||||
|
use std::cell::Ref;
|
||||||
|
|
||||||
|
pub use self::imp::ForegroundConfig;
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct ForegroundWidget(ObjectSubclass<imp::ForegroundWidget>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ForegroundWidget {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(ForegroundConfig::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ForegroundWidget {
|
||||||
|
pub fn new(config: ForegroundConfig) -> Self {
|
||||||
|
let this: Self = glib::Object::new();
|
||||||
|
let imp = this.imp();
|
||||||
|
imp.config.replace(config);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, scale: f32, dpi: i32, mapper: Ref<'_, Mapper>) {
|
||||||
|
let config = self.imp().config.borrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
2
src/render/layer/mod.rs
Normal file
2
src/render/layer/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod background;
|
||||||
|
pub mod foreground;
|
||||||
@ -1,23 +1,22 @@
|
|||||||
mod background;
|
mod cms;
|
||||||
mod exterior;
|
|
||||||
mod foreground;
|
|
||||||
mod imp;
|
mod imp;
|
||||||
mod interior;
|
mod layer;
|
||||||
|
mod predefined;
|
||||||
|
mod renders;
|
||||||
|
use self::cms::CMS;
|
||||||
|
pub use self::imp::{RenderConfig, RenderMotion, RenderStatus};
|
||||||
|
pub use self::layer::background::{BackgroundConfig, BackgroundWidget};
|
||||||
|
pub use self::layer::foreground::{ForegroundConfig, ForegroundWidget};
|
||||||
use crate::coords::Mapper;
|
use crate::coords::Mapper;
|
||||||
use crate::data::RadarData2d;
|
use adw::prelude::{GLAreaExt, GestureDragExt};
|
||||||
use crate::pipeline::ProjPipe;
|
use geo_types::LineString;
|
||||||
use crate::pipeline::{Pipeline, ShadePipe};
|
use glib::clone;
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
pub use self::background::{BackgroundConfig, BackgroundWidget};
|
|
||||||
pub use self::foreground::{ForegroundConfig, ForegroundWidget};
|
|
||||||
use self::imp::{RenderConfig, RenderStatus};
|
|
||||||
use adw::prelude::WidgetExt;
|
|
||||||
use femtovg::Color;
|
|
||||||
pub use glib::subclass::prelude::*;
|
pub use glib::subclass::prelude::*;
|
||||||
use ndarray::{self, Array2};
|
use gtk::traits::WidgetExt;
|
||||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
use gtk::{EventControllerScrollFlags, Inhibit};
|
||||||
use proj::ProjError;
|
pub use layer::foreground::interior::Layer;
|
||||||
|
use std::cell::Ref;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
|
||||||
pub(super) type WindowCoord = (f32, f32);
|
pub(super) type WindowCoord = (f32, f32);
|
||||||
|
|
||||||
@ -28,21 +27,74 @@ glib::wrapper! {
|
|||||||
|
|
||||||
impl Default for Render {
|
impl Default for Render {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(None)
|
Self::new(None, RenderConfig { padding: [10.0; 4] })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render {
|
impl Render {
|
||||||
pub fn new(mapper: Option<Mapper>) -> Self {
|
pub fn new(mapper: Option<Mapper>, cfg: RenderConfig) -> Self {
|
||||||
let this: Self = glib::Object::new();
|
let this: Self = glib::Object::new();
|
||||||
{
|
{
|
||||||
let mut configs = this.imp().config.borrow_mut();
|
|
||||||
let mut status = this.imp().status.borrow_mut();
|
let mut status = this.imp().status.borrow_mut();
|
||||||
status.scale = 1.0;
|
status.scale = 1.0;
|
||||||
}
|
}
|
||||||
|
this.imp().config.replace(cfg);
|
||||||
if let Some(mapper) = mapper {
|
if let Some(mapper) = mapper {
|
||||||
this.set_mapper(mapper);
|
this.set_mapper(mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let pointer_location_detecture = gtk::EventControllerMotion::new();
|
||||||
|
pointer_location_detecture.connect_motion(
|
||||||
|
clone!( @weak this as r => move |_context, x, y| {
|
||||||
|
r.update_status(|s| {
|
||||||
|
let dpi = r.scale_factor();
|
||||||
|
let (_,h) = s.window_size;
|
||||||
|
s.pointer_location = (x as f32 * dpi as f32, h as f32 - y as f32 * dpi as f32);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let scale_detecture = gtk::EventControllerScroll::new(EventControllerScrollFlags::VERTICAL);
|
||||||
|
scale_detecture.connect_scroll(clone!(
|
||||||
|
@weak this as r => @default-panic,move |_context, _x, y| {
|
||||||
|
r.update_status(|status|{
|
||||||
|
status.scale = y as f32;
|
||||||
|
status.motion = RenderMotion::Scale;
|
||||||
|
});
|
||||||
|
r.queue_render();
|
||||||
|
Inhibit(false)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
let drag_detecture = gtk::GestureDrag::new();
|
||||||
|
drag_detecture.connect_drag_update(clone!(
|
||||||
|
@weak this as r => move |this, _, _| {
|
||||||
|
let (ox, oy) = this.offset().unwrap_or((0.0,0.0));
|
||||||
|
r.update_status(|s| {
|
||||||
|
s.translate = Some((-ox as f32 * 1.35, oy as f32 * 1.35));
|
||||||
|
s.motion = RenderMotion::Translate;
|
||||||
|
});
|
||||||
|
r.queue_render();
|
||||||
|
}));
|
||||||
|
|
||||||
|
drag_detecture.connect_drag_end(clone!(
|
||||||
|
@weak this as r => move |_,_,_|{
|
||||||
|
r.update_status(|cfg| {
|
||||||
|
cfg.translate = None;
|
||||||
|
cfg.motion = RenderMotion::Translate;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
r.queue_render();
|
||||||
|
));
|
||||||
|
|
||||||
|
this.set_hexpand(true);
|
||||||
|
this.set_vexpand(true);
|
||||||
|
this.add_controller(pointer_location_detecture);
|
||||||
|
this.add_controller(scale_detecture);
|
||||||
|
this.add_controller(drag_detecture);
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +106,10 @@ impl Render {
|
|||||||
f(&mut cfg);
|
f(&mut cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_cfg(&self, cfg: RenderConfig) {
|
||||||
|
self.imp().config.replace(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_status<F>(&self, mut f: F)
|
pub fn update_status<F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut RenderStatus),
|
F: FnMut(&mut RenderStatus),
|
||||||
@ -62,83 +118,75 @@ impl Render {
|
|||||||
f(&mut status);
|
f(&mut status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn change_pointer(&mut self, x: f32, y: f32) {
|
|
||||||
// if let Some(canvas) = self.imp().canvas.borrow().as_ref() {
|
|
||||||
// let dpi = self.scale_factor();
|
|
||||||
// let cw = canvas.width();
|
|
||||||
// let ch = canvas.height();
|
|
||||||
// let (tx, ty) = self.imp().translate();
|
|
||||||
// let scale = self.imp().config.borrow().scale;
|
|
||||||
// let (lon, lat) = self
|
|
||||||
// .imp()
|
|
||||||
// .mapper
|
|
||||||
// .borrow()
|
|
||||||
// .fore_map((
|
|
||||||
// (x * dpi as f32 + tx) / cw / scale,
|
|
||||||
// (y * dpi as f32 + ty) / ch / scale,
|
|
||||||
// ))
|
|
||||||
// .unwrap();
|
|
||||||
// let mut cfg = self.imp().config.borrow_mut();
|
|
||||||
// cfg.pointer_lon_lat = (lon, lat);
|
|
||||||
// cfg.pointer_location = (x, y);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn set_translate(&self, trans: (f32, f32)) {
|
|
||||||
// if let Some(c) = self.imp().canvas.borrow().as_ref() {
|
|
||||||
// self.imp().config.borrow_mut().translate = (c.width() * trans.0, c.height() * trans.1);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn set_mapper(&self, mapper: Mapper) {
|
pub fn set_mapper(&self, mapper: Mapper) {
|
||||||
self.imp().mapper.replace(mapper);
|
self.imp().mapper.replace(mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn load_data_2d<T, Raw>(
|
pub fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
||||||
&self,
|
// let (x, y) = loc;
|
||||||
mut data: RadarData2d<T, Raw>,
|
// let (x_range, y_range) = self.imp().window_range().unwrap();
|
||||||
) -> Result<(), ProjError>
|
|
||||||
|
// if x >= x_range.0 && x <= x_range.1 && y >= y_range.0 && y <= y_range.1 {
|
||||||
|
|
||||||
|
// }
|
||||||
|
let foremapped = self.get_mapper().map(loc).unwrap();
|
||||||
|
return self.imp().map(foremapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inverse_map(&self, loc: (f32, f32)) -> Option<(f64, f64)> {
|
||||||
|
let foremapped = self.imp().inverse_map(loc);
|
||||||
|
foremapped.and_then(|foremapped| self.get_mapper().inverse_map(foremapped).ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ring_map(&self, ring: &LineString<f64>) -> Option<LineString<f32>> {
|
||||||
|
Some(
|
||||||
|
ring.points()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| self.map((p.x(), p.y())))
|
||||||
|
.filter(|p| p.is_some())
|
||||||
|
.collect::<Option<Vec<_>>>()
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn point_in_bound(&self, loc: (f64, f64)) -> bool {
|
||||||
|
self.get_mapper().point_in_bound(loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mapper(&self) -> Ref<Mapper> {
|
||||||
|
self.imp().mapper.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window_size(&self) -> (i32, i32) {
|
||||||
|
self.imp().window_size().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_drawer<F>(&self, range: (f64, f64, f64, f64), window_size: (f32, f32), mut f: F)
|
||||||
where
|
where
|
||||||
T: Num
|
F: FnMut(&CMS),
|
||||||
+ Clone
|
|
||||||
+ PartialEq
|
|
||||||
+ PartialOrd
|
|
||||||
+ AsPrimitive<i8>
|
|
||||||
+ AsPrimitive<f64>
|
|
||||||
+ Debug
|
|
||||||
+ FromPrimitive,
|
|
||||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
|
||||||
{
|
{
|
||||||
assert!(data.dim1.shape().len() == data.dim2.shape().len());
|
let (lon1, lon2, lat1, lat2) = range;
|
||||||
|
let mut mapper = self.get_mapper().clone();
|
||||||
|
mapper.set_lat_range(lat1..lat2);
|
||||||
|
mapper.set_lon_range(lon1..lon2);
|
||||||
|
|
||||||
let levels: Vec<T> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65]
|
println!("new_mapper: {:?}", mapper.get_bounds());
|
||||||
.into_iter()
|
println!("old_mapper: {:?}", self.get_mapper().get_bounds());
|
||||||
.map(|b| T::from_i8(b).unwrap())
|
let cms = CMS::new(mapper, window_size);
|
||||||
.collect();
|
f(&cms);
|
||||||
let colors = vec![
|
}
|
||||||
Color::rgb(0, 172, 164),
|
|
||||||
Color::rgb(192, 192, 254),
|
|
||||||
Color::rgb(122, 114, 238),
|
|
||||||
Color::rgb(30, 38, 208),
|
|
||||||
Color::rgb(166, 252, 168),
|
|
||||||
Color::rgb(0, 234, 0),
|
|
||||||
Color::rgb(16, 146, 26),
|
|
||||||
Color::rgb(252, 244, 100),
|
|
||||||
Color::rgb(200, 200, 2),
|
|
||||||
Color::rgb(140, 140, 0),
|
|
||||||
Color::rgb(254, 172, 172),
|
|
||||||
Color::rgb(254, 100, 92),
|
|
||||||
Color::rgb(238, 2, 48),
|
|
||||||
Color::rgb(212, 142, 254),
|
|
||||||
Color::rgb(170, 36, 250),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mapper = self.imp().mapper.borrow();
|
pub fn pointer_loc(&self, transed: bool) -> (f64, f64) {
|
||||||
|
let raw = self.imp().status.borrow().pointer_location.clone();
|
||||||
|
if transed {
|
||||||
|
self.inverse_map(raw).unwrap()
|
||||||
|
} else {
|
||||||
|
(raw.0 as f64, raw.1 as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let pjp = ProjPipe::new(&mapper);
|
pub fn render_range(&self) -> ((f64, f64), (f64, f64)) {
|
||||||
let rrp = ShadePipe::new(levels, colors.into_iter().map(|v| v.into()).collect());
|
self.imp().window_range().unwrap()
|
||||||
let rainbow = pjp.run(&data).unwrap();
|
|
||||||
let pbow: Array2<Option<Color>> = rrp.run(&data).unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
72
src/render/predefined/color_mapper.rs
Normal file
72
src/render/predefined/color_mapper.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use femtovg::Color;
|
||||||
|
use num_traits::NumOps;
|
||||||
|
pub trait ColorMapper<T: NumOps + PartialOrd> : Debug {
|
||||||
|
fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BoundaryNorm<T: NumOps + PartialOrd> {
|
||||||
|
boundaries: Vec<T>,
|
||||||
|
extrand: bool,
|
||||||
|
colors: Vec<femtovg::Color>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BoundaryNorm<i8> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
boundaries: vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65],
|
||||||
|
extrand: true,
|
||||||
|
colors: vec![
|
||||||
|
Color::rgb(0, 172, 164),
|
||||||
|
Color::rgb(192, 192, 254),
|
||||||
|
Color::rgb(122, 114, 238),
|
||||||
|
Color::rgb(30, 38, 208),
|
||||||
|
Color::rgb(166, 252, 168),
|
||||||
|
Color::rgb(0, 234, 0),
|
||||||
|
Color::rgb(16, 146, 26),
|
||||||
|
Color::rgb(252, 244, 100),
|
||||||
|
Color::rgb(200, 200, 2),
|
||||||
|
Color::rgb(140, 140, 0),
|
||||||
|
Color::rgb(254, 172, 172),
|
||||||
|
Color::rgb(254, 100, 92),
|
||||||
|
Color::rgb(238, 2, 48),
|
||||||
|
Color::rgb(212, 142, 254),
|
||||||
|
Color::rgb(170, 36, 250),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: NumOps + PartialOrd> BoundaryNorm<T> {
|
||||||
|
pub fn new(boundaries: Vec<T>, colors: Vec<femtovg::Color>, extrand: bool) -> Self {
|
||||||
|
// assert_eq!(boundaries.len(), colors.len() + 1);
|
||||||
|
BoundaryNorm {
|
||||||
|
boundaries,
|
||||||
|
extrand,
|
||||||
|
colors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color> {
|
||||||
|
let mut index = 0;
|
||||||
|
if value == invalid_value {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
for (i, boundary) in self.boundaries.iter().enumerate() {
|
||||||
|
if value < *boundary {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
index = index.saturating_sub(1).min(self.colors.len() - 1);
|
||||||
|
Some(self.colors[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: NumOps + PartialOrd + Debug> ColorMapper<T> for BoundaryNorm<T> {
|
||||||
|
fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color> {
|
||||||
|
self.map_value_to_color(value, invalid_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/render/predefined/gis.rs
Normal file
63
src/render/predefined/gis.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use crate::coords::Mapper;
|
||||||
|
use crate::render::Render;
|
||||||
|
use femtovg::renderer::OpenGl;
|
||||||
|
use femtovg::{Canvas, Paint};
|
||||||
|
use geo_types::LineString;
|
||||||
|
use geojson::GeoJson;
|
||||||
|
|
||||||
|
pub struct MapRender;
|
||||||
|
|
||||||
|
impl MapRender {
|
||||||
|
pub fn test(geojson: &GeoJson, canvas: &mut Canvas<OpenGl>, render: &Render) {
|
||||||
|
let paint = Paint::color(femtovg::Color::rgb(255, 255, 255));
|
||||||
|
|
||||||
|
if let GeoJson::FeatureCollection(ref feature_collection) = geojson {
|
||||||
|
for feature in &feature_collection.features {
|
||||||
|
feature
|
||||||
|
.geometry
|
||||||
|
.as_ref()
|
||||||
|
.iter()
|
||||||
|
.for_each(|geometry| match geometry.value {
|
||||||
|
geojson::Value::Polygon(ref polygon) => {
|
||||||
|
let mut path = femtovg::Path::new();
|
||||||
|
let polygon = &polygon[0];
|
||||||
|
for (i, point) in polygon.iter().enumerate() {
|
||||||
|
let _point = (point[0], point[1]);
|
||||||
|
if render.point_in_bound(_point) {
|
||||||
|
let (x, y) = render.map(_point).unwrap();
|
||||||
|
if i == 0 {
|
||||||
|
path.move_to(x, y);
|
||||||
|
} else {
|
||||||
|
path.line_to(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
geojson::Value::MultiPolygon(ref multi_polygon) => {
|
||||||
|
let mut path = femtovg::Path::new();
|
||||||
|
for polygon in multi_polygon {
|
||||||
|
let out_ring = &polygon[0];
|
||||||
|
let out_ring_line: LineString = out_ring
|
||||||
|
.iter()
|
||||||
|
.map(|x| (x[0], x[1]))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into();
|
||||||
|
let out_ring = render.ring_map(&out_ring_line).unwrap();
|
||||||
|
|
||||||
|
for (i, point) in out_ring.points().enumerate() {
|
||||||
|
let (x, y) = (point.x(), point.y());
|
||||||
|
if i == 0 {
|
||||||
|
path.move_to(x, y);
|
||||||
|
} else {
|
||||||
|
path.line_to(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.stroke_path(&mut path, &paint);
|
||||||
|
}
|
||||||
|
_ => println!("Unknown geometry type: {:?}", geometry.value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
179
src/render/predefined/grid_field_renderer.rs
Normal file
179
src/render/predefined/grid_field_renderer.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
use super::color_mapper::{BoundaryNorm, ColorMapper};
|
||||||
|
use crate::render::layer::foreground::interior::LayerImpl;
|
||||||
|
use femtovg::{ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget};
|
||||||
|
use geo_types::LineString;
|
||||||
|
use ndarray::ArrayView2;
|
||||||
|
use num_traits::{Num, NumOps};
|
||||||
|
use std::{marker::PhantomData, fmt::Debug};
|
||||||
|
|
||||||
|
use super::super::renders::DataRenderer;
|
||||||
|
use crate::{
|
||||||
|
data::Radar2d,
|
||||||
|
render::{layer::foreground::interior::Target, Render},
|
||||||
|
utils::meshgrid,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GridFieldRenderer<CMAP, T>
|
||||||
|
where
|
||||||
|
T: NumOps + PartialOrd,
|
||||||
|
CMAP: ColorMapper<T>,
|
||||||
|
{
|
||||||
|
cmap: CMAP,
|
||||||
|
value_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: NumOps + PartialOrd + Copy, CMAP: ColorMapper<T>> GridFieldRenderer<CMAP, T> {
|
||||||
|
pub fn new(cmap: CMAP) -> Self {
|
||||||
|
Self {
|
||||||
|
cmap,
|
||||||
|
value_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn draw_2d(
|
||||||
|
&self,
|
||||||
|
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
|
||||||
|
data: ArrayView2<T>,
|
||||||
|
render: &Render,
|
||||||
|
dims: (ArrayView2<f64>, ArrayView2<f64>),
|
||||||
|
window_size: (f32, f32),
|
||||||
|
fill_value: T,
|
||||||
|
) {
|
||||||
|
let shape = data.shape();
|
||||||
|
let (rows, cols) = (shape[0], shape[1]);
|
||||||
|
let (dim1, dim2) = dims;
|
||||||
|
|
||||||
|
let d1_s = dim1[[0, 0]];
|
||||||
|
let d1_e = dim1[[0, cols - 1]];
|
||||||
|
|
||||||
|
let d2_s = dim2[[0, 0]];
|
||||||
|
let d2_e = dim2[[rows - 1, 0]];
|
||||||
|
|
||||||
|
render.create_drawer((d1_s, d1_e, d2_s, d2_e), window_size, |cms| {
|
||||||
|
for r in 0..rows - 1 {
|
||||||
|
for c in 0..cols - 1 {
|
||||||
|
let lb_lat = dim2[[r, c]];
|
||||||
|
let lb_lon = dim1[[r, c]];
|
||||||
|
|
||||||
|
let rt_lat = dim2[[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 mapped_color = self.cmap.map_value_to_color(*v, fill_value);
|
||||||
|
|
||||||
|
if None == mapped_color {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapped_ring = cms.ring_map(&cell).unwrap();
|
||||||
|
|
||||||
|
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.close();
|
||||||
|
canvas.fill_path(&path, &Paint::color(mapped_color.unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, CMAP> DataRenderer for GridFieldRenderer<CMAP, T>
|
||||||
|
where
|
||||||
|
T: Num + NumOps + PartialOrd + Copy + Clone,
|
||||||
|
CMAP: ColorMapper<T>,
|
||||||
|
{
|
||||||
|
type Data = Radar2d<T>;
|
||||||
|
fn render(
|
||||||
|
&self,
|
||||||
|
render: &Render,
|
||||||
|
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
|
||||||
|
data: &Self::Data,
|
||||||
|
) -> Target {
|
||||||
|
let new_img = canvas
|
||||||
|
.create_image_empty(3000, 3000, Rgba8, ImageFlags::empty())
|
||||||
|
.expect("Can't Create Image");
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
|
canvas.reset();
|
||||||
|
|
||||||
|
if let Ok(_) = canvas.image_size(new_img) {
|
||||||
|
canvas.set_render_target(RenderTarget::Image(new_img));
|
||||||
|
let _data = data.data.view();
|
||||||
|
let (_dim1, _dim2) = meshgrid(data.dim1.view(), data.dim2.view());
|
||||||
|
self.draw_2d(
|
||||||
|
canvas,
|
||||||
|
_data,
|
||||||
|
render,
|
||||||
|
(_dim1.view(), _dim2.view()),
|
||||||
|
(3000.0, 3000.0),
|
||||||
|
data.fill_value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.restore();
|
||||||
|
canvas.set_render_target(RenderTarget::Screen);
|
||||||
|
|
||||||
|
let d1_start = (data.dim1.view()).first().unwrap().clone();
|
||||||
|
let d1_end = (data.dim1.view()).last().unwrap().clone();
|
||||||
|
|
||||||
|
let d2_start = data.dim2.view().first().unwrap().clone();
|
||||||
|
let d2_end = data.dim2.view().last().unwrap().clone();
|
||||||
|
|
||||||
|
Target::new(
|
||||||
|
new_img,
|
||||||
|
3000f32,
|
||||||
|
3000f32,
|
||||||
|
((d1_start, d1_end).into(), (d2_start, d2_end).into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GridLayerImpl<CMAP, T>
|
||||||
|
where
|
||||||
|
T: Num + NumOps + PartialOrd + Copy + Clone,
|
||||||
|
CMAP: ColorMapper<T>,
|
||||||
|
{
|
||||||
|
renderer: GridFieldRenderer<CMAP, T>,
|
||||||
|
data: Radar2d<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type DbzGridLayerImpl = GridLayerImpl<BoundaryNorm<i8>, i8>;
|
||||||
|
|
||||||
|
impl<CMAP, T> GridLayerImpl<CMAP, T>
|
||||||
|
where
|
||||||
|
T: Num + NumOps + PartialOrd + Copy + Clone,
|
||||||
|
CMAP: ColorMapper<T>,
|
||||||
|
{
|
||||||
|
pub fn new(renderer: GridFieldRenderer<CMAP, T>, data: Radar2d<T>) -> Self {
|
||||||
|
Self { renderer, data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CMAP, T> LayerImpl for GridLayerImpl<CMAP, T>
|
||||||
|
where
|
||||||
|
T: Num + NumOps + PartialOrd + Copy + Clone + Debug,
|
||||||
|
CMAP: ColorMapper<T> + Debug,
|
||||||
|
{
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
|
||||||
|
render: &Render,
|
||||||
|
) -> Option<Target> {
|
||||||
|
Some(self.renderer.render(render, canvas, &self.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/render/predefined/layers.rs
Normal file
67
src/render/predefined/layers.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use femtovg::Paint;
|
||||||
|
use num_traits::{Num, NumOps};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data::{DataLoader, Radar2d},
|
||||||
|
render::layer::foreground::interior::Layer,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
color_mapper::ColorMapper,
|
||||||
|
grid_field_renderer::{GridFieldRenderer, GridLayerImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Layer {
|
||||||
|
pub fn grid_render_layer<T, CMAP>(data: Radar2d<T>, color_map: CMAP) -> Self
|
||||||
|
where
|
||||||
|
T: std::fmt::Debug + Num + NumOps + PartialOrd + Copy + Clone + 'static,
|
||||||
|
CMAP: ColorMapper<T> + 'static,
|
||||||
|
{
|
||||||
|
Self::new(
|
||||||
|
true,
|
||||||
|
|s, c, render, _| {
|
||||||
|
if let Some(target) = s.render_target() {
|
||||||
|
if let Ok(_) = c.image_size(target.target) {
|
||||||
|
let (x, y) = target.size(render);
|
||||||
|
let (ox, oy) = target.origin(render);
|
||||||
|
let painter = Paint::image(target.target, ox, oy, x, y, 0.0, 1.0);
|
||||||
|
let mut path = femtovg::Path::new();
|
||||||
|
path.rect(ox, oy, x, y);
|
||||||
|
c.fill_path(&path, &painter);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(renderer) = s.get_imp().as_ref() {
|
||||||
|
let img = renderer.draw(c, render).unwrap();
|
||||||
|
if let Ok(_) = c.image_size(img.target) {
|
||||||
|
let (x, y) = img.size(render);
|
||||||
|
let (ox, oy) = img.origin(render);
|
||||||
|
println!("{} {} {} {}", x, y, ox, oy);
|
||||||
|
let painter = Paint::image(img.target, ox, oy, x, y, 0.0, 1.0);
|
||||||
|
let mut path = femtovg::Path::new();
|
||||||
|
path.rect(ox, oy, x, y);
|
||||||
|
s.set_render_target(img);
|
||||||
|
c.fill_path(&path, &painter);
|
||||||
|
c.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(GridLayerImpl::new(GridFieldRenderer::new(color_map), data)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grid_render_layer_with_path<T, CMAP, LOADER>(
|
||||||
|
path: impl AsRef<Path>,
|
||||||
|
loader: LOADER,
|
||||||
|
color_map: CMAP,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
T: Num + NumOps + PartialOrd + Copy + Clone + 'static + std::fmt::Debug,
|
||||||
|
CMAP: ColorMapper<T> + 'static,
|
||||||
|
LOADER: DataLoader<T, Radar2d<T>>,
|
||||||
|
{
|
||||||
|
let data = loader.load(path).unwrap();
|
||||||
|
self::Layer::grid_render_layer(data, color_map)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/render/predefined/mod.rs
Normal file
4
src/render/predefined/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod color_mapper;
|
||||||
|
pub mod grid_field_renderer;
|
||||||
|
pub mod gis;
|
||||||
|
pub mod layers;
|
||||||
14
src/render/renders.rs
Normal file
14
src/render/renders.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use femtovg::{renderer::OpenGl, Canvas};
|
||||||
|
use crate::render::layer::foreground::interior::Target;
|
||||||
|
use crate::coords::Mapper;
|
||||||
|
use super::Render;
|
||||||
|
|
||||||
|
pub trait DataRenderer {
|
||||||
|
type Data;
|
||||||
|
fn render(
|
||||||
|
&self,
|
||||||
|
render: &Render,
|
||||||
|
canvas: &mut Canvas<OpenGl>,
|
||||||
|
data: &Self::Data,
|
||||||
|
) -> Target;
|
||||||
|
}
|
||||||
12
src/utils.rs
12
src/utils.rs
@ -1,3 +1,15 @@
|
|||||||
|
use ndarray::{Array2, ArrayView1};
|
||||||
|
|
||||||
|
pub fn meshgrid<T>(x: ArrayView1<T>, y: ArrayView1<T>) -> (Array2<T>, Array2<T>)
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let shape = (y.len(), x.len());
|
||||||
|
println!("shape: {:?}", shape);
|
||||||
|
let xx = Array2::from_shape_fn(shape, |(_, j)| x[j].clone());
|
||||||
|
let yy = Array2::from_shape_fn(shape, |(i, _)| y[i].clone());
|
||||||
|
(xx, yy)
|
||||||
|
}
|
||||||
// let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
|
// let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
|
||||||
// let colors = vec![
|
// let colors = vec![
|
||||||
// RGBABuilder::default()
|
// RGBABuilder::default()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user