add foreground implemented widget
This commit is contained in:
parent
145e09ef53
commit
f2f979c483
192
src/data/mapper.rs
Normal file
192
src/data/mapper.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use geo_types::{coord, Coord as GCoord, LineString};
|
||||
use num_traits::Num;
|
||||
use proj::{Proj, ProjError};
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
painter::{wgs84::ProjectionS, Coord},
|
||||
render::WindowCoord,
|
||||
};
|
||||
|
||||
pub struct Mapper {
|
||||
proj: Proj,
|
||||
range: (Range<f64>, Range<f64>),
|
||||
bounds: (f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl From<Proj> for Mapper {
|
||||
fn from(proj: Proj) -> Self {
|
||||
let default_range: (Range<f64>, Range<f64>) = (-180.0..180.0, -90.0..90.0);
|
||||
let bounds = Self::bound(&proj, default_range.clone()).unwrap();
|
||||
Self {
|
||||
proj: proj,
|
||||
range: default_range,
|
||||
bounds,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ProjectionS> From<C> for Mapper {
|
||||
fn from(proj: C) -> Self {
|
||||
let p = Proj::new(proj.build().as_str()).unwrap();
|
||||
p.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Mapper {
|
||||
pub fn new(proj: Proj, lon_range: Range<f64>, lat_range: Range<f64>) -> Self {
|
||||
let bounds = Self::bound(&proj, (lon_range.clone(), lat_range.clone())).unwrap();
|
||||
let range = (lon_range, lat_range);
|
||||
Self {
|
||||
proj: proj,
|
||||
range,
|
||||
bounds,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(&self, point: (f64, f64)) -> Result<(f64, f64), ProjError> {
|
||||
let (p1, p2) = self
|
||||
.proj
|
||||
.convert((point.0.to_radians(), point.1.to_radians()))?;
|
||||
|
||||
let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0);
|
||||
let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2);
|
||||
|
||||
Ok((x, y))
|
||||
}
|
||||
|
||||
pub fn set_lon_range(&mut self, range: Range<f64>) {
|
||||
self.range.0 = range;
|
||||
self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap();
|
||||
}
|
||||
|
||||
pub fn set_lat_range(&mut self, range: Range<f64>) {
|
||||
self.range.1 = range;
|
||||
self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap();
|
||||
}
|
||||
|
||||
fn bound(
|
||||
proj: &Proj,
|
||||
range: (Range<f64>, Range<f64>),
|
||||
) -> Result<(f64, f64, f64, f64), ProjError> {
|
||||
let left_bottom = proj.convert((range.0.start.to_radians(), range.1.start.to_radians()))?;
|
||||
let right_top = proj.convert((range.0.end.to_radians(), range.1.end.to_radians()))?;
|
||||
Ok((left_bottom.0, right_top.0, left_bottom.1, right_top.1))
|
||||
}
|
||||
|
||||
pub fn ring_map(&self, ring: &LineString) -> Result<LineString, ProjError> {
|
||||
let mut result = Vec::new();
|
||||
for l in ring.lines() {
|
||||
let start_projected = self.map((l.start.x, l.start.y))?;
|
||||
let end_projected = self.map((l.end.x, l.end.y))?;
|
||||
|
||||
let cartesian_start = self.cartesian(l.start.x, l.start.y);
|
||||
let cartesian_end = self.cartesian(l.end.x, l.end.y);
|
||||
|
||||
let delta2 = 0.5;
|
||||
let depth = 16;
|
||||
let mut res: Vec<GCoord> = Vec::new();
|
||||
self.resample_line_to(
|
||||
start_projected,
|
||||
end_projected,
|
||||
l.start.x,
|
||||
l.end.x,
|
||||
cartesian_start,
|
||||
cartesian_end,
|
||||
&|x| self.map((x.x, x.y)),
|
||||
delta2,
|
||||
depth,
|
||||
&mut res,
|
||||
)?;
|
||||
result.extend(res);
|
||||
}
|
||||
|
||||
Ok(LineString::new(result))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cartesian(&self, lambda: f64, phi: f64) -> (f64, f64, f64) {
|
||||
let cos_phi = phi.cosh();
|
||||
return (cos_phi * lambda.cosh(), cos_phi * lambda.sinh(), phi.sinh());
|
||||
}
|
||||
|
||||
fn resample_line_to<F>(
|
||||
&self,
|
||||
start_projected: (f64, f64),
|
||||
end_projected: (f64, f64),
|
||||
lambda0: f64,
|
||||
lambda1: f64,
|
||||
cartesian_start: (f64, f64, f64),
|
||||
cartesian_end: (f64, f64, f64),
|
||||
project: &F,
|
||||
delta2: f64,
|
||||
depth: u8,
|
||||
result: &mut Vec<GCoord>,
|
||||
) -> Result<(), ProjError>
|
||||
where
|
||||
F: Fn(GCoord) -> Result<(f64, f64), ProjError>,
|
||||
{
|
||||
let cos_min_distance: f64 = 30f64.cosh();
|
||||
let (x0, y0) = start_projected;
|
||||
let (x1, y1) = end_projected;
|
||||
let (a0, b0, c0) = cartesian_start;
|
||||
let (a1, b1, c1) = cartesian_end;
|
||||
|
||||
let dx = x1 - x0;
|
||||
let dy = y1 - y0;
|
||||
let d2 = dx * dx + dy * dy;
|
||||
|
||||
if d2 > 4.0 * delta2 && depth > 0 {
|
||||
let a = a0 + a1;
|
||||
let b = b0 + b1;
|
||||
let c = c0 + c1;
|
||||
let m = (a * a + b * b + c * c).sqrt();
|
||||
let depth = depth - 1;
|
||||
|
||||
let phi2 = (c / m).asin();
|
||||
let lambda2 =
|
||||
if (c / m).abs() - 1.0 < f64::EPSILON || (lambda0 - lambda1).abs() < f64::EPSILON {
|
||||
(lambda0 + lambda1) / 2.0
|
||||
} else {
|
||||
b.atan2(a)
|
||||
};
|
||||
let (x2, y2) = project(coord! {x:lambda2,y:phi2})?;
|
||||
let dx2 = x2 - x0;
|
||||
let dy2 = y2 - y0;
|
||||
let dz = dy * dx2 - dx * dy2;
|
||||
if dz * dz / d2 > delta2
|
||||
|| ((dx * dx2 + dy * dy2) / d2 - 0.5).abs() > 0.3
|
||||
|| a0 * a1 + b0 * b1 + c0 * c1 < cos_min_distance
|
||||
{
|
||||
self.resample_line_to(
|
||||
start_projected,
|
||||
(x2, y2),
|
||||
lambda0,
|
||||
lambda2,
|
||||
cartesian_start,
|
||||
(a / m, b / m, c),
|
||||
project,
|
||||
delta2,
|
||||
depth - 1,
|
||||
result,
|
||||
)?;
|
||||
result.push(coord! {x:x2,y:y2});
|
||||
self.resample_line_to(
|
||||
(x2, y2),
|
||||
end_projected,
|
||||
lambda2,
|
||||
lambda1,
|
||||
(a / m, b / m, c),
|
||||
cartesian_end,
|
||||
project,
|
||||
delta2,
|
||||
depth - 1,
|
||||
result,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resample(&self) {}
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
use ndarray::{s, Array, Array1, Array2, Array3, ArrayBase, Ix1, Ix2, OwnedRepr, RawDataClone};
|
||||
use ndarray::{
|
||||
s, Array, Array1, Array2, Array3, ArrayBase, Ix1, Ix2, OwnedRepr, RawDataClone, ViewRepr,
|
||||
};
|
||||
use npyz::{npz::NpzArchive, Deserialize};
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
use quadtree_rs::{area::AreaBuilder, Quadtree};
|
||||
@ -6,6 +8,7 @@ use std::{self, f64::consts::PI, fmt::Debug, io::BufReader, path::Path};
|
||||
use thiserror::Error;
|
||||
|
||||
mod concrete_data;
|
||||
pub mod mapper;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DataError {
|
||||
@ -344,3 +347,5 @@ impl<T: Num + Clone + PartialEq + PartialOrd> Radar2d<T> {
|
||||
Ok(meth.load(path)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Radar2dRef<T> = RadarData2d<T, ViewRepr<T>>;
|
||||
|
||||
32
src/main.rs
32
src/main.rs
@ -1,26 +1,23 @@
|
||||
use std::ptr;
|
||||
|
||||
use data::mapper::Mapper;
|
||||
use data::{Npz, Radar2d};
|
||||
use glib::{timeout_add, timeout_add_local};
|
||||
use glib_macros::clone;
|
||||
use gtk::gdk::builders::RGBABuilder;
|
||||
use gtk::gdk::Display;
|
||||
use gtk::{
|
||||
gio, glib, style_context_add_provider_for_display, Application, ApplicationWindow, CssProvider,
|
||||
FontButton, GestureDrag,
|
||||
};
|
||||
use gtk::{prelude::*, DrawingArea};
|
||||
use std::ptr;
|
||||
mod data;
|
||||
mod monitor;
|
||||
mod painter;
|
||||
mod render;
|
||||
mod tree;
|
||||
mod window;
|
||||
use data::{Npz, Radar2d, RadarData2d};
|
||||
use monitor::Monitor;
|
||||
use ndarray::parallel::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
use painter::wgs84::{LatLonCoord, Mercator, ProjectionS, Range};
|
||||
use render::Render;
|
||||
use render::{BackgroundConfig, BackgroundWidget};
|
||||
use render::{BackgroundConfig, BackgroundWidget, ForegroundConfig, ForegroundWidget, Render};
|
||||
|
||||
const APP_ID: &str = "org.gtk_rs.HelloWorld2";
|
||||
|
||||
@ -60,24 +57,23 @@ fn build_ui(app: &Application) {
|
||||
.build();
|
||||
|
||||
let mut background_config = BackgroundConfig::new();
|
||||
let mut foreground_config = ForegroundConfig::new();
|
||||
|
||||
let background_widget = BackgroundWidget::new(background_config);
|
||||
let foreground_widget = ForegroundWidget::new(foreground_config);
|
||||
let render = Render::new(background_widget, foreground_widget);
|
||||
|
||||
let drawing_area = Render::new();
|
||||
let path = "/Users/ruomu/test2.npz";
|
||||
|
||||
// let path = "/Users/ruomu/test2.npz";
|
||||
let data = Radar2d::<i8>::load(path, Npz).unwrap();
|
||||
|
||||
// let data = Radar2d::<i8>::load(path, Npz).unwrap();
|
||||
// let projection = Mercator::new();
|
||||
let projection = Mercator::new();
|
||||
let mapper: Mapper = projection.into();
|
||||
|
||||
// let aa = Range::from((*data.dim1.first().unwrap(), *data.dim1.last().unwrap()));
|
||||
// let ab = Range::from((*data.dim2.first().unwrap(), *data.dim2.last().unwrap()));
|
||||
render.set_mapper(mapper);
|
||||
let monitor = Monitor::new(render);
|
||||
|
||||
// let coord = LatLonCoord::new(Some(aa), Some(ab), ((0, 1000), (0, 1000)), projection);
|
||||
|
||||
// let result = data.mapped(&coord);
|
||||
|
||||
let monitor = Monitor::new();
|
||||
monitor.load_data_2d(data);
|
||||
|
||||
window.set_child(Some(&monitor));
|
||||
window.set_default_width(1000);
|
||||
|
||||
@ -5,11 +5,9 @@ use gtk::CompositeTemplate;
|
||||
use gtk::{glib, prelude::WidgetExtManual};
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
#[derive(Default, CompositeTemplate)]
|
||||
#[template(file = "monitor.ui")]
|
||||
#[derive(Default)]
|
||||
pub struct Monitor {
|
||||
#[template_child(id = "render")]
|
||||
pub(super) label: TemplateChild<Render>,
|
||||
pub(super) renderer: RefCell<Render>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@ -17,14 +15,6 @@ impl ObjectSubclass for Monitor {
|
||||
const NAME: &'static str = "Monitor";
|
||||
type Type = super::Monitor;
|
||||
type ParentType = gtk::Box;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for Monitor {}
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
mod imp;
|
||||
use crate::data::RadarData2d;
|
||||
use crate::render::Render;
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::clone;
|
||||
use glib::{clone::Downgrade, subclass::prelude::*};
|
||||
use gtk::glib;
|
||||
use gtk::traits::WidgetExt;
|
||||
use gtk::{EventController, EventControllerScrollFlags, Inhibit};
|
||||
use num_traits::Num;
|
||||
use proj::{Proj, ProjError};
|
||||
use std::borrow::Borrow;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct Monitor(ObjectSubclass<imp::Monitor>)
|
||||
@ -9,8 +16,47 @@ glib::wrapper! {
|
||||
}
|
||||
|
||||
impl Monitor {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(render: Render) -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
let pointer_location_detecture = gtk::EventControllerMotion::new();
|
||||
pointer_location_detecture.connect_motion(
|
||||
clone!(@weak this as s => move |_context, x, y| {
|
||||
s.imp().renderer.borrow()
|
||||
.imp()
|
||||
.pointer_location
|
||||
.replace((x as f32, y as f32));
|
||||
}),
|
||||
);
|
||||
|
||||
let scale_detecture = gtk::EventControllerScroll::new(EventControllerScrollFlags::VERTICAL);
|
||||
|
||||
scale_detecture.connect_scroll(clone!(
|
||||
@weak this as s => @default-panic,move |_context, _x, y| {
|
||||
let renderer = s.imp().borrow().renderer.borrow();
|
||||
let current_scale = renderer.imp().scale.borrow().clone();
|
||||
renderer.imp().scale.replace(
|
||||
(current_scale + y as f32 / 100.0).max(1.0).min(5.0),
|
||||
);
|
||||
Inhibit(false)
|
||||
}
|
||||
));
|
||||
|
||||
render.add_controller(pointer_location_detecture);
|
||||
render.add_controller(scale_detecture);
|
||||
render.set_hexpand(true);
|
||||
render.set_parent(&this);
|
||||
this.imp().renderer.replace(render);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub fn load_data_2d<T, Raw>(&self, data: RadarData2d<T, Raw>) -> Result<(), ProjError>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
let renderer = self.imp().renderer.borrow();
|
||||
renderer.load_data_2d(data)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="Monitor" parent="GtkBox">
|
||||
<child>
|
||||
<object class="MyRender" id="render">
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
@ -1,7 +1,10 @@
|
||||
use geo_types::{coord, Coord as GCoord, Line, LineString, Polygon};
|
||||
use ndarray::{Array2, ViewRepr};
|
||||
use num_traits::Num;
|
||||
use proj::ProjError;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use crate::data::RadarData2d;
|
||||
use crate::data::{mapper::Mapper, RadarData2d};
|
||||
|
||||
use super::AfterMapping2d;
|
||||
pub mod wgs84;
|
||||
@ -20,26 +23,34 @@ where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
pub fn mapped(&self, coord: &impl Coord<f64>) -> AfterMapping2d<T> {
|
||||
pub fn mapped(&self, coord: impl Borrow<Mapper>) -> Result<AfterMapping2d<T>, ProjError> {
|
||||
let mapper: &Mapper = coord.borrow();
|
||||
self.map_by_fn(|x| mapper.map(x))
|
||||
}
|
||||
|
||||
pub fn map_by_fn<F>(&self, f: F) -> Result<AfterMapping2d<T>, ProjError>
|
||||
where
|
||||
F: Fn((f64, f64)) -> Result<(f64, f64), ProjError>,
|
||||
{
|
||||
let mesh_dim1_len = self.dim1.len();
|
||||
let mesh_dim2_len = self.dim2.len();
|
||||
|
||||
let mut d1 = Array2::<f64>::zeros((mesh_dim1_len, mesh_dim2_len));
|
||||
let mut d2 = Array2::<f64>::zeros((mesh_dim1_len, mesh_dim2_len));
|
||||
let mut d1 = Array2::<f64>::zeros((mesh_dim2_len, mesh_dim1_len));
|
||||
let mut d2 = Array2::<f64>::zeros((mesh_dim2_len, mesh_dim1_len));
|
||||
|
||||
self.dim1.iter().enumerate().for_each(|(i, v)| {
|
||||
self.dim2.iter().enumerate().for_each(|(j, u)| {
|
||||
let (x, y) = coord.map(*v, *u);
|
||||
d1[[i, j]] = x;
|
||||
d2[[i, j]] = y;
|
||||
});
|
||||
});
|
||||
for (i, v) in self.dim1.iter().enumerate() {
|
||||
for (j, u) in self.dim2.iter().enumerate() {
|
||||
let (x, y) = f((*v, *u))?;
|
||||
d1[[j, i]] = x;
|
||||
d2[[j, i]] = y;
|
||||
}
|
||||
}
|
||||
|
||||
AfterMapping2d {
|
||||
Ok(AfterMapping2d {
|
||||
dim1: d1,
|
||||
dim2: d2,
|
||||
data: self.data.view(),
|
||||
coord_type: self.coord_type.clone(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ struct PCS<T: ProjectionS> {
|
||||
pub lon_range: Range,
|
||||
pub lat_range: Range,
|
||||
pub proj_param: T,
|
||||
transformer: Proj,
|
||||
pub transformer: Proj,
|
||||
}
|
||||
|
||||
unsafe impl Sync for PCS<Mercator> {}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::painter::Coord;
|
||||
use crate::render::{imp, WindowCoord};
|
||||
use femtovg::Paint;
|
||||
use geo_macros::Prj;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
mod imp;
|
||||
use crate::render::WindowCoord;
|
||||
use femtovg::{renderer::OpenGl, Canvas, Path};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
use gtk::{ffi::gtk_widget_get_width, glib, graphene::Rect, prelude::SnapshotExtManual};
|
||||
@ -40,7 +41,21 @@ impl BackgroundWidget {
|
||||
if imp.config.borrow().show_lon_lines {}
|
||||
}
|
||||
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>) {
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, scale: f32, dpi: i32) {
|
||||
let canvas_widht = canvas.width();
|
||||
let canvas_height = canvas.height();
|
||||
let config = self.imp().config.borrow();
|
||||
|
||||
self.mesh_lines(canvas);
|
||||
}
|
||||
|
||||
pub fn set_lat_lines(&self, lat_lines: Vec<Vec<WindowCoord>>) {
|
||||
let imp = self.imp();
|
||||
imp.config.borrow_mut().lat_lines = lat_lines;
|
||||
}
|
||||
|
||||
pub fn set_lon_lines(&self, lon_lines: Vec<Vec<WindowCoord>>) {
|
||||
let imp = self.imp();
|
||||
imp.config.borrow_mut().lon_lines = lon_lines;
|
||||
}
|
||||
}
|
||||
|
||||
40
src/render/foreground/imp.rs
Normal file
40
src/render/foreground/imp.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::data::{Radar2dRef, RadarData2d};
|
||||
use crate::painter::Coord;
|
||||
use crate::render::{imp, WindowCoord};
|
||||
use femtovg::Paint;
|
||||
use geo_macros::Prj;
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
use ndarray::Array2;
|
||||
|
||||
#[derive(Prj, Default)]
|
||||
pub struct ForegroundConfig {
|
||||
pub show_lat_lines: bool,
|
||||
pub show_lon_lines: bool,
|
||||
pub lat_lines: Vec<Vec<WindowCoord>>,
|
||||
pub lon_lines: Vec<Vec<WindowCoord>>,
|
||||
pub painter: Paint,
|
||||
}
|
||||
|
||||
impl ForegroundConfig {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ForegroundWidget {
|
||||
pub(super) config: RefCell<ForegroundConfig>,
|
||||
pub(super) dim1: RefCell<Option<Array2<f64>>>,
|
||||
pub(super) dim2: RefCell<Option<Array2<f64>>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for ForegroundWidget {
|
||||
const NAME: &'static str = "ForegroundWidget";
|
||||
type Type = super::ForegroundWidget;
|
||||
}
|
||||
|
||||
impl ObjectImpl for ForegroundWidget {}
|
||||
39
src/render/foreground/mod.rs
Normal file
39
src/render/foreground/mod.rs
Normal file
@ -0,0 +1,39 @@
|
||||
mod imp;
|
||||
use crate::{data::mapper::Mapper, render::WindowCoord};
|
||||
use femtovg::{renderer::OpenGl, Canvas, Path};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
use gtk::{ffi::gtk_widget_get_width, glib, graphene::Rect, prelude::SnapshotExtManual};
|
||||
use ndarray::Array2;
|
||||
use std::ops::Range;
|
||||
|
||||
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: &Mapper) {
|
||||
let canvas_widht = canvas.width();
|
||||
let canvas_height = canvas.height();
|
||||
let config = self.imp().config.borrow();
|
||||
}
|
||||
|
||||
pub(super) fn set_dims(&mut self, dims: (Array2<f64>, Array2<f64>)) {
|
||||
self.imp().dim1.replace(Some(dims.0));
|
||||
self.imp().dim2.replace(Some(dims.1));
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,43 @@
|
||||
use super::background::BackgroundWidget;
|
||||
use glib::{ObjectExt, ParamSpec, Properties, Value};
|
||||
use super::foreground::ForegroundWidget;
|
||||
use super::WindowCoord;
|
||||
use crate::data::mapper::Mapper;
|
||||
use crate::painter::wgs84::Mercator;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::traits::{GLAreaExt, WidgetExt};
|
||||
use gtk::{glib, prelude::WidgetExtManual};
|
||||
use ndarray::Array2;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::f32::consts::PI;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Render {
|
||||
// pub(super) background: RefCell<BackgroundWidget>,
|
||||
pub(super) background: RefCell<BackgroundWidget>,
|
||||
pub(super) foreground: RefCell<ForegroundWidget>,
|
||||
pub(super) dim1: RefCell<Option<Array2<f64>>>,
|
||||
pub(super) dim2: RefCell<Option<Array2<f64>>>,
|
||||
pub scale: RefCell<f32>,
|
||||
pub pointer_location: RefCell<WindowCoord>,
|
||||
pub transform: RefCell<WindowCoord>,
|
||||
pub mapper: RefCell<Mapper>,
|
||||
canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
|
||||
}
|
||||
|
||||
impl Default for Render {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
background: RefCell::new(BackgroundWidget::default()),
|
||||
foreground: RefCell::new(ForegroundWidget::default()),
|
||||
scale: RefCell::new(1.0),
|
||||
pointer_location: RefCell::new((0.0, 0.0)),
|
||||
transform: RefCell::new((0.0, 0.0)),
|
||||
mapper: RefCell::new(Mercator::new().into()),
|
||||
canvas: RefCell::new(None),
|
||||
dim1: RefCell::new(None),
|
||||
dim2: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for Render {
|
||||
const NAME: &'static str = "MyRender";
|
||||
@ -66,12 +91,10 @@ impl GLAreaImpl for Render {
|
||||
(h * dpi) as u32,
|
||||
Color::rgba(0, 0, 0, 255),
|
||||
);
|
||||
canvas.fill_text(
|
||||
0.,
|
||||
0.,
|
||||
"hello",
|
||||
&Paint::color(Color::rgba(255, 255, 255, 0)),
|
||||
);
|
||||
|
||||
self.background
|
||||
.borrow()
|
||||
.draw(canvas, self.scale.borrow().clone(), dpi);
|
||||
|
||||
canvas.flush();
|
||||
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
mod background;
|
||||
mod foreground;
|
||||
|
||||
mod imp;
|
||||
use crate::data::{mapper::Mapper, RadarData2d};
|
||||
|
||||
pub use self::background::{BackgroundConfig, BackgroundWidget};
|
||||
use glib::subclass::prelude::*;
|
||||
use gtk::{traits::GestureSingleExt, EventControllerScrollFlags};
|
||||
pub use self::foreground::{ForegroundConfig, ForegroundWidget};
|
||||
pub use glib::subclass::prelude::*;
|
||||
use ndarray::{self, s, Axis, Dimension, Ix2, Zip};
|
||||
use num_traits::Num;
|
||||
use proj::ProjError;
|
||||
|
||||
pub(super) type WindowCoord = (f32, f32);
|
||||
|
||||
@ -13,12 +20,36 @@ glib::wrapper! {
|
||||
|
||||
impl Default for Render {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(BackgroundWidget::default(), ForegroundWidget::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Render {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
pub fn new(background: BackgroundWidget, foreground: ForegroundWidget) -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
this.imp().scale.replace(1.0);
|
||||
this.imp().background.replace(background);
|
||||
this.imp().foreground.replace(foreground);
|
||||
this
|
||||
}
|
||||
|
||||
pub fn set_mapper(&self, mapper: Mapper) {
|
||||
self.imp().mapper.replace(mapper);
|
||||
}
|
||||
|
||||
pub(super) fn load_data_2d<T, Raw>(&self, data: RadarData2d<T, Raw>) -> Result<(), ProjError>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
assert!(data.dim1.shape().len() == data.dim2.shape().len());
|
||||
|
||||
let meshed = data.map_by_fn(|(x, y)| Ok((x, y)))?;
|
||||
|
||||
self.imp()
|
||||
.foreground
|
||||
.borrow_mut()
|
||||
.set_dims((meshed.dim1, meshed.dim2));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
102
test.js
Normal file
102
test.js
Normal file
@ -0,0 +1,102 @@
|
||||
import {cartesian} from "../cartesian.js";
|
||||
import {abs, asin, atan2, cos, epsilon, radians, sqrt} from "../math.js";
|
||||
import {transformer} from "../transform.js";
|
||||
|
||||
var maxDepth = 16, // maximum depth of subdivision
|
||||
cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
|
||||
|
||||
export default function(project, delta2) {
|
||||
return +delta2 ? resample(project, delta2) : resampleNone(project);
|
||||
}
|
||||
|
||||
function resampleNone(project) {
|
||||
return transformer({
|
||||
point: function(x, y) {
|
||||
x = project(x, y);
|
||||
this.stream.point(x[0], x[1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resample(project, delta2) {
|
||||
|
||||
function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
|
||||
var dx = x1 - x0,
|
||||
dy = y1 - y0,
|
||||
d2 = dx * dx + dy * dy;
|
||||
if (d2 > 4 * delta2 && depth--) {
|
||||
var a = a0 + a1,
|
||||
b = b0 + b1,
|
||||
c = c0 + c1,
|
||||
m = sqrt(a * a + b * b + c * c),
|
||||
phi2 = asin(c /= m),
|
||||
lambda2 = abs(abs(c) - 1) < epsilon || abs(lambda0 - lambda1) < epsilon ? (lambda0 + lambda1) / 2 : atan2(b, a),
|
||||
p = project(lambda2, phi2),
|
||||
x2 = p[0],
|
||||
y2 = p[1],
|
||||
dx2 = x2 - x0,
|
||||
dy2 = y2 - y0,
|
||||
dz = dy * dx2 - dx * dy2;
|
||||
if (dz * dz / d2 > delta2 // perpendicular projected distance
|
||||
|| abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
|
||||
|| a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
|
||||
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
|
||||
stream.point(x2, y2);
|
||||
resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
return function(stream) {
|
||||
var lambda00, x00, y00, a00, b00, c00, // first point
|
||||
lambda0, x0, y0, a0, b0, c0; // previous point
|
||||
|
||||
var resampleStream = {
|
||||
point: point,
|
||||
lineStart: lineStart,
|
||||
lineEnd: lineEnd,
|
||||
polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },
|
||||
polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }
|
||||
};
|
||||
|
||||
function point(x, y) {
|
||||
x = project(x, y);
|
||||
stream.point(x[0], x[1]);
|
||||
}
|
||||
|
||||
function lineStart() {
|
||||
x0 = NaN;
|
||||
resampleStream.point = linePoint;
|
||||
stream.lineStart();
|
||||
}
|
||||
|
||||
function linePoint(lambda, phi) {
|
||||
var c = cartesian([lambda, phi]), p = project(lambda, phi);
|
||||
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
|
||||
stream.point(x0, y0);
|
||||
}
|
||||
|
||||
function lineEnd() {
|
||||
resampleStream.point = point;
|
||||
stream.lineEnd();
|
||||
}
|
||||
|
||||
function ringStart() {
|
||||
lineStart();
|
||||
resampleStream.point = ringPoint;
|
||||
resampleStream.lineEnd = ringEnd;
|
||||
}
|
||||
|
||||
function ringPoint(lambda, phi) {
|
||||
linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
|
||||
resampleStream.point = linePoint;
|
||||
}
|
||||
|
||||
function ringEnd() {
|
||||
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
|
||||
resampleStream.lineEnd = lineEnd;
|
||||
lineEnd();
|
||||
}
|
||||
|
||||
return resampleStream;
|
||||
};
|
||||
}
|
||||
95
tile.js
Normal file
95
tile.js
Normal file
@ -0,0 +1,95 @@
|
||||
function redraw() {
|
||||
var t = (0 | Math.log(projection.scale()) / Math.LN2) - 5
|
||||
, e = d3.quadTiles(projection, t)
|
||||
, n = projection.clipExtent();
|
||||
tiles_ = d3.quadTiles(projection.clipExtent([[1, 1], [width - 1, height - 1]]), t);
|
||||
var a = svg.selectAll(".tile").data(tiles_, key);
|
||||
a.enter().append("path").attr("class", "tile"),
|
||||
a.exit().remove(),
|
||||
a.attr("class", "tile").attr("d", path),
|
||||
svg.selectAll(".tile").data(e, key).classed("highlight", !0).exit().classed("highlight", !1);
|
||||
var r = svg.selectAll("text").data(e, key);
|
||||
r.enter().append("text").attr("text-anchor", "middle").text(key),
|
||||
r.exit().remove(),
|
||||
r.attr("transform", function (t) {
|
||||
return "translate(" + projection(t.centroid) + ")"
|
||||
}),
|
||||
projection.clipExtent(n)
|
||||
}
|
||||
function key(t) {
|
||||
return t.key.join(", ")
|
||||
}
|
||||
!function () {
|
||||
function t() { }
|
||||
function e(t) {
|
||||
return 360 * Math.atan(Math.exp(-t * Math.PI / 180)) / Math.PI - 90
|
||||
}
|
||||
d3.quadTiles = function (n, a) {
|
||||
function r(t, n, p, d) {
|
||||
var h = p - t
|
||||
, g = e(n)
|
||||
, u = e(d)
|
||||
, v = l * h;
|
||||
i = !0,
|
||||
s.polygonStart(),
|
||||
s.lineStart();
|
||||
for (var f = t; p + v / 2 > f && i; f += v)
|
||||
s.point(f, g);
|
||||
for (var m = g; (m += v) < u && i;)
|
||||
s.point(p, m);
|
||||
for (var f = p; f > t - v / 2 && i; f -= v)
|
||||
s.point(f, u);
|
||||
for (var m = u; (m -= v) > g && i;)
|
||||
s.point(t, m);
|
||||
if (i && s.point(t, g),
|
||||
s.lineEnd(),
|
||||
s.polygonEnd(),
|
||||
360 / c >= h)
|
||||
i || o.push({
|
||||
type: "Polygon",
|
||||
coordinates: [d3.range(t, p + v / 2, v).map(function (t) {
|
||||
return [t, n]
|
||||
}).concat([[p, .5 * (n + d)]]).concat(d3.range(p, t - v / 2, -v).map(function (t) {
|
||||
return [t, d]
|
||||
})).concat([[t, .5 * (n + d)]]).concat([[t, n]]).map(function (t) {
|
||||
return [t[0], e(t[1])]
|
||||
})],
|
||||
key: [0 | (180 + t) / 360 * c, 0 | (180 + n) / 360 * c, a],
|
||||
centroid: [.5 * (t + p), .5 * (g + u)]
|
||||
});
|
||||
else if (!i) {
|
||||
var f = .5 * (t + p)
|
||||
, m = .5 * (n + d);
|
||||
r(t, n, f, m),
|
||||
r(f, n, p, m),
|
||||
r(t, m, f, d),
|
||||
r(f, m, p, d)
|
||||
}
|
||||
}
|
||||
var i, o = [], c = 1 << (a = Math.max(0, a)), l = Math.max(.2, Math.min(1, .01 * a)), p = n.precision(), s = n.precision(960).stream({
|
||||
point: function () {
|
||||
i = !1
|
||||
},
|
||||
lineStart: t,
|
||||
lineEnd: t,
|
||||
polygonStart: t,
|
||||
polygonEnd: t
|
||||
});
|
||||
return r(-180, -180, 180, 180),
|
||||
n.precision(p),
|
||||
o
|
||||
}
|
||||
}();
|
||||
var width = 960
|
||||
, height = 600
|
||||
, p = 100
|
||||
, projection = d3.geo.albers().rotate([0, 0]).center([0, 38.7]).scale(1280).translate([width / 2, height / 2]).precision(.1).clipExtent([[p, p], [width - p, height - p]])
|
||||
, path = d3.geo.path().projection(projection)
|
||||
, svg = d3.select("#map").append("svg").attr("width", width).attr("height", height).style("pointer-events", "all").call(d3.behavior.zoom().translate(projection.translate()).scale(projection.scale()).scaleExtent([50, 1e7]).on("zoom", function () {
|
||||
projection.scale(d3.event.scale).translate(d3.event.translate),
|
||||
redraw()
|
||||
}));
|
||||
svg.append("path").datum({
|
||||
type: "Sphere"
|
||||
}).attr("class", "outline").attr("d", path),
|
||||
redraw();
|
||||
Loading…
Reference in New Issue
Block a user