refactor
This commit is contained in:
parent
30f6c206f7
commit
fdea34f739
248
Cargo.lock
generated
248
Cargo.lock
generated
@ -39,9 +39,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.71"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
@ -98,6 +98,12 @@ dependencies = [
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -228,6 +234,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
name = "cinrad_g"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cairo-rs",
|
||||
"epoxy",
|
||||
"femtovg",
|
||||
@ -239,12 +246,14 @@ dependencies = [
|
||||
"glow",
|
||||
"glue",
|
||||
"gtk4",
|
||||
"image",
|
||||
"libloading 0.8.0",
|
||||
"ndarray",
|
||||
"npyz",
|
||||
"num-traits",
|
||||
"proj",
|
||||
"proj-sys",
|
||||
"proj5",
|
||||
"quadtree_rs",
|
||||
"shapefile",
|
||||
"svg",
|
||||
@ -369,6 +378,12 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
@ -497,6 +512,31 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1e481eb11a482815d3e9d618db8c42a93207134662873809335a92327440c18"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "femtovg"
|
||||
version = "0.7.1"
|
||||
@ -550,6 +590,19 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.10.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"pin-project",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@ -599,6 +652,12 @@ dependencies = [
|
||||
"syn 2.0.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.28"
|
||||
@ -718,6 +777,29 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gio"
|
||||
version = "0.17.10"
|
||||
@ -966,6 +1048,15 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
@ -1016,15 +1107,21 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.6"
|
||||
version = "0.24.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a"
|
||||
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-rational 0.4.1",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1061,6 +1158,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.64"
|
||||
@ -1088,6 +1194,12 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
@ -1120,6 +1232,16 @@ version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.19"
|
||||
@ -1170,6 +1292,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1451,6 +1583,26 @@ dependencies = [
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.63",
|
||||
"quote 1.0.29",
|
||||
"syn 2.0.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.10"
|
||||
@ -1469,6 +1621,19 @@ version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.1"
|
||||
@ -1529,9 +1694,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proj"
|
||||
version = "0.27.1"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f67b3ee003b8b02f854a82e51f78a11082bae993a3e25963ae44df8f9fd2d0d0"
|
||||
checksum = "7ad1830ad8966eba22c76e78440458f07bd812bef5c3efdf335dec55cd1085ab"
|
||||
dependencies = [
|
||||
"geo-types",
|
||||
"libc",
|
||||
@ -1553,6 +1718,15 @@ dependencies = [
|
||||
"tar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proj5"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60099264c806d50862be737c6cfecf83a4f6de7df5009e2b3708e0821498bdf3"
|
||||
dependencies = [
|
||||
"scoped_threadpool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "py_literal"
|
||||
version = "0.4.0"
|
||||
@ -1566,6 +1740,15 @@ dependencies = [
|
||||
"pest_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quadtree_rs"
|
||||
version = "0.1.3"
|
||||
@ -1694,6 +1877,12 @@ dependencies = [
|
||||
"unicode-script",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped_threadpool"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@ -1784,6 +1973,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
@ -1808,6 +2003,15 @@ version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.7.0"
|
||||
@ -1933,6 +2137,17 @@ dependencies = [
|
||||
"syn 2.0.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.22"
|
||||
@ -2073,6 +2288,12 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.87"
|
||||
@ -2137,6 +2358,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.0"
|
||||
@ -2331,3 +2558,12 @@ dependencies = [
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
@ -10,7 +10,6 @@ edition = "2021"
|
||||
cairo-rs = { version = "0.17.0", features = ["xlib"] }
|
||||
glib = "0.17.9"
|
||||
gtk = { version = "0.6.6", package = "gtk4", features = ["v4_8"] }
|
||||
proj = "0.27.0"
|
||||
|
||||
# gtk = "0.15.5"
|
||||
|
||||
@ -29,6 +28,10 @@ glue = "0.8.7"
|
||||
epoxy = "0.1.0"
|
||||
femtovg = "0.7.1"
|
||||
glow = "0.12.2"
|
||||
proj = "0.27.2"
|
||||
image = "0.24.7"
|
||||
anyhow = "1.0.72"
|
||||
proj5 = { version = "0.1.7", features = ["multithreading"] }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
335
src/backend.rs
335
src/backend.rs
@ -1,335 +0,0 @@
|
||||
use cairo::{Context as CairoContext, FontSlant, FontWeight};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
|
||||
/// The drawing backend that is backed with a Cairo context
|
||||
pub struct CairoBackend<'a> {
|
||||
context: &'a CairoContext,
|
||||
width: u32,
|
||||
height: u32,
|
||||
init_flag: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CairoError;
|
||||
|
||||
impl std::fmt::Display for CairoError {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(fmt, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CairoError {}
|
||||
|
||||
impl<'a> CairoBackend<'a> {
|
||||
fn set_color(&self, color: &BackendColor) {
|
||||
self.context.set_source_rgba(
|
||||
f64::from(color.rgb.0) / 255.0,
|
||||
f64::from(color.rgb.1) / 255.0,
|
||||
f64::from(color.rgb.2) / 255.0,
|
||||
color.alpha,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_stroke_width(&self, width: u32) {
|
||||
self.context.set_line_width(f64::from(width));
|
||||
}
|
||||
|
||||
fn set_font<S: BackendTextStyle>(&self, font: &S) {
|
||||
match font.style() {
|
||||
FontStyle::Normal => self.context.select_font_face(
|
||||
font.family().as_str(),
|
||||
FontSlant::Normal,
|
||||
FontWeight::Normal,
|
||||
),
|
||||
FontStyle::Bold => self.context.select_font_face(
|
||||
font.family().as_str(),
|
||||
FontSlant::Normal,
|
||||
FontWeight::Bold,
|
||||
),
|
||||
FontStyle::Oblique => self.context.select_font_face(
|
||||
font.family().as_str(),
|
||||
FontSlant::Oblique,
|
||||
FontWeight::Normal,
|
||||
),
|
||||
FontStyle::Italic => self.context.select_font_face(
|
||||
font.family().as_str(),
|
||||
FontSlant::Italic,
|
||||
FontWeight::Normal,
|
||||
),
|
||||
};
|
||||
self.context.set_font_size(font.size());
|
||||
}
|
||||
|
||||
pub fn new(context: &'a CairoContext, (w, h): (u32, u32)) -> Result<Self, CairoError> {
|
||||
Ok(Self {
|
||||
context,
|
||||
width: w,
|
||||
height: h,
|
||||
init_flag: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DrawingBackend for CairoBackend<'a> {
|
||||
type ErrorType = cairo::Error;
|
||||
|
||||
fn get_size(&self) -> (u32, u32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
if !self.init_flag {
|
||||
let (x0, y0, x1, y1) = self
|
||||
.context
|
||||
.clip_extents()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
|
||||
self.context.scale(
|
||||
(x1 - x0) / f64::from(self.width),
|
||||
(y1 - y0) / f64::from(self.height),
|
||||
);
|
||||
|
||||
self.init_flag = true;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_pixel(
|
||||
&mut self,
|
||||
point: BackendCoord,
|
||||
color: BackendColor,
|
||||
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
self.context
|
||||
.rectangle(f64::from(point.0), f64::from(point.1), 1.0, 1.0);
|
||||
self.context.set_source_rgba(
|
||||
f64::from(color.rgb.0) / 255.0,
|
||||
f64::from(color.rgb.1) / 255.0,
|
||||
f64::from(color.rgb.2) / 255.0,
|
||||
color.alpha,
|
||||
);
|
||||
|
||||
self.context
|
||||
.fill()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_line<S: BackendStyle>(
|
||||
&mut self,
|
||||
from: BackendCoord,
|
||||
to: BackendCoord,
|
||||
style: &S,
|
||||
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
self.set_color(&style.color());
|
||||
self.set_stroke_width(style.stroke_width());
|
||||
|
||||
self.context.move_to(f64::from(from.0), f64::from(from.1));
|
||||
self.context.line_to(f64::from(to.0), f64::from(to.1));
|
||||
|
||||
self.context
|
||||
.stroke()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_rect<S: BackendStyle>(
|
||||
&mut self,
|
||||
upper_left: BackendCoord,
|
||||
bottom_right: BackendCoord,
|
||||
style: &S,
|
||||
fill: bool,
|
||||
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
self.set_color(&style.color());
|
||||
self.set_stroke_width(style.stroke_width());
|
||||
|
||||
self.context.rectangle(
|
||||
f64::from(upper_left.0),
|
||||
f64::from(upper_left.1),
|
||||
f64::from(bottom_right.0 - upper_left.0),
|
||||
f64::from(bottom_right.1 - upper_left.1),
|
||||
);
|
||||
|
||||
if fill {
|
||||
self.context
|
||||
.fill()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
} else {
|
||||
self.context
|
||||
.stroke()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
|
||||
&mut self,
|
||||
path: I,
|
||||
style: &S,
|
||||
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
self.set_color(&style.color());
|
||||
self.set_stroke_width(style.stroke_width());
|
||||
|
||||
let mut path = path.into_iter();
|
||||
if let Some((x, y)) = path.next() {
|
||||
self.context.move_to(f64::from(x), f64::from(y));
|
||||
}
|
||||
|
||||
for (x, y) in path {
|
||||
self.context.line_to(f64::from(x), f64::from(y));
|
||||
}
|
||||
|
||||
self.context
|
||||
.stroke()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
|
||||
&mut self,
|
||||
path: I,
|
||||
style: &S,
|
||||
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
self.set_color(&style.color());
|
||||
self.set_stroke_width(style.stroke_width());
|
||||
|
||||
let mut path = path.into_iter();
|
||||
|
||||
if let Some((x, y)) = path.next() {
|
||||
self.context.move_to(f64::from(x), f64::from(y));
|
||||
|
||||
for (x, y) in path {
|
||||
self.context.line_to(f64::from(x), f64::from(y));
|
||||
}
|
||||
|
||||
self.context.close_path();
|
||||
self.context
|
||||
.fill()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_circle<S: BackendStyle>(
|
||||
&mut self,
|
||||
center: BackendCoord,
|
||||
radius: u32,
|
||||
style: &S,
|
||||
fill: bool,
|
||||
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
self.set_color(&style.color());
|
||||
self.set_stroke_width(style.stroke_width());
|
||||
|
||||
self.context.new_sub_path();
|
||||
self.context.arc(
|
||||
f64::from(center.0),
|
||||
f64::from(center.1),
|
||||
f64::from(radius),
|
||||
0.0,
|
||||
std::f64::consts::PI * 2.0,
|
||||
);
|
||||
|
||||
if fill {
|
||||
self.context
|
||||
.fill()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
} else {
|
||||
self.context
|
||||
.stroke()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn estimate_text_size<S: BackendTextStyle>(
|
||||
&self,
|
||||
text: &str,
|
||||
font: &S,
|
||||
) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {
|
||||
self.set_font(font);
|
||||
|
||||
let extents = self
|
||||
.context
|
||||
.text_extents(text)
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
|
||||
Ok((extents.width() as u32, extents.height() as u32))
|
||||
}
|
||||
|
||||
fn draw_text<S: BackendTextStyle>(
|
||||
&mut self,
|
||||
text: &str,
|
||||
style: &S,
|
||||
pos: BackendCoord,
|
||||
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
|
||||
let color = style.color();
|
||||
let (mut x, mut y) = (pos.0, pos.1);
|
||||
|
||||
let degree = match style.transform() {
|
||||
FontTransform::None => 0.0,
|
||||
FontTransform::Rotate90 => 90.0,
|
||||
FontTransform::Rotate180 => 180.0,
|
||||
FontTransform::Rotate270 => 270.0,
|
||||
//FontTransform::RotateAngle(angle) => angle as f64,
|
||||
} / 180.0
|
||||
* std::f64::consts::PI;
|
||||
|
||||
if degree != 0.0 {
|
||||
self.context
|
||||
.save()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
self.context.translate(f64::from(x), f64::from(y));
|
||||
self.context.rotate(degree);
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
self.set_font(style);
|
||||
self.set_color(&color);
|
||||
|
||||
let extents = self
|
||||
.context
|
||||
.text_extents(text)
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
|
||||
let dx = match style.anchor().h_pos {
|
||||
HPos::Left => 0.0,
|
||||
HPos::Right => -extents.width(),
|
||||
HPos::Center => -extents.width() / 2.0,
|
||||
};
|
||||
let dy = match style.anchor().v_pos {
|
||||
VPos::Top => extents.height(),
|
||||
VPos::Center => extents.height() / 2.0,
|
||||
VPos::Bottom => 0.0,
|
||||
};
|
||||
|
||||
self.context.move_to(
|
||||
f64::from(x) + dx - extents.x_bearing(),
|
||||
f64::from(y) + dy - extents.y_bearing() - extents.height(),
|
||||
);
|
||||
|
||||
self.context
|
||||
.show_text(text)
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
|
||||
if degree != 0.0 {
|
||||
self.context
|
||||
.restore()
|
||||
.map_err(DrawingErrorKind::DrawingError)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,14 @@
|
||||
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,
|
||||
};
|
||||
use super::proj::ProjectionS;
|
||||
|
||||
pub struct Mapper {
|
||||
proj: Proj,
|
||||
range: (Range<f64>, Range<f64>),
|
||||
bounds: (f64, f64, f64, f64),
|
||||
}
|
||||
unsafe impl Sync for Mapper {}
|
||||
|
||||
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);
|
||||
@ -189,6 +183,4 @@ impl Mapper {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resample(&self) {}
|
||||
}
|
||||
152
src/coords/mod.rs
Normal file
152
src/coords/mod.rs
Normal file
@ -0,0 +1,152 @@
|
||||
use num_traits::AsPrimitive;
|
||||
use num_traits::Num;
|
||||
pub mod mapper;
|
||||
pub mod proj;
|
||||
pub mod wgs84;
|
||||
|
||||
pub use mapper::Mapper;
|
||||
pub use wgs84::LatLonCoord;
|
||||
|
||||
pub type ScreenCoord = (f64, f64);
|
||||
type Lat = f64;
|
||||
type Lon = f64;
|
||||
|
||||
pub trait Coord<T: Num> {
|
||||
fn map(&self, axis_1: T, axis_2: T) -> ScreenCoord;
|
||||
fn dim1_range(&self) -> (T, T);
|
||||
fn dim2_range(&self) -> (T, T);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Range(pub f64, pub f64);
|
||||
|
||||
impl Range {
|
||||
pub fn key_points(&self, max_points: usize) -> Vec<f64> {
|
||||
if max_points == 0 {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let range = (self.0.min(self.1) as f64, self.1.max(self.0) as f64);
|
||||
assert!(!(range.0.is_nan() || range.1.is_nan()));
|
||||
if (range.0 - range.1).abs() < std::f64::EPSILON {
|
||||
return vec![range.0 as f64];
|
||||
}
|
||||
|
||||
let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
|
||||
// The value granularity controls how we round the values.
|
||||
// To avoid generating key points like 1.00000000001, we round to the nearest multiple of the
|
||||
// value granularity.
|
||||
// By default, we make the granularity as the 1/10 of the scale.
|
||||
let mut value_granularity = scale / 10.0;
|
||||
fn rem_euclid(a: f64, b: f64) -> f64 {
|
||||
let ret = if b > 0.0 {
|
||||
a - (a / b).floor() * b
|
||||
} else {
|
||||
a - (a / b).ceil() * b
|
||||
};
|
||||
if (ret - b).abs() < std::f64::EPSILON {
|
||||
0.0
|
||||
} else {
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we need to make sure that the loop invariant:
|
||||
// The scale must yield number of points than requested
|
||||
if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
|
||||
scale *= 10.0;
|
||||
value_granularity *= 10.0;
|
||||
}
|
||||
|
||||
'outer: loop {
|
||||
let old_scale = scale;
|
||||
for nxt in [2.0, 5.0, 10.0].iter() {
|
||||
let mut new_left = range.0 - rem_euclid(range.0, old_scale / nxt);
|
||||
if new_left < range.0 {
|
||||
new_left += old_scale / nxt;
|
||||
}
|
||||
let new_right = range.1 - rem_euclid(range.1, old_scale / nxt);
|
||||
|
||||
let npoints = 1.0 + ((new_right - new_left) / old_scale * nxt);
|
||||
|
||||
if npoints.round() as usize > max_points {
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
scale = old_scale / nxt;
|
||||
}
|
||||
scale = old_scale / 10.0;
|
||||
value_granularity /= 10.0;
|
||||
}
|
||||
|
||||
let mut ret = vec![];
|
||||
// In some extreme cases, left might be too big, so that (left + scale) - left == 0 due to
|
||||
// floating point error.
|
||||
// In this case, we may loop forever. To avoid this, we need to use two variables to store
|
||||
// the current left value. So we need keep a left_base and a left_relative.
|
||||
let left = {
|
||||
let mut value = range.0 - rem_euclid(range.0, scale);
|
||||
if value < range.0 {
|
||||
value += scale;
|
||||
}
|
||||
value
|
||||
};
|
||||
let left_base = (left / value_granularity).floor() * value_granularity;
|
||||
let mut left_relative = left - left_base;
|
||||
let right = range.1 - rem_euclid(range.1, scale);
|
||||
while (right - left_relative - left_base) >= -std::f64::EPSILON {
|
||||
let new_left_relative = (left_relative / value_granularity).round() * value_granularity;
|
||||
if new_left_relative < 0.0 {
|
||||
left_relative += value_granularity;
|
||||
}
|
||||
ret.push((left_relative + left_base) as f64);
|
||||
left_relative += scale;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsPrimitive<f64> + Num> From<(T, T)> for Range {
|
||||
fn from(value: (T, T)) -> Self {
|
||||
let value = (value.0.as_(), value.1.as_());
|
||||
let (_min, _max) = (value.0.min(value.1), value.0.max(value.1));
|
||||
Self(_min, _max)
|
||||
}
|
||||
}
|
||||
|
||||
// impl<T, Raw> RadarData2d<T, Raw>
|
||||
// where
|
||||
// T: Num + Clone + PartialEq + PartialOrd,
|
||||
// Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
// {
|
||||
// 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_dim2_len, mesh_dim1_len));
|
||||
// let mut d2 = Array2::<f64>::zeros((mesh_dim2_len, mesh_dim1_len));
|
||||
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Ok(AfterMapping2d {
|
||||
// dim1: d1,
|
||||
// dim2: d2,
|
||||
// data: self.data.view(),
|
||||
// coord_type: self.coord_type.clone(),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
80
src/coords/proj/mercator.rs
Normal file
80
src/coords/proj/mercator.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use crate::coords::Range;
|
||||
|
||||
use super::ProjectionS;
|
||||
use geo_macros::Prj;
|
||||
|
||||
#[derive(Prj)]
|
||||
/// A struct representing the Mercator projection.
|
||||
pub struct Mercator {
|
||||
/// The central longitude of the projection.
|
||||
pub central_lon: f64,
|
||||
/// The minimum latitude of the projection.
|
||||
pub min_latitude: f64,
|
||||
/// The maximum latitude of the projection.
|
||||
pub max_latitude: f64,
|
||||
/// The false easting of the projection.
|
||||
pub false_easting: f64,
|
||||
/// The false northing of the projection.
|
||||
pub false_northing: f64,
|
||||
/// The latitude of true scale of the projection.
|
||||
pub latitude_true_scale: f64,
|
||||
}
|
||||
|
||||
fn proj_string<'a>(vs: Vec<(&'a str, &'a str)>) -> String {
|
||||
vs.into_iter()
|
||||
.map(|(option, value)| format!("+{}={}", option, value))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
impl Mercator {
|
||||
/// Creates a new instance of the `Mercator` projection with default values.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
central_lon: 0.0,
|
||||
min_latitude: -82.0,
|
||||
max_latitude: 82.0,
|
||||
false_easting: 0.0,
|
||||
false_northing: 0.0,
|
||||
latitude_true_scale: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProjectionS for Mercator {
|
||||
fn build(&self) -> String {
|
||||
let _central_lon = format!("{:.1}", &self.central_lon);
|
||||
let _false_easting = format!("{:.1}", &self.false_easting);
|
||||
let _false_northing = format!("{:.1}", &self.false_northing);
|
||||
let _ts = format!("{:.1}", &self.latitude_true_scale);
|
||||
|
||||
let input = vec![
|
||||
("proj", "merc"),
|
||||
("lon_0", _central_lon.as_str()),
|
||||
("x_0", _false_easting.as_str()),
|
||||
("y_0", _false_northing.as_str()),
|
||||
("lat_ts", _ts.as_str()),
|
||||
("units", "m"),
|
||||
("ellps", "GRS80"),
|
||||
];
|
||||
let _proj_string = proj_string(input);
|
||||
_proj_string
|
||||
}
|
||||
|
||||
fn logic_range(&self, lon_range: Option<Range>, lat_range: Option<Range>) -> (Range, Range) {
|
||||
let lon_range = lon_range.unwrap_or(Range {
|
||||
0: -180f64,
|
||||
1: 180f64,
|
||||
});
|
||||
let lat_range = lat_range.unwrap_or(Range {
|
||||
0: self.min_latitude,
|
||||
1: self.max_latitude,
|
||||
});
|
||||
|
||||
let lat_range = Range {
|
||||
0: lat_range.0.max(self.min_latitude),
|
||||
1: lat_range.1.min(self.max_latitude),
|
||||
};
|
||||
(lon_range, lat_range)
|
||||
}
|
||||
}
|
||||
79
src/coords/proj/mod.rs
Normal file
79
src/coords/proj/mod.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use super::{Lat, Lon, Range};
|
||||
use proj::Proj;
|
||||
use proj5;
|
||||
use thiserror::Error;
|
||||
mod mercator;
|
||||
|
||||
pub use mercator::Mercator;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Projs {
|
||||
PlateCarree,
|
||||
LambertConformal,
|
||||
LambertCylindrical,
|
||||
Mercator,
|
||||
}
|
||||
|
||||
/// A trait for defining a projection.
|
||||
pub trait ProjectionS {
|
||||
/// Returns a proj-string of the projection.
|
||||
fn build(&self) -> String;
|
||||
|
||||
/// Returns the logical range of the projection.
|
||||
/// In common, different projections have different logical ranges.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lon_range` - An optional longitude range.
|
||||
/// * `lat_range` - An optional latitude range.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A tuple containing the longitude and latitude ranges.
|
||||
fn logic_range(&self, lon_range: Option<Range>, lat_range: Option<Range>) -> (Range, Range);
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(super) enum ProjError {
|
||||
#[error("proj error")]
|
||||
ProjError(#[from] proj::ProjError),
|
||||
}
|
||||
|
||||
pub(super) struct PCS<T: ProjectionS> {
|
||||
pub lon_range: Range,
|
||||
pub lat_range: Range,
|
||||
pub proj_param: T,
|
||||
// pub proj_target: proj5::CoordinateBuf,
|
||||
pub transformer: Proj,
|
||||
}
|
||||
|
||||
impl<T: ProjectionS> PCS<T> {
|
||||
pub(super) fn new(proj_param: T, lon_range: Option<Range>, lat_range: Option<Range>) -> Self {
|
||||
let (lon_range, lat_range) = proj_param.logic_range(lon_range, lat_range);
|
||||
Self {
|
||||
lon_range,
|
||||
lat_range,
|
||||
transformer: Proj::new(proj_param.build().as_str()).unwrap(),
|
||||
proj_param: proj_param,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bbox(&self) -> Result<(Range, Range), ProjError> {
|
||||
let _proj_transformer = &self.transformer;
|
||||
let lb = (self.lon_range.0.to_radians(), self.lat_range.0.to_radians());
|
||||
let rt = (self.lon_range.1.to_radians(), self.lat_range.1.to_radians());
|
||||
|
||||
let bl = _proj_transformer.convert(lb)?;
|
||||
let rt = _proj_transformer.convert(rt)?;
|
||||
|
||||
Ok((Range::from((bl.0, rt.0)), Range::from((bl.1, rt.1))))
|
||||
}
|
||||
|
||||
pub fn map(&self, lon_lat: (Lat, Lon)) -> (f64, f64) {
|
||||
let _proj_transformer = &self.transformer;
|
||||
let _lon_lat = _proj_transformer
|
||||
.convert((lon_lat.0.to_radians(), lon_lat.1.to_radians()))
|
||||
.unwrap();
|
||||
|
||||
_lon_lat
|
||||
}
|
||||
}
|
||||
88
src/coords/wgs84.rs
Normal file
88
src/coords/wgs84.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use super::proj::{ProjectionS, PCS};
|
||||
use super::Coord;
|
||||
use super::{Lat, Lon, Range};
|
||||
use proj::ProjError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CoordError {
|
||||
#[error("")]
|
||||
ProjError {
|
||||
#[from]
|
||||
source: ProjError,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct LatLonCoord<T: ProjectionS> {
|
||||
actual: (Range, Range),
|
||||
logical: (Range, Range),
|
||||
pcs: PCS<T>,
|
||||
}
|
||||
|
||||
impl<T: ProjectionS> LatLonCoord<T> {
|
||||
/// Creates a new `LatLonCoord` instance.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lon` - An optional longitude range.
|
||||
/// * `lat` - An optional latitude range.
|
||||
/// * `actual` - A tuple containing the actual ranges.
|
||||
/// * `project` - A projection.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new `LatLonCoord` instance.
|
||||
pub fn new(
|
||||
lon: Option<Range>,
|
||||
lat: Option<Range>,
|
||||
actual: ((i32, i32), (i32, i32)),
|
||||
// actual: (Range, Range),
|
||||
project: T,
|
||||
) -> Self {
|
||||
let pcs = PCS::new(project, lon, lat);
|
||||
let _box = pcs.bbox().unwrap();
|
||||
Self {
|
||||
actual: (Range::from(actual.0), Range::from(actual.1)),
|
||||
pcs: pcs,
|
||||
logical: _box,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_actual(&mut self, actual: ((i32, i32), (i32, i32))) {
|
||||
self.actual = (Range::from(actual.0), Range::from(actual.1));
|
||||
}
|
||||
|
||||
pub fn lon_range(&self) -> Range {
|
||||
self.pcs.lon_range
|
||||
}
|
||||
|
||||
pub fn lat_range(&self) -> Range {
|
||||
self.pcs.lat_range
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ProjectionS> Coord<f64> for LatLonCoord<T> {
|
||||
fn map(&self, axis_1: f64, axis_2: f64) -> super::ScreenCoord {
|
||||
let point = self.pcs.map((axis_1, axis_2));
|
||||
let logical_dim1_span = self.logical.0 .1 - self.logical.0 .0;
|
||||
|
||||
let dim1_rate = (point.0 - self.logical.0 .0) / logical_dim1_span;
|
||||
let logical_dim2_span = self.logical.1 .1 - self.logical.1 .0;
|
||||
let dim2_rate = (point.1 - self.logical.1 .0) / logical_dim2_span;
|
||||
|
||||
(
|
||||
(dim1_rate * (self.actual.0 .1 - self.actual.0 .0) as f64) + self.actual.0 .0,
|
||||
(dim2_rate * (self.actual.1 .1 - self.actual.1 .0) as f64) + self.actual.1 .0,
|
||||
)
|
||||
}
|
||||
|
||||
fn dim1_range(&self) -> (f64, f64) {
|
||||
let v = self.lon_range();
|
||||
(v.0, v.1)
|
||||
}
|
||||
|
||||
fn dim2_range(&self) -> (f64, f64) {
|
||||
let v = self.lat_range();
|
||||
(v.0, v.1)
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
use super::RadarData2d;
|
||||
|
||||
pub type RadarReflectivity<Raw> = RadarData2d<i8, Raw>;
|
||||
101
src/data/mod.rs
101
src/data/mod.rs
@ -1,36 +1,22 @@
|
||||
use crate::errors::DataError;
|
||||
use image::RgbImage;
|
||||
use ndarray::{
|
||||
s, Array, Array1, Array2, Array3, ArrayBase, Axis, Ix1, Ix2, OwnedRepr, RawDataClone, ViewRepr,
|
||||
s, Array, Array1, Array2, Array3, ArrayBase, Axis, DataMut, Ix1, Ix2, OwnedRepr, RawDataClone,
|
||||
ViewRepr,
|
||||
};
|
||||
use npyz::{npz::NpzArchive, Deserialize};
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
use quadtree_rs::{area::AreaBuilder, Quadtree};
|
||||
use std::{self, f64::consts::PI, fmt::Debug, io::BufReader, path::Path};
|
||||
use thiserror::Error;
|
||||
|
||||
mod concrete_data;
|
||||
pub mod mapper;
|
||||
pub type Radar2d<T> = RadarData2d<T, OwnedRepr<T>>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DataError {
|
||||
#[error("value")]
|
||||
FormatError,
|
||||
#[error("")]
|
||||
IOError {
|
||||
#[from]
|
||||
source: std::io::Error,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum DownSampleMeth {
|
||||
STD,
|
||||
MEAN,
|
||||
VAR,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CoordType {
|
||||
Polar,
|
||||
LatLon,
|
||||
pub trait MultiDimensionData<T>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
{
|
||||
fn map_by_fn<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnMut(&mut T);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -47,16 +33,12 @@ where
|
||||
pub coord_type: CoordType,
|
||||
}
|
||||
|
||||
pub type Radar2d<T> = RadarData2d<T, OwnedRepr<T>>;
|
||||
|
||||
impl<T: Num + Clone + PartialEq + PartialOrd> Radar2d<T> {
|
||||
pub fn load(path: impl AsRef<Path>, meth: impl DataLoader<Self>) -> Result<Self, DataError> {
|
||||
pub fn load(path: impl AsRef<Path>, meth: impl DataLoader<T, Self>) -> Result<Self, DataError> {
|
||||
Ok(meth.load(path)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Radar2dRef<'a, T> = RadarData2d<T, ViewRepr<&'a T>>;
|
||||
|
||||
pub struct RadarData3d<T, X = f64, Y = f64, Z = f64>
|
||||
where
|
||||
T: Num,
|
||||
@ -70,8 +52,6 @@ where
|
||||
pub data: Array3<T>,
|
||||
}
|
||||
|
||||
pub trait MultiDimensionData {}
|
||||
|
||||
impl<T, Raw> RadarData2d<T, Raw>
|
||||
where
|
||||
T: Num + AsPrimitive<f64> + FromPrimitive + Clone + Debug + PartialOrd + PartialEq,
|
||||
@ -95,17 +75,27 @@ where
|
||||
|
||||
let mut output: Array2<T> = Array2::zeros(output_shape);
|
||||
|
||||
let mut dim1 = Array1::zeros(output_shape.1);
|
||||
let mut dim2 = Array1::zeros(output_shape.0);
|
||||
let dim1 = Array1::linspace(
|
||||
*self.dim1.first().unwrap(),
|
||||
*self.dim1.last().unwrap(),
|
||||
output_shape.1,
|
||||
);
|
||||
|
||||
let dim2 = Array1::linspace(
|
||||
*self.dim2.first().unwrap(),
|
||||
*self.dim2.last().unwrap(),
|
||||
output_shape.0,
|
||||
);
|
||||
// let mut dim2 = Array1::zeros(output_shape.0);
|
||||
|
||||
output.iter_mut().enumerate().for_each(|(s, vv)| {
|
||||
let ri = s / output_shape.1;
|
||||
let ci = s % output_shape.1;
|
||||
let src_yf0 = scale_y * ci as f64;
|
||||
let src_yf0 = scale_y * ri as f64;
|
||||
let src_yf1 = src_yf0 + scale_y;
|
||||
let src_y0 = src_yf0.floor() as usize;
|
||||
let src_y1 = src_yf1.floor() as usize;
|
||||
let src_xf0 = scale_x * ri as f64;
|
||||
let src_xf0 = scale_x * ci as f64;
|
||||
let src_xf1 = src_xf0 + scale_x;
|
||||
let src_x0 = src_xf0.floor() as usize;
|
||||
let src_x1 = src_xf1.floor() as usize;
|
||||
@ -116,9 +106,6 @@ where
|
||||
src_yf0, src_yf1, src_xf0, src_xf1, src_y0, src_y1, src_x0, src_x1, vv,
|
||||
),
|
||||
}
|
||||
|
||||
dim1[ci] = self.dim1[src_x0];
|
||||
dim2[ri] = self.dim2[src_y0];
|
||||
});
|
||||
|
||||
if let DownSampleMeth::STD = meth {
|
||||
@ -202,8 +189,8 @@ where
|
||||
if w_sum < f64::EPSILON {
|
||||
return;
|
||||
}
|
||||
|
||||
*row = T::from_f64((wvv_sum * w_sum - wv_sum * wv_sum) / w_sum / w_sum).unwrap();
|
||||
let v = ((wvv_sum * w_sum - wv_sum * wv_sum) / w_sum / w_sum).clamp(-125f64, 124f64);
|
||||
*row = T::from_f64(v).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,15 +232,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Raw> MultiDimensionData for RadarData2d<T, Raw>
|
||||
impl<T, Raw> MultiDimensionData<T> for RadarData2d<T, Raw>
|
||||
where
|
||||
T: Num + Clone,
|
||||
T: PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + RawDataClone,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + RawDataClone + DataMut,
|
||||
{
|
||||
fn map_by_fn<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnMut(&mut T),
|
||||
{
|
||||
self.data.map_inplace(f);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DataLoader<T: MultiDimensionData> {
|
||||
pub trait DataLoader<V, T>
|
||||
where
|
||||
V: Num + Clone + PartialEq + PartialOrd,
|
||||
T: MultiDimensionData<V>,
|
||||
{
|
||||
fn load<P: AsRef<Path>>(&self, path: P) -> Result<T, DataError>;
|
||||
}
|
||||
|
||||
@ -290,7 +287,7 @@ impl Npz {
|
||||
fn load_3d(&self, data: &mut NpzArchive<BufReader<std::fs::File>>) {}
|
||||
}
|
||||
|
||||
impl<T> DataLoader<RadarData2d<T, OwnedRepr<T>>> for Npz
|
||||
impl<T> DataLoader<T, RadarData2d<T, OwnedRepr<T>>> for Npz
|
||||
where
|
||||
T: Num + Clone + Deserialize,
|
||||
T: PartialEq + PartialOrd,
|
||||
@ -469,3 +466,15 @@ fn windowed_sinc(x: f64, y: f64) -> f64 {
|
||||
};
|
||||
sinc * window
|
||||
}
|
||||
|
||||
pub enum DownSampleMeth {
|
||||
STD,
|
||||
MEAN,
|
||||
VAR,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CoordType {
|
||||
Polar,
|
||||
LatLon,
|
||||
}
|
||||
|
||||
22
src/errors.rs
Normal file
22
src/errors.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use proj::ProjError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DataError {
|
||||
#[error("value")]
|
||||
FormatError,
|
||||
#[error("")]
|
||||
IOError {
|
||||
#[from]
|
||||
source: std::io::Error,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PipelineError {
|
||||
#[error("proj error")]
|
||||
ProjError {
|
||||
#[from]
|
||||
source: ProjError,
|
||||
},
|
||||
}
|
||||
13
src/main.rs
13
src/main.rs
@ -1,16 +1,18 @@
|
||||
use data::mapper::Mapper;
|
||||
use coords::proj::Mercator;
|
||||
use coords::Mapper;
|
||||
use data::{Npz, Radar2d};
|
||||
use gtk::prelude::*;
|
||||
use gtk::{gio, glib, Application, ApplicationWindow};
|
||||
use std::ptr;
|
||||
mod coords;
|
||||
mod data;
|
||||
mod errors;
|
||||
mod monitor;
|
||||
mod painter;
|
||||
mod pipeline;
|
||||
mod render;
|
||||
mod tree;
|
||||
mod window;
|
||||
use monitor::Monitor;
|
||||
use painter::wgs84::{LatLonCoord, Mercator, ProjectionS, Range};
|
||||
use render::{BackgroundConfig, BackgroundWidget, ForegroundConfig, ForegroundWidget, Render};
|
||||
|
||||
const APP_ID: &str = "org.gtk_rs.HelloWorld2";
|
||||
@ -57,10 +59,8 @@ fn build_ui(app: &Application) {
|
||||
let foreground_widget = ForegroundWidget::new(foreground_config);
|
||||
let render = Render::new(background_widget, foreground_widget);
|
||||
|
||||
let path = "/home/tsuki/projects/radar-g/test2.npz";
|
||||
|
||||
let path = "/Users/ruomu/projects/cinrad_g/test2.npz";
|
||||
let data = Radar2d::<i8>::load(path, Npz).unwrap();
|
||||
|
||||
let projection = Mercator::new();
|
||||
let mut mapper: Mapper = projection.into();
|
||||
mapper.set_lat_range(29.960..30.764);
|
||||
@ -68,7 +68,6 @@ fn build_ui(app: &Application) {
|
||||
|
||||
render.set_mapper(mapper);
|
||||
let monitor = Monitor::new(render);
|
||||
|
||||
monitor.load_data_2d(data).unwrap();
|
||||
|
||||
window.set_child(Some(&monitor));
|
||||
|
||||
@ -22,10 +22,9 @@ impl Monitor {
|
||||
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));
|
||||
s.imp().renderer.borrow().change_cfg(
|
||||
|cfg| cfg.pointer_location = (x as f32, y as f32)
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@ -34,10 +33,10 @@ impl Monitor {
|
||||
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),
|
||||
);
|
||||
renderer.change_cfg(|cfg| {
|
||||
let current_scale = cfg.scale;
|
||||
cfg.scale = (current_scale + y as f32 / 100.0).max(1.0).min(5.0);
|
||||
});
|
||||
Inhibit(false)
|
||||
}
|
||||
));
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
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::{mapper::Mapper, RadarData2d};
|
||||
|
||||
use super::AfterMapping2d;
|
||||
pub mod wgs84;
|
||||
|
||||
pub type ScreenCoord = (f64, f64);
|
||||
|
||||
pub trait Coord<T: Num>: Sync + Send {
|
||||
fn map(&self, axis_1: T, axis_2: T) -> ScreenCoord;
|
||||
|
||||
fn dim1_range(&self) -> (T, T);
|
||||
fn dim2_range(&self) -> (T, T);
|
||||
}
|
||||
|
||||
impl<T, Raw> RadarData2d<T, Raw>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
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_dim2_len, mesh_dim1_len));
|
||||
let mut d2 = Array2::<f64>::zeros((mesh_dim2_len, mesh_dim1_len));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(AfterMapping2d {
|
||||
dim1: d1,
|
||||
dim2: d2,
|
||||
data: self.data.view(),
|
||||
coord_type: self.coord_type.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,334 +0,0 @@
|
||||
use super::Coord;
|
||||
use geo_macros::Prj;
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
use proj::{Proj, ProjError};
|
||||
use thiserror::Error;
|
||||
|
||||
type Lat = f64;
|
||||
type Lon = f64;
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Range(pub f64, pub f64);
|
||||
|
||||
impl Range {
|
||||
pub fn key_points(&self, max_points: usize) -> Vec<f64> {
|
||||
if max_points == 0 {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let range = (self.0.min(self.1) as f64, self.1.max(self.0) as f64);
|
||||
assert!(!(range.0.is_nan() || range.1.is_nan()));
|
||||
if (range.0 - range.1).abs() < std::f64::EPSILON {
|
||||
return vec![range.0 as f64];
|
||||
}
|
||||
|
||||
let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
|
||||
// The value granularity controls how we round the values.
|
||||
// To avoid generating key points like 1.00000000001, we round to the nearest multiple of the
|
||||
// value granularity.
|
||||
// By default, we make the granularity as the 1/10 of the scale.
|
||||
let mut value_granularity = scale / 10.0;
|
||||
fn rem_euclid(a: f64, b: f64) -> f64 {
|
||||
let ret = if b > 0.0 {
|
||||
a - (a / b).floor() * b
|
||||
} else {
|
||||
a - (a / b).ceil() * b
|
||||
};
|
||||
if (ret - b).abs() < std::f64::EPSILON {
|
||||
0.0
|
||||
} else {
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we need to make sure that the loop invariant:
|
||||
// The scale must yield number of points than requested
|
||||
if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
|
||||
scale *= 10.0;
|
||||
value_granularity *= 10.0;
|
||||
}
|
||||
|
||||
'outer: loop {
|
||||
let old_scale = scale;
|
||||
for nxt in [2.0, 5.0, 10.0].iter() {
|
||||
let mut new_left = range.0 - rem_euclid(range.0, old_scale / nxt);
|
||||
if new_left < range.0 {
|
||||
new_left += old_scale / nxt;
|
||||
}
|
||||
let new_right = range.1 - rem_euclid(range.1, old_scale / nxt);
|
||||
|
||||
let npoints = 1.0 + ((new_right - new_left) / old_scale * nxt);
|
||||
|
||||
if npoints.round() as usize > max_points {
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
scale = old_scale / nxt;
|
||||
}
|
||||
scale = old_scale / 10.0;
|
||||
value_granularity /= 10.0;
|
||||
}
|
||||
|
||||
let mut ret = vec![];
|
||||
// In some extreme cases, left might be too big, so that (left + scale) - left == 0 due to
|
||||
// floating point error.
|
||||
// In this case, we may loop forever. To avoid this, we need to use two variables to store
|
||||
// the current left value. So we need keep a left_base and a left_relative.
|
||||
let left = {
|
||||
let mut value = range.0 - rem_euclid(range.0, scale);
|
||||
if value < range.0 {
|
||||
value += scale;
|
||||
}
|
||||
value
|
||||
};
|
||||
let left_base = (left / value_granularity).floor() * value_granularity;
|
||||
let mut left_relative = left - left_base;
|
||||
let right = range.1 - rem_euclid(range.1, scale);
|
||||
while (right - left_relative - left_base) >= -std::f64::EPSILON {
|
||||
let new_left_relative = (left_relative / value_granularity).round() * value_granularity;
|
||||
if new_left_relative < 0.0 {
|
||||
left_relative += value_granularity;
|
||||
}
|
||||
ret.push((left_relative + left_base) as f64);
|
||||
left_relative += scale;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CoordError {
|
||||
#[error("")]
|
||||
ProjError {
|
||||
#[from]
|
||||
source: ProjError,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T: AsPrimitive<f64> + Num> From<(T, T)> for Range {
|
||||
fn from(value: (T, T)) -> Self {
|
||||
let value = (value.0.as_(), value.1.as_());
|
||||
let (_min, _max) = (value.0.min(value.1), value.0.max(value.1));
|
||||
Self(_min, _max)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LatLonCoord<T: ProjectionS> {
|
||||
// actual: ((f64, f64), (f64, f64)),
|
||||
actual: (Range, Range),
|
||||
logical: (Range, Range),
|
||||
pcs: PCS<T>,
|
||||
}
|
||||
|
||||
struct PCS<T: ProjectionS> {
|
||||
pub lon_range: Range,
|
||||
pub lat_range: Range,
|
||||
pub proj_param: T,
|
||||
pub transformer: Proj,
|
||||
}
|
||||
|
||||
unsafe impl Sync for PCS<Mercator> {}
|
||||
unsafe impl Send for PCS<Mercator> {}
|
||||
|
||||
impl<T: ProjectionS> LatLonCoord<T> {
|
||||
/// Creates a new `LatLonCoord` instance.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lon` - An optional longitude range.
|
||||
/// * `lat` - An optional latitude range.
|
||||
/// * `actual` - A tuple containing the actual ranges.
|
||||
/// * `project` - A projection.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new `LatLonCoord` instance.
|
||||
pub fn new(
|
||||
lon: Option<Range>,
|
||||
lat: Option<Range>,
|
||||
actual: ((i32, i32), (i32, i32)),
|
||||
// actual: (Range, Range),
|
||||
project: T,
|
||||
) -> Self {
|
||||
let pcs = PCS::new(project, lon, lat);
|
||||
let _box = pcs.bbox().unwrap();
|
||||
Self {
|
||||
actual: (Range::from(actual.0), Range::from(actual.1)),
|
||||
pcs: pcs,
|
||||
logical: _box,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_actual(&mut self, actual: ((i32, i32), (i32, i32))) {
|
||||
self.actual = (Range::from(actual.0), Range::from(actual.1));
|
||||
}
|
||||
|
||||
pub fn lon_range(&self) -> Range {
|
||||
self.pcs.lon_range
|
||||
}
|
||||
|
||||
pub fn lat_range(&self) -> Range {
|
||||
self.pcs.lat_range
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ProjectionS> Sync for LatLonCoord<T> {}
|
||||
unsafe impl<T: ProjectionS> Send for LatLonCoord<T> {}
|
||||
|
||||
impl<T: ProjectionS> Coord<f64> for LatLonCoord<T> {
|
||||
fn map(&self, axis_1: f64, axis_2: f64) -> super::ScreenCoord {
|
||||
let point = self.pcs.map((axis_1, axis_2));
|
||||
let logical_dim1_span = self.logical.0 .1 - self.logical.0 .0;
|
||||
|
||||
let dim1_rate = (point.0 - self.logical.0 .0) / logical_dim1_span;
|
||||
let logical_dim2_span = self.logical.1 .1 - self.logical.1 .0;
|
||||
let dim2_rate = (point.1 - self.logical.1 .0) / logical_dim2_span;
|
||||
|
||||
(
|
||||
(dim1_rate * (self.actual.0 .1 - self.actual.0 .0) as f64) + self.actual.0 .0,
|
||||
(dim2_rate * (self.actual.1 .1 - self.actual.1 .0) as f64) + self.actual.1 .0,
|
||||
)
|
||||
}
|
||||
|
||||
fn dim1_range(&self) -> (f64, f64) {
|
||||
let v = self.lon_range();
|
||||
(v.0, v.1)
|
||||
}
|
||||
|
||||
fn dim2_range(&self) -> (f64, f64) {
|
||||
let v = self.lat_range();
|
||||
(v.0, v.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Projection {
|
||||
PlateCarree,
|
||||
LambertConformal,
|
||||
LambertCylindrical,
|
||||
Mercator,
|
||||
}
|
||||
|
||||
/// A trait for defining a projection.
|
||||
pub trait ProjectionS: Sync + Send {
|
||||
/// Returns a proj-string of the projection.
|
||||
fn build(&self) -> String;
|
||||
|
||||
/// Returns the logical range of the projection.
|
||||
/// In common, different projections have different logical ranges.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lon_range` - An optional longitude range.
|
||||
/// * `lat_range` - An optional latitude range.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A tuple containing the longitude and latitude ranges.
|
||||
fn logic_range(&self, lon_range: Option<Range>, lat_range: Option<Range>) -> (Range, Range);
|
||||
}
|
||||
|
||||
impl<T: ProjectionS> PCS<T> {
|
||||
fn new(proj_param: T, lon_range: Option<Range>, lat_range: Option<Range>) -> Self {
|
||||
let (lon_range, lat_range) = proj_param.logic_range(lon_range, lat_range);
|
||||
Self {
|
||||
lon_range,
|
||||
lat_range,
|
||||
transformer: Proj::new(proj_param.build().as_str()).unwrap(),
|
||||
proj_param: proj_param,
|
||||
}
|
||||
}
|
||||
|
||||
fn bbox(&self) -> Result<(Range, Range), CoordError> {
|
||||
let _proj_transformer = &self.transformer;
|
||||
let lb = (self.lon_range.0.to_radians(), self.lat_range.0.to_radians());
|
||||
let rt = (self.lon_range.1.to_radians(), self.lat_range.1.to_radians());
|
||||
|
||||
let bl = _proj_transformer.convert(lb)?;
|
||||
let rt = _proj_transformer.convert(rt)?;
|
||||
|
||||
Ok((Range::from((bl.0, rt.0)), Range::from((bl.1, rt.1))))
|
||||
}
|
||||
|
||||
fn map(&self, lon_lat: (Lat, Lon)) -> (f64, f64) {
|
||||
let _proj_transformer = &self.transformer;
|
||||
let _lon_lat = _proj_transformer
|
||||
.convert((lon_lat.0.to_radians(), lon_lat.1.to_radians()))
|
||||
.unwrap();
|
||||
|
||||
_lon_lat
|
||||
}
|
||||
}
|
||||
#[derive(Prj)]
|
||||
/// A struct representing the Mercator projection.
|
||||
pub struct Mercator {
|
||||
/// The central longitude of the projection.
|
||||
pub central_lon: f64,
|
||||
/// The minimum latitude of the projection.
|
||||
pub min_latitude: f64,
|
||||
/// The maximum latitude of the projection.
|
||||
pub max_latitude: f64,
|
||||
/// The false easting of the projection.
|
||||
pub false_easting: f64,
|
||||
/// The false northing of the projection.
|
||||
pub false_northing: f64,
|
||||
/// The latitude of true scale of the projection.
|
||||
pub latitude_true_scale: f64,
|
||||
}
|
||||
|
||||
fn proj_string<'a>(vs: Vec<(&'a str, &'a str)>) -> String {
|
||||
vs.into_iter()
|
||||
.map(|(option, value)| format!("+{}={}", option, value))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
impl Mercator {
|
||||
/// Creates a new instance of the `Mercator` projection with default values.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
central_lon: 0.0,
|
||||
min_latitude: -82.0,
|
||||
max_latitude: 82.0,
|
||||
false_easting: 0.0,
|
||||
false_northing: 0.0,
|
||||
latitude_true_scale: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProjectionS for Mercator {
|
||||
fn build(&self) -> String {
|
||||
let _central_lon = format!("{:.1}", &self.central_lon);
|
||||
let _false_easting = format!("{:.1}", &self.false_easting);
|
||||
let _false_northing = format!("{:.1}", &self.false_northing);
|
||||
let _ts = format!("{:.1}", &self.latitude_true_scale);
|
||||
|
||||
let input = vec![
|
||||
("proj", "merc"),
|
||||
("lon_0", _central_lon.as_str()),
|
||||
("x_0", _false_easting.as_str()),
|
||||
("y_0", _false_northing.as_str()),
|
||||
("lat_ts", _ts.as_str()),
|
||||
("units", "m"),
|
||||
("ellps", "GRS80"),
|
||||
];
|
||||
let _proj_string = proj_string(input);
|
||||
_proj_string
|
||||
}
|
||||
|
||||
fn logic_range(&self, lon_range: Option<Range>, lat_range: Option<Range>) -> (Range, Range) {
|
||||
let lon_range = lon_range.unwrap_or(Range {
|
||||
0: -180f64,
|
||||
1: 180f64,
|
||||
});
|
||||
let lat_range = lat_range.unwrap_or(Range {
|
||||
0: self.min_latitude,
|
||||
1: self.max_latitude,
|
||||
});
|
||||
|
||||
let lat_range = Range {
|
||||
0: lat_range.0.max(self.min_latitude),
|
||||
1: lat_range.1.min(self.max_latitude),
|
||||
};
|
||||
(lon_range, lat_range)
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
mod coords;
|
||||
mod painter;
|
||||
|
||||
pub use coords::wgs84;
|
||||
pub use coords::Coord;
|
||||
use ndarray::Ix2;
|
||||
use ndarray::ViewRepr;
|
||||
use num_traits::Num;
|
||||
pub use painter::Painter;
|
||||
|
||||
use crate::data::{RadarData2d, RadarData3d};
|
||||
|
||||
pub type AfterMapping2d<'a, T> = RadarData2d<T, ViewRepr<&'a T>, f64, f64, Ix2>;
|
||||
// pub type AfterMapping3d<T, Raw> = RadarData3d<T, Raw, f64, f64, Ix2>;
|
||||
|
||||
impl<'a, T> AfterMapping2d<'a, T>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
{
|
||||
pub fn traverse(&self, mut f: impl FnMut((usize, f64), (usize, f64)) -> ()) {
|
||||
self.dim1.iter().enumerate().for_each(|(i, v)| {
|
||||
self.dim2.iter().enumerate().for_each(|(j, u)| {
|
||||
f((i, *v), (j, *u));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,233 +0,0 @@
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
marker::PhantomData,
|
||||
rc::{self, Rc},
|
||||
};
|
||||
|
||||
use crate::{data::RadarData2d, tree::get};
|
||||
use ndarray::parallel::prelude::*;
|
||||
|
||||
use super::coords::{Coord, ScreenCoord};
|
||||
use gtk::{
|
||||
cairo::*,
|
||||
gdk::{builders::RGBABuilder, RGBA},
|
||||
};
|
||||
use num_traits::{FromPrimitive, Num};
|
||||
pub struct Painter<T: Num, C: Coord<T>> {
|
||||
width: u32,
|
||||
height: u32,
|
||||
shift: (f64, f64),
|
||||
coord: C,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Num + Clone, C: Coord<T>> Painter<T, C> {
|
||||
pub fn new(coord: C, width: u32, height: u32) -> Self {
|
||||
Self {
|
||||
coord,
|
||||
width,
|
||||
height,
|
||||
shift: (0f64, 0f64),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_color(&self, context: &Context, color: &RGBA) {
|
||||
context.set_source_rgba(
|
||||
color.red() as f64,
|
||||
color.green() as f64,
|
||||
color.blue() as f64,
|
||||
color.alpha() as f64,
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_pixel(&self, context: &Context, point: ScreenCoord, color: &RGBA) {
|
||||
context.rectangle(f64::from(point.0), f64::from(point.1), 1.0, 1.0);
|
||||
self.set_color(context, color);
|
||||
|
||||
context.fill();
|
||||
}
|
||||
|
||||
pub fn set_shift(&mut self, dx: f64, dy: f64) {
|
||||
self.shift = (self.shift.0 + dx, self.shift.1 + dy);
|
||||
}
|
||||
|
||||
fn draw_rect(
|
||||
&self,
|
||||
context: &Context,
|
||||
point: ScreenCoord,
|
||||
width: f64,
|
||||
height: f64,
|
||||
color: &RGBA,
|
||||
fill: bool,
|
||||
) {
|
||||
self.set_color(context, color);
|
||||
self.set_stroke_width(context, 1);
|
||||
context.rectangle(point.0, point.1, width, height);
|
||||
context.fill();
|
||||
// if fill {
|
||||
// self.context.fill();
|
||||
// } else {
|
||||
// self.context.stroke();
|
||||
// }
|
||||
}
|
||||
|
||||
fn translate(&self, context: &Context) {
|
||||
context.translate(self.shift.0, self.shift.1);
|
||||
}
|
||||
|
||||
fn set_stroke_width(&self, context: &Context, width: u32) {
|
||||
context.set_line_width(f64::from(width));
|
||||
}
|
||||
|
||||
fn draw_line(&self, context: &Context, start: ScreenCoord, end: ScreenCoord) {
|
||||
self.set_color(context, &RGBA::BLACK);
|
||||
self.set_stroke_width(context, 1);
|
||||
context.move_to(start.0, start.1);
|
||||
context.line_to(end.0, end.1);
|
||||
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
fn draw_path<I: IntoIterator<Item = ScreenCoord>>(&self, context: &Context, path: I) {
|
||||
let mut path = path.into_iter();
|
||||
if let Some((x, y)) = path.next() {
|
||||
context.move_to(x, y);
|
||||
}
|
||||
|
||||
for (x, y) in path {
|
||||
context.line_to(x, y);
|
||||
}
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Coord<f64>> Painter<f64, C> {
|
||||
pub fn draw_radar_2d<T: Num + Clone + PartialEq + PartialOrd + FromPrimitive + Copy, D>(
|
||||
&self,
|
||||
context: &Context,
|
||||
data: &RadarData2d<T, D>,
|
||||
) where
|
||||
D: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
let levels: Vec<T> = vec![
|
||||
T::from_i8(0).unwrap(),
|
||||
T::from_i8(5).unwrap(),
|
||||
T::from_i8(10).unwrap(),
|
||||
T::from_i8(15).unwrap(),
|
||||
T::from_i8(20).unwrap(),
|
||||
T::from_i8(25).unwrap(),
|
||||
T::from_i8(30).unwrap(),
|
||||
T::from_i8(35).unwrap(),
|
||||
T::from_i8(40).unwrap(),
|
||||
T::from_i8(45).unwrap(),
|
||||
T::from_i8(50).unwrap(),
|
||||
T::from_i8(55).unwrap(),
|
||||
T::from_i8(60).unwrap(),
|
||||
T::from_i8(65).unwrap(),
|
||||
];
|
||||
let colors = vec![
|
||||
RGBABuilder::default()
|
||||
.red(0 as f32 / 255.0)
|
||||
.green(172 as f32 / 255.0)
|
||||
.blue(164 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(192 as f32 / 255.0)
|
||||
.green(192 as f32 / 255.0)
|
||||
.blue(254 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(122 as f32 / 255.0)
|
||||
.green(114 as f32 / 255.0)
|
||||
.blue(238 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(30 as f32 / 255.0)
|
||||
.green(38 as f32 / 255.0)
|
||||
.blue(208 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(166 as f32 / 255.0)
|
||||
.green(252 as f32 / 255.0)
|
||||
.blue(168 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(0 as f32 / 255.0)
|
||||
.green(234 as f32 / 255.0)
|
||||
.blue(0 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(16 as f32 / 255.0)
|
||||
.green(146 as f32 / 255.0)
|
||||
.blue(26 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(252 as f32 / 255.0)
|
||||
.green(244 as f32 / 255.0)
|
||||
.blue(100 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(200 as f32 / 255.0)
|
||||
.green(200 as f32 / 255.0)
|
||||
.blue(2 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(140 as f32 / 255.0)
|
||||
.green(140 as f32 / 255.0)
|
||||
.blue(0 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(254 as f32 / 255.0)
|
||||
.green(172 as f32 / 255.0)
|
||||
.blue(172 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(254 as f32 / 255.0)
|
||||
.green(100 as f32 / 255.0)
|
||||
.blue(92 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(238 as f32 / 255.0)
|
||||
.green(2 as f32 / 255.0)
|
||||
.blue(48 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(212 as f32 / 255.0)
|
||||
.green(142 as f32 / 255.0)
|
||||
.blue(254 as f32 / 255.0)
|
||||
.build(),
|
||||
RGBABuilder::default()
|
||||
.red(170 as f32 / 255.0)
|
||||
.green(36 as f32 / 255.0)
|
||||
.blue(250 as f32 / 255.0)
|
||||
.build(),
|
||||
];
|
||||
|
||||
// let mut polygons: Vec<(shapefile::Polygon, _)> =
|
||||
// shapefile::read_as::<_, shapefile::Polygon, shapefile::dbase::Record>(
|
||||
// "/Users/ruomu/china/省界_region.shp",
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// for (polygon, _) in polygons.into_iter() {
|
||||
// let x_range = polygon.bbox().x_range();
|
||||
// let y_range = polygon.bbox().y_range();
|
||||
|
||||
// let lon_range = self.coord.dim1_range();
|
||||
// let lat_range = self.coord.dim2_range();
|
||||
// }
|
||||
|
||||
self.translate(context);
|
||||
data.data.outer_iter().enumerate().for_each(|(r_num, row)| {
|
||||
row.iter().enumerate().for_each(|(c_num, v)| {
|
||||
let point = self.coord.map(data.dim1[c_num], data.dim2[r_num]);
|
||||
if *v > T::from_i8(-125).unwrap() {
|
||||
let color = get(&levels, &colors, *v);
|
||||
// self.draw_pixel(point, &color);
|
||||
self.draw_rect(context, point, 1.5, 1.5, &color, true);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
94
src/pipeline/mod.rs
Normal file
94
src/pipeline/mod.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use anyhow::{Ok, Result};
|
||||
use femtovg;
|
||||
use geo_types::{line_string, LineString};
|
||||
use image::RgbImage;
|
||||
use ndarray::Array2;
|
||||
use num_traits::Num;
|
||||
|
||||
use crate::{
|
||||
coords::Mapper,
|
||||
data::{Radar2d, RadarData2d},
|
||||
};
|
||||
|
||||
pub struct Color(femtovg::Color);
|
||||
|
||||
impl Color {
|
||||
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
Self(femtovg::Color::rgba(r, g, b, a))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Pipeline<T> {
|
||||
type Output;
|
||||
fn run(&self, input: T) -> Result<Self::Output>;
|
||||
}
|
||||
pub struct ProjPipe {
|
||||
pub mapper: Mapper,
|
||||
}
|
||||
|
||||
impl<T, Raw> Pipeline<RadarData2d<T, Raw>> for ProjPipe
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
type Output = Array2<LineString>;
|
||||
|
||||
fn run(&self, input: RadarData2d<T, Raw>) -> Result<Self::Output> {
|
||||
let dim1 = input.dim1.view();
|
||||
let dim2 = input.dim2.view();
|
||||
|
||||
let shape = input.data.shape();
|
||||
let mut polygons = Vec::with_capacity(dim1.len() * dim2.len());
|
||||
|
||||
let d1_dpi = dim1[1] - dim1[0];
|
||||
let d2_dpi = dim2[1] - dim2[0];
|
||||
|
||||
for d1 in dim1 {
|
||||
for d2 in dim2 {
|
||||
let line: LineString = vec![
|
||||
(*d1, *d2),
|
||||
(*d1 + d1_dpi, *d2),
|
||||
(*d1 + d1_dpi, *d2 + d2_dpi),
|
||||
(*d1, *d2 + d2_dpi),
|
||||
(*d1, *d2),
|
||||
]
|
||||
.into();
|
||||
let projed_polygon = self.mapper.ring_map(&line)?;
|
||||
polygons.push(projed_polygon);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Array2::from_shape_vec([shape[0], shape[1]], polygons)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShadePipe<T: Num> {
|
||||
colors: Vec<Color>,
|
||||
levels: Vec<T>,
|
||||
}
|
||||
|
||||
struct ShaderPrepare(Array2<LineString>);
|
||||
|
||||
impl<T: Num + PartialOrd> ShadePipe<T> {
|
||||
pub fn new(levels: Vec<T>, colors: Vec<Color>) -> Self {
|
||||
Self { colors, levels }
|
||||
}
|
||||
|
||||
pub fn get_color(&self, v: T) -> &Color {
|
||||
let len = self.levels.len();
|
||||
|
||||
let mut left = 0;
|
||||
let mut right = len - 1;
|
||||
|
||||
while left < right - 1 {
|
||||
let middle = (right + left) / 2;
|
||||
if v > self.levels[middle] {
|
||||
left = middle;
|
||||
} else {
|
||||
right = middle;
|
||||
}
|
||||
}
|
||||
|
||||
&self.colors[left]
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,9 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::painter::Coord;
|
||||
use crate::render::{imp, WindowCoord};
|
||||
use femtovg::Paint;
|
||||
use geo_macros::Prj;
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Prj, Default)]
|
||||
pub struct BackgroundConfig {
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
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;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Prj, Default)]
|
||||
pub struct ForegroundConfig {
|
||||
@ -29,7 +26,7 @@ pub struct ForegroundWidget {
|
||||
pub(super) config: RefCell<ForegroundConfig>,
|
||||
pub(super) dim1: RefCell<Option<Array2<f64>>>,
|
||||
pub(super) dim2: RefCell<Option<Array2<f64>>>,
|
||||
pub(super) data: RefCell<Option<Array2<i8>>>,
|
||||
pub(super) image: RefCell<Option<Vec<u8>>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
mod imp;
|
||||
use crate::coords::Mapper;
|
||||
use crate::tree::get;
|
||||
use crate::{data::mapper::Mapper, render::WindowCoord};
|
||||
use femtovg::{renderer::OpenGl, Canvas, Path};
|
||||
use femtovg::{Color, Paint};
|
||||
use femtovg::{Color, FontId, ImageFlags, Paint};
|
||||
use geo_types::LineString;
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
use gtk::{ffi::gtk_widget_get_width, glib, graphene::Rect, prelude::SnapshotExtManual};
|
||||
use ndarray::parallel;
|
||||
use ndarray::Slice;
|
||||
use gtk::glib;
|
||||
use ndarray::{s, Array2, Axis, Zip};
|
||||
use std::cell::Ref;
|
||||
use std::ops::Range;
|
||||
|
||||
pub use self::imp::ForegroundConfig;
|
||||
|
||||
@ -37,87 +34,18 @@ impl ForegroundWidget {
|
||||
let canvas_height = canvas.height();
|
||||
let config = self.imp().config.borrow();
|
||||
|
||||
let dim1 = self.imp().dim1.borrow();
|
||||
let dim2 = self.imp().dim2.borrow();
|
||||
let data = self.imp().data.borrow();
|
||||
let img = canvas
|
||||
.load_image_file("test.png", ImageFlags::NEAREST)
|
||||
.unwrap();
|
||||
|
||||
let c = dim1.as_ref().unwrap().slice(s![..;3,..;3]);
|
||||
let d = dim2.as_ref().unwrap().slice(s![..;3,..;3]);
|
||||
let e = data.as_ref().unwrap().slice(s![..;3,..;3]);
|
||||
let mut path = Path::new();
|
||||
|
||||
let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
|
||||
path.rect(0.0, 0.0, canvas_width, canvas_height);
|
||||
canvas.fill_path(
|
||||
&path,
|
||||
&Paint::image(img, 0.0, 0.0, canvas_width, canvas_height, 0.0, 1.0),
|
||||
);
|
||||
|
||||
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.003, lat + 0.003)).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));
|
||||
});
|
||||
canvas.flush();
|
||||
}
|
||||
|
||||
@ -126,7 +54,80 @@ impl ForegroundWidget {
|
||||
self.imp().dim2.replace(Some(dims.1));
|
||||
}
|
||||
|
||||
pub(super) fn set_data(&mut self, data: Array2<i8>) {
|
||||
self.imp().data.replace(Some(data));
|
||||
pub(super) fn set_image(&mut self, image: Vec<u8>) {
|
||||
self.imp().image.replace(Some(image));
|
||||
}
|
||||
}
|
||||
|
||||
// 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,23 +1,29 @@
|
||||
use super::background::BackgroundWidget;
|
||||
use super::foreground::ForegroundWidget;
|
||||
use super::WindowCoord;
|
||||
use crate::data::mapper::Mapper;
|
||||
use crate::painter::wgs84::Mercator;
|
||||
use crate::coords::proj::Mercator;
|
||||
use crate::coords::Mapper;
|
||||
use femtovg::{Color, Paint, Path};
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::traits::{GLAreaExt, WidgetExt};
|
||||
use gtk::{glib, prelude::WidgetExtManual};
|
||||
use ndarray::Array2;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::RefCell;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct RenderConfig {
|
||||
pub dim1: Option<Array2<f64>>,
|
||||
pub dim2: Option<Array2<f64>>,
|
||||
pub scale: f32,
|
||||
pub pointer_location: WindowCoord,
|
||||
pub transform: WindowCoord,
|
||||
}
|
||||
|
||||
pub struct Render {
|
||||
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 config: RefCell<RenderConfig>,
|
||||
pub mapper: RefCell<Mapper>,
|
||||
canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
|
||||
}
|
||||
@ -27,13 +33,9 @@ impl Default for Render {
|
||||
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)),
|
||||
config: RefCell::new(RenderConfig::default()),
|
||||
mapper: RefCell::new(Mercator::new().into()),
|
||||
canvas: RefCell::new(None),
|
||||
dim1: RefCell::new(None),
|
||||
dim2: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,10 +53,6 @@ impl ObjectImpl for Render {
|
||||
self.parent_constructed();
|
||||
let area = self.obj();
|
||||
area.set_has_stencil_buffer(true);
|
||||
// area.add_tick_callback(|area, _| {
|
||||
// area.queue_render();
|
||||
// glib::Continue(true)
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,9 +72,8 @@ impl GLAreaImpl for Render {
|
||||
}
|
||||
|
||||
fn render(&self, context: >k::gdk::GLContext) -> bool {
|
||||
use femtovg::{Color, Paint, Path};
|
||||
|
||||
self.ensure_canvas();
|
||||
|
||||
let mut canvas = self.canvas.borrow_mut();
|
||||
let canvas = canvas.as_mut().unwrap();
|
||||
|
||||
@ -84,6 +81,8 @@ impl GLAreaImpl for Render {
|
||||
let w = self.obj().width();
|
||||
let h = self.obj().width();
|
||||
|
||||
let configs = self.config.borrow();
|
||||
|
||||
canvas.clear_rect(
|
||||
0,
|
||||
0,
|
||||
@ -92,16 +91,11 @@ impl GLAreaImpl for Render {
|
||||
Color::rgba(0, 0, 0, 255),
|
||||
);
|
||||
|
||||
self.background
|
||||
.borrow()
|
||||
.draw(canvas, self.scale.borrow().clone(), dpi);
|
||||
self.background.borrow().draw(canvas, configs.scale, dpi);
|
||||
|
||||
self.foreground.borrow().draw(
|
||||
canvas,
|
||||
self.scale.borrow().clone(),
|
||||
dpi,
|
||||
self.mapper.borrow(),
|
||||
);
|
||||
self.foreground
|
||||
.borrow()
|
||||
.draw(canvas, configs.scale, dpi, self.mapper.borrow());
|
||||
|
||||
canvas.flush();
|
||||
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
mod background;
|
||||
mod foreground;
|
||||
|
||||
mod imp;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::data::{mapper::Mapper, RadarData2d};
|
||||
use crate::coords::Mapper;
|
||||
use crate::data::{MultiDimensionData, RadarData2d};
|
||||
|
||||
pub use self::background::{BackgroundConfig, BackgroundWidget};
|
||||
pub use self::foreground::{ForegroundConfig, ForegroundWidget};
|
||||
use self::imp::RenderConfig;
|
||||
use crate::data::DownSampleMeth;
|
||||
pub use glib::subclass::prelude::*;
|
||||
use image::RgbImage;
|
||||
use ndarray::{self, s, Array2, Axis, Dimension, Ix2, Zip};
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
use proj::ProjError;
|
||||
@ -30,17 +32,31 @@ impl Default for Render {
|
||||
impl Render {
|
||||
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);
|
||||
{
|
||||
let mut configs = this.imp().config.borrow_mut();
|
||||
configs.scale = 1.0;
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
pub fn change_cfg<F>(&self, mut f: F)
|
||||
where
|
||||
F: FnMut(&mut RenderConfig),
|
||||
{
|
||||
let mut cfg = self.imp().config.borrow_mut();
|
||||
f(&mut cfg);
|
||||
}
|
||||
|
||||
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>
|
||||
pub(super) fn load_data_2d<T, Raw>(
|
||||
&self,
|
||||
mut data: RadarData2d<T, Raw>,
|
||||
) -> Result<(), ProjError>
|
||||
where
|
||||
T: Num
|
||||
+ Clone
|
||||
@ -54,18 +70,45 @@ impl Render {
|
||||
{
|
||||
assert!(data.dim1.shape().len() == data.dim2.shape().len());
|
||||
|
||||
data.downsample((801 / 2, 947 / 2), DownSampleMeth::VAR);
|
||||
|
||||
let meshed = data.map_by_fn(|(x, y)| Ok((x, y)))?;
|
||||
let d = data.data.to_owned().map(|x| x.as_());
|
||||
|
||||
self.imp()
|
||||
.foreground
|
||||
.borrow_mut()
|
||||
.set_dims((meshed.dim1, meshed.dim2));
|
||||
|
||||
self.imp().foreground.borrow_mut().set_data(d);
|
||||
|
||||
let mapper = self.imp().mapper.borrow();
|
||||
// data.downsample((801 * 2 / 3, 947 * 2 / 3), DownSampleMeth::VAR);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// 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),
|
||||
// ];
|
||||
|
||||
// let c = d.map(|v| {
|
||||
// let c = get(&levels, &colors, *v);
|
||||
// image::Rgb([
|
||||
// (c.r * 255.0) as u8,
|
||||
// (c.g * 255.0) as u8,
|
||||
// (c.b * 255.0) as u8,
|
||||
// ])
|
||||
// });
|
||||
|
||||
// let mut img = RgbImage::from_fn(927, 801, |x, y| c[[y as usize, x as usize]]);
|
||||
|
||||
// img.save("test.png").unwrap();
|
||||
|
||||
// self.imp()
|
||||
// .foreground
|
||||
// .borrow_mut()
|
||||
// .set_dims((meshed.dim1, meshed.dim2));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user