benchmarking
Some checks are pending
Verify Latest Dependencies / Verify Latest Dependencies (push) Waiting to run
build and test / wxbox - latest (push) Waiting to run

This commit is contained in:
core 2025-05-19 10:11:41 -04:00
parent 9ab0a77d64
commit 1c836c6ecf
199 changed files with 607 additions and 6204 deletions

2
.idea/wxbox.iml generated
View file

@ -20,6 +20,8 @@
<sourceFolder url="file://$MODULE_DIR$/crates/ar2/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/crates/nommer/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/crates/ar2/benches" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/crates/grib2/benches" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/crates/tiler/benches" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />

3249
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,16 +4,11 @@ version = "0.1.0"
edition = "2024"
[dependencies]
tracing = "0.1"
nexrad-decode = "0.1.1"
nexrad-data = "0.2"
serde = { version = "1", features = ["derive"]}
toml = "0.8"
thiserror = "2"
wxbox-nommer = { path = "../nommer" }
bzip2 = { version = "0.5", default-features = false, features = ["libbz2-rs-sys"] }
rayon = { version = "1" }
bincode = { version = "2", features = ["serde"] }
[dev-dependencies]
criterion = { version = "0.5" }

Binary file not shown.

View file

@ -1,14 +1,29 @@
use std::{env, fs};
use std::hint::black_box;
use std::time::Instant;
use criterion::{criterion_group, criterion_main, Criterion};
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use wxbox_ar2::parse;
const KCRP20170825_235733_V06: (&[u8], &str) = (include_bytes!("KCRP20170825_235733_V06"), "KCRP20170825_235733_V06");
const KGWX20250518_165333_V06: (&[u8], &str) = (include_bytes!("KGWX20250518_165333_V06"), "KGWX20250518_165333_V06");
fn criterion_benchmark(c: &mut Criterion) {
let f = fs::read("/home/core/dl/KGWX20250518_165333_V06").unwrap();
c.bench_function("parse KGWX", |b| b.iter(|| {
parse(black_box(f.clone()))
}));
let mut group = c.benchmark_group("parse");
for (test_file_bytes, name) in [KCRP20170825_235733_V06, KGWX20250518_165333_V06].iter() {
group.bench_with_input(
BenchmarkId::from_parameter(name),
test_file_bytes,
|b, bytes| {
b.iter(|| {
parse(bytes.to_vec()).unwrap();
});
}
);
}
group.finish();
}
criterion_group!(benches, criterion_benchmark);

491
crates/ar2/flamegraph.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 566 KiB

View file

@ -3,9 +3,10 @@ use nexrad_decode::messages::MessageContents;
use nexrad_decode::messages::digital_radar_data::{GenericDataBlock, RadialStatus};
use nexrad_decode::result::Error;
use std::fmt::Debug;
use rayon::prelude::*;
pub mod sites;
pub mod parser;
pub struct Scan {
pub coverage_pattern_number: u16,
@ -85,25 +86,41 @@ pub enum MomentValue {
pub fn parse(input: Vec<u8>) -> nexrad_data::result::Result<Scan> {
let file = File::new(input);
let mut vcp = None;
let mut vcp: Option<u16> = None;
let mut radials = vec![];
for mut record in file.records() {
if record.compressed() {
record = record.decompress()?;
}
let messages = record.messages()?;
for message in messages {
let contents = message.into_contents();
if let MessageContents::DigitalRadarData(radar_data_message) = contents {
if vcp.is_none() {
if let Some(volume_block) = &radar_data_message.volume_data_block {
vcp = Some(volume_block.volume_coverage_pattern_number);
}
}
radials.push(into_radial(*radar_data_message)?);
let mut radial_chunks: Vec<nexrad_data::result::Result<(Vec<Radial>, Option<u16>)>> = file.records()
.par_iter_mut()
.map(|record| {
let mut vcp = None;
let mut radials = vec![];
if record.compressed() {
*record = record.decompress()?;
}
let messages = record.messages()?;
for message in messages {
let contents = message.contents();
if let MessageContents::DigitalRadarData(radar_data_message) = contents {
if vcp.is_none() {
if let Some(volume_block) = &radar_data_message.volume_data_block {
vcp = Some(volume_block.volume_coverage_pattern_number);
}
}
radials.push(into_radial(*radar_data_message.clone())?)
}
}
Ok((radials, vcp))
})
.collect();
for radial_chunk in radial_chunks {
let (mut radial_chunk, this_vcp) = radial_chunk?;
if vcp.is_none() && this_vcp.is_some() {
vcp = this_vcp;
}
radials.append(&mut radial_chunk);
}
Ok(Scan {
@ -188,6 +205,4 @@ impl Sweep {
sweeps
}
}
pub const DATA_BYTES: &[u8] = include_bytes!("../KCRP20170825_235733_V06");
}

View file

@ -2,7 +2,6 @@ use std::{env, fs};
use std::fs::File;
use std::time::Instant;
use wxbox_ar2::parse;
use wxbox_ar2::parser::Ar2Parser;
fn main() {
let f = fs::read(env::args().nth(1).unwrap()).unwrap();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

View file

@ -1,30 +0,0 @@
[package]
name = "wxbox-client"
version = "0.1.0"
edition = "2021"
[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"
eframe = { version = "0.31", features = ["persistence"] }
egui = "0.31"
poll-promise = "0.3"
image = "0.25"
rand = "0.9"
serde = { version = "1", features = ["derive"] }
toml = "0.8"
egui_extras = { version = "0.31", features = ["serde"] }
egui_demo_lib = { version = "0.31", features = ["serde"] }
egui-phosphor = "0.9"
walkers = { version = "0.36", git = "https://github.com/c0repwn3r/walkers", branch = "core/wxbox", features = ["serde"] }
#walkers = { version = "0.36", path = "../../../walkers/walkers", features = ["serde"] }
getrandom = { version = "0.3", features = ["wasm_js"] }
lru = "0.13"
futures = "0.3"
egui_flex = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
tracing-web = "0.1"
wasm-bindgen-futures = "0.4"
web-sys = "0.3"
console_error_panic_hook = "0.1"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View file

@ -1,28 +0,0 @@
{
"name": "wxbox",
"short_name": "wxbox",
"icons": [
{
"src": "./assets/icon-256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "./assets/maskable_icon_x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "./assets/icon-1024.png",
"sizes": "1024x1024",
"type": "image/png"
}
],
"lang": "en-US",
"id": "/index.html",
"start_url": "./index.html",
"display": "standalone",
"background_color": "white",
"theme_color": "white"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

View file

@ -1,29 +0,0 @@
/*
var cacheName = 'egui-template-pwa';
*/
/*
var filesToCache = [
'./',
'./index.html',
'./eframe_template.js',
'./eframe_template_bg.wasm',
];
*/
/* Start the service worker and cache all of the app's content *//*
self.addEventListener('install', function (e) {
e.waitUntil(
caches.open(cacheName).then(function (cache) {
return cache.addAll(filesToCache);
})
);
});
*/
/* Serve cached content when offline *//*
self.addEventListener('fetch', function (e) {
e.respondWith(
caches.match(e.request).then(function (response) {
return response || fetch(e.request);
})
);
});
*/

View file

@ -1,154 +0,0 @@
<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- Disable zooming: -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<head>
<!-- change this to your project name -->
<title>wxbox</title>
<!-- config for our rust wasm binary. go to https://trunkrs.dev/assets/#rust for more customization -->
<link data-trunk rel="rust" data-wasm-opt="2" />
<!-- this is the base url relative to which other urls will be constructed. trunk will insert this from the public-url option -->
<base data-trunk-public-url />
<link data-trunk rel="icon" href="assets/favicon.ico">
<link data-trunk rel="copy-file" href="assets/sw.js"/>
<link data-trunk rel="copy-file" href="assets/manifest.json"/>
<link data-trunk rel="copy-file" href="assets/icon-1024.png" data-target-path="assets"/>
<link data-trunk rel="copy-file" href="assets/icon-256.png" data-target-path="assets"/>
<link data-trunk rel="copy-file" href="assets/icon_ios_touch_192.png" data-target-path="assets"/>
<link data-trunk rel="copy-file" href="assets/maskable_icon_x512.png" data-target-path="assets"/>
<link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" href="assets/icon_ios_touch_192.png">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#404040">
<style>
html {
/* Remove touch delay: */
touch-action: manipulation;
}
body {
/* Light mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #fcfcfd;
}
@media (prefers-color-scheme: dark) {
body {
/* Dark mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #111113;
}
}
/* Allow canvas to fill entire web page: */
html,
body {
overflow: hidden;
margin: 0 !important;
height: 100%;
width: 100%;
}
/* Make canvas fill entire document: */
canvas {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;/*
@supports (-webkit-touch-callout: none) {
height: calc(100% - 26px);
}
@supports not (-webkit-touch-callout: none) {
height: 100%;
}*/
height:100%;
}
.centered {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #f0f0f0;
font-size: 24px;
font-family: Ubuntu-Light, Helvetica, sans-serif;
text-align: center;
}
/* ---------------------------------------------- */
/* Loading animation from https://loading.io/css/ */
.lds-dual-ring {
display: inline-block;
width: 24px;
height: 24px;
}
.lds-dual-ring:after {
content: " ";
display: block;
width: 24px;
height: 24px;
margin: 0px;
border-radius: 50%;
border: 3px solid #fff;
border-color: #fff transparent #fff transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<!-- The WASM code will resize the canvas dynamically -->
<!-- the id is hardcoded in main.rs . so, make sure both match. -->
<canvas id="the_canvas_id"></canvas>
<!-- the loading spinner will be removed in main.rs -->
<div class="centered" id="loading_text">
<p style="font-size:16px">
Loading…
</p>
<div class="lds-dual-ring"></div>
</div>
<!--Register Service Worker. this will cache the wasm / js scripts for offline use (for PWA functionality). -->
<!-- Force refresh (Ctrl + F5) to load the latest files instead of cached files -->
<script>
// We disable caching during development so that we always view the latest version.
if ('serviceWorker' in navigator && window.location.hash !== "#dev") {
window.addEventListener('load', function () {
navigator.serviceWorker.register('sw.js');
});
}
</script>
</body>
</html>
<!-- Powered by egui: https://github.com/emilk/egui/ -->

View file

@ -1,101 +0,0 @@
use crate::map::sources::baselayers::BASELAYER_OSM;
use crate::map::sources::{ActiveLayer, LayerId};
use crate::map::tiles::LayerManager;
use crate::ui::shell::footer::footer;
use crate::ui::shell::left_bar::left_bar;
use crate::ui::shell::right_bar::right_bar;
use crate::ui::shell::top_bar::top_bar;
use crate::ui::tokens::DESIGN_TOKENS;
use eframe::{Frame, Storage};
use egui::util::History;
use egui::Context;
use egui_demo_lib::{View, WidgetGallery};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use walkers::{HttpTiles, MapMemory, Position, Tiles};
use crate::ui::{Breakpoint, CtxExt};
#[derive(Serialize, Deserialize)]
pub struct App {
pub(crate) frame_time_history: History<f32>,
pub add_layer_open: bool,
pub selected_layer: Option<LayerId>,
pub map_memory: MapMemory,
pub layer_manager: LayerManager,
pub position: Position,
pub menu_open: bool
}
impl App {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
DESIGN_TOKENS.apply(&cc.egui_ctx);
egui_extras::install_image_loaders(&cc.egui_ctx);
if let Some(storage) = cc.storage {
if let Some(stored) = eframe::get_value(storage, eframe::APP_KEY) {
let mut stored: Self = stored;
stored.frame_time_history = History::new(1..100, 0.5);
stored.layer_manager.ctx = Some(cc.egui_ctx.clone());
stored.layer_manager.registered_sources = HashMap::default();
stored.layer_manager.register_builtin_layers();
return stored;
}
}
let mut layer_manager = LayerManager::default();
layer_manager.register_builtin_layers();
layer_manager.active_layers.push(ActiveLayer {
source_id: BASELAYER_OSM.source_id,
layer_id: LayerId::new(),
visible: true,
});
layer_manager.ctx = Some(cc.egui_ctx.clone());
Self {
frame_time_history: History::new(1..100, 0.5),
add_layer_open: false,
selected_layer: None,
map_memory: MapMemory::default(),
layer_manager,
position: Position::new(35.227085, -80.843124),
menu_open: false
}
}
}
impl eframe::App for App {
fn save(&mut self, storage: &mut dyn Storage) {
eframe::set_value(storage, eframe::APP_KEY, self);
}
fn update<'a>(&'a mut self, ctx: &'a Context, frame: &'a mut Frame) {
if let Some(seconds) = frame.info().cpu_usage {
self.frame_time_history.add(ctx.input(|i| i.time), seconds);
}
top_bar(ctx);
let show_main_menu_in_footer = ctx.breakpoint() < Breakpoint::Xl4;
footer(ctx, self, show_main_menu_in_footer);
if !show_main_menu_in_footer {
left_bar(
ctx,
&mut self.add_layer_open,
&mut self.layer_manager,
&mut self.selected_layer,
);
}
//right_bar(ctx, self);
egui::CentralPanel::default().show(ctx, |ui| {
ui.add(walkers::Map::new(
Some(&mut self.layer_manager),
&mut self.map_memory,
self.position,
));
});
}
}

View file

@ -1,3 +0,0 @@
pub mod app;
pub mod map;
pub mod ui;

View file

@ -1,85 +0,0 @@
use egui::Theme;
use tracing::debug;
use wxbox_client::ui::tokens::DESIGN_TOKENS;
#[cfg(not(target_arch = "wasm32"))]
fn main() -> eframe::Result {
tracing_subscriber::fmt::init();
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default(),
multisampling: 8,
..Default::default()
};
debug!("{:?}", DESIGN_TOKENS.get_color("colors.bg", Theme::Dark));
eframe::run_native(
"wxbox",
native_options,
Box::new(|cc| Ok(Box::new(wxbox_client::app::App::new(cc)))),
)
}
#[cfg(target_arch = "wasm32")]
fn main() {
use tracing::Level;
use tracing_subscriber::fmt::format::Pretty;
use tracing_subscriber::prelude::*;
use tracing_web::{performance_layer, MakeWebConsoleWriter};
console_error_panic_hook::set_once();
let fmt_layer = tracing_subscriber::fmt::layer()
.with_ansi(false) // not supported in browsers
.without_time() // std::time doesn't exist in wasm
.with_writer(MakeWebConsoleWriter::new().with_max_level(Level::DEBUG)); // wgpu spams the console, and this is slow as hell
let perf_layer = performance_layer() // enable performance tracing
.with_details_from_fields(Pretty::default()); // ... with pretty fields
tracing_subscriber::registry()
.with(fmt_layer)
.with(perf_layer)
.init(); // register the logger
use eframe::wasm_bindgen::JsCast as _;
let web_options = eframe::WebOptions::default();
wasm_bindgen_futures::spawn_local(async {
let document = web_sys::window()
.expect("No window")
.document()
.expect("No document");
let canvas = document
.get_element_by_id("the_canvas_id")
.expect("Failed to find the_canvas_id")
.dyn_into::<web_sys::HtmlCanvasElement>()
.expect("the_canvas_id was not a HtmlCanvasElement");
let start_result = eframe::WebRunner::new()
.start(
canvas,
web_options,
Box::new(|cc| Ok(Box::new(wxbox_client::app::App::new(cc)))),
)
.await;
// Remove the loading text and spinner:
if let Some(loading_text) = document.get_element_by_id("loading_text") {
match start_result {
Ok(_) => {
loading_text.remove();
}
Err(e) => {
loading_text.set_inner_html(
"<p> The app has crashed. See the developer console for details. </p>",
);
panic!("Failed to start eframe: {e:?}");
}
}
}
});
}

View file

@ -1,6 +0,0 @@
use crate::map::tiles::LayerManager;
use walkers::sources::Attribution;
use walkers::{TextureWithUv, TileId, Tiles};
pub mod sources;
pub mod tiles;

View file

@ -1,16 +0,0 @@
use crate::map::sources::{LayerSource, LayerTypeHint};
use std::convert::Into;
use std::sync::LazyLock;
pub static BASELAYER_OSM: LazyLock<LayerSource> = LazyLock::new(|| LayerSource {
source_id: 0x572dd260332d5f7f,
tile_url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png".into(),
display_name: "OpenStreetMap".into(),
short_name: "OpenStreetMap".into(),
type_hint: LayerTypeHint::Baselayer,
location: "Worldwide".into(),
source: "OpenStreetMap contributors".into(),
source_link: "https://openstreetmap.org".into(),
tile_size: 256,
max_zoom: 19,
});

View file

@ -1,110 +0,0 @@
use crate::map::sources::{LayerSource, LayerTypeHint};
use std::convert::Into;
use std::sync::LazyLock;
pub static NOAA_MRMS_MERGED_CREF_QC_CONUS: LazyLock<LayerSource> = LazyLock::new(|| LayerSource {
source_id: 0xC10FCD0566B7F952,
tile_url: format!(
"{}/grib2/noaa_mrms_merged_composite_reflectivity_qc_CONUS/{{z}}/{{x}}/{{y}}@2x.png",
env!("TILER_BASE_URL")
),
display_name: "Merged Composite Reflectivity QCd".into(),
short_name: "Composite Reflectivity (M-CONUS-QCd)".into(),
type_hint: LayerTypeHint::RadarData,
location: "Continental US".into(),
source: "NOAA / MRMS".into(),
source_link: "https://mrms.nssl.noaa.gov".into(),
tile_size: 512,
max_zoom: 19,
});
pub static NOAA_MRMS_MERGED_CREF_QC_ALASKA: LazyLock<LayerSource> = LazyLock::new(|| LayerSource {
source_id: 0x779F656117545E91,
tile_url: format!(
"{}/grib2/noaa_mrms_merged_composite_reflectivity_qc_ALASKA/{{z}}/{{x}}/{{y}}@2x.png",
env!("TILER_BASE_URL")
),
display_name: "Merged Composite Reflectivity QCd".into(),
short_name: "Composite Reflectivity (M-ALASKA-QCd)".into(),
type_hint: LayerTypeHint::RadarData,
location: "Alaska".into(),
source: "NOAA / MRMS".into(),
source_link: "https://mrms.nssl.noaa.gov".into(),
tile_size: 512,
max_zoom: 19,
});
pub static NOAA_MRMS_MERGED_CREF_QC_CARIB: LazyLock<LayerSource> = LazyLock::new(|| LayerSource {
source_id: 0xA7076E0145BC4BDD,
tile_url: format!(
"{}/grib2/noaa_mrms_merged_composite_reflectivity_qc_CARIB/{{z}}/{{x}}/{{y}}@2x.png",
env!("TILER_BASE_URL")
),
display_name: "Merged Composite Reflectivity QCd".into(),
short_name: "Composite Reflectivity (M-CARIB-QCd)".into(),
type_hint: LayerTypeHint::RadarData,
location: "Caribbean".into(),
source: "NOAA / MRMS".into(),
source_link: "https://mrms.nssl.noaa.gov".into(),
tile_size: 512,
max_zoom: 19,
});
pub static NOAA_MRMS_MERGED_CREF_QC_GUAM: LazyLock<LayerSource> = LazyLock::new(|| LayerSource {
source_id: 0xB277C4B00469BD02,
tile_url: format!(
"{}/grib2/noaa_mrms_merged_composite_reflectivity_qc_GUAM/{{z}}/{{x}}/{{y}}@2x.png",
env!("TILER_BASE_URL")
),
display_name: "Merged Composite Reflectivity QCd".into(),
short_name: "Composite Reflectivity (M-GUAM-QCd)".into(),
type_hint: LayerTypeHint::RadarData,
location: "Guam".into(),
source: "NOAA / MRMS".into(),
source_link: "https://mrms.nssl.noaa.gov".into(),
tile_size: 512,
max_zoom: 19,
});
pub static NOAA_MRMS_MERGED_CREF_QC_HAWAII: LazyLock<LayerSource> = LazyLock::new(|| LayerSource {
source_id: 0x238710F2CD275445,
tile_url: format!(
"{}/grib2/noaa_mrms_merged_composite_reflectivity_qc_HAWAII/{{z}}/{{x}}/{{y}}@2x.png",
env!("TILER_BASE_URL")
),
display_name: "Merged Composite Reflectivity QCd".into(),
short_name: "Composite Reflectivity (M-HAWAII-QCd)".into(),
type_hint: LayerTypeHint::RadarData,
location: "Hawaii".into(),
source: "NOAA / MRMS".into(),
source_link: "https://mrms.nssl.noaa.gov".into(),
tile_size: 512,
max_zoom: 19,
});
pub static NOAA_MRMS_MERGED_RHOHV_3KM_CONUS: LazyLock<LayerSource> =
LazyLock::new(|| LayerSource {
source_id: 0x0EFAF68CB6E30B8F,
tile_url: format!(
"{}/grib2/noaa_mrms_merged_rhohv_3km_CONUS/{{z}}/{{x}}/{{y}}@2x.png",
env!("TILER_BASE_URL")
),
display_name: "Merged RhoHV @ 3km".into(),
short_name: "RhoHV @ 3km (CONUS)".into(),
type_hint: LayerTypeHint::RadarData,
location: "Continental US".into(),
source: "NOAA / MRMS".into(),
source_link: "https://mrms.nssl.noaa.gov".into(),
tile_size: 512,
max_zoom: 19,
});
pub static ROC_KCRP_TEST: LazyLock<LayerSource> = LazyLock::new(|| LayerSource {
source_id: 0x0EFAF68CB6E30B8E,
tile_url: format!(
"{}/nexrad/kcrp_ref_test/{{z}}/{{x}}/{{y}}@2x.png",
env!("TILER_BASE_URL")
),
display_name: "KCRP SSR Test".into(),
short_name: "KCRP SSR TEST (KCRP)".into(),
type_hint: LayerTypeHint::RadarData,
location: "KCRP".into(),
source: "NOAA / ROC".into(),
source_link: "https://roc.noaa.gov".into(),
tile_size: 512,
max_zoom: 19,
});

View file

@ -1,84 +0,0 @@
use crate::map::tiles::SourceId;
use image::DynamicImage;
use poll_promise::Promise;
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Formatter};
pub mod baselayers;
pub mod datalayers;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct LayerSource {
pub source_id: u64,
pub tile_url: String,
pub display_name: String,
pub short_name: String,
pub location: String,
pub source: String,
pub source_link: String,
pub type_hint: LayerTypeHint,
pub tile_size: u32,
pub max_zoom: u8,
}
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub enum LayerTypeHint {
Baselayer,
RadarData,
}
impl LayerTypeHint {
pub fn icon(&self) -> &'static str {
match self {
LayerTypeHint::Baselayer => egui_phosphor::regular::MAP_TRIFOLD,
LayerTypeHint::RadarData => egui_phosphor::regular::TARGET,
}
}
pub fn name(&self) -> &'static str {
match self {
LayerTypeHint::Baselayer => "Baselayer",
LayerTypeHint::RadarData => "Radar data",
}
}
}
pub struct Tile {
pub promise: Promise<Result<DynamicImage, String>>,
}
impl Debug for Tile {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
if self.promise.ready().is_some() {
"<loaded texture>"
} else {
"<texture promise>"
}
)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub struct ActiveLayer {
pub source_id: SourceId,
pub layer_id: LayerId,
pub visible: bool,
}
#[derive(Hash, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct LayerId(u32);
impl LayerId {
pub fn new() -> Self {
Self(rand::random())
}
}
impl Debug for LayerId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:04x}", self.0)
}
}
impl From<LayerId> for egui::Id {
fn from(id: LayerId) -> Self {
Self::new(id)
}
}

View file

@ -1,140 +0,0 @@
use crate::map::sources::baselayers::*;
use crate::map::sources::datalayers::*;
use crate::map::sources::{ActiveLayer, LayerSource, Tile};
use eframe::emath::{pos2, Rect};
use futures::channel::mpsc::TrySendError;
use image::{ColorType, DynamicImage};
use lru::LruCache;
use poll_promise::Promise;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::cmp::PartialEq;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use tracing::{debug, error, trace};
use walkers::sources::{Attribution, TileSource};
use walkers::{HttpTiles, Texture, TextureWithUv, TileId, Tiles};
// type aliases to make the tile map less hellish
pub type ZoomLevel = usize;
pub type XCoord = usize;
pub type YCoord = usize;
pub type SourceId = u64;
#[derive(Default, Serialize, Deserialize, Debug)]
#[serde(default)]
pub struct LayerManager {
#[serde(skip)]
pub ctx: Option<egui::Context>,
pub registered_sources: HashMap<SourceId, LayerSource>,
pub active_layers: Vec<ActiveLayer>,
#[serde(skip)]
pub individual_sources: HashMap<SourceId, WHttpTiles>,
}
pub struct WHttpTiles(pub Rc<RefCell<HttpTiles>>);
impl Debug for WHttpTiles {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "<WHttpTiles>")
}
}
impl LayerManager {
pub fn register_layer(&mut self, layer: &LayerSource) {
self.registered_sources
.insert(layer.source_id, layer.clone());
}
pub fn unregister_layer(&mut self, layer_id: &SourceId) {
self.registered_sources.remove(layer_id);
self.active_layers.retain(|u| u.source_id != *layer_id);
}
pub fn register_builtin_layers(&mut self) {
let layers = [
// -- Base layers --
&*BASELAYER_OSM, // OpenStreetMap
// -- NOAA MRMS - Merged Composite Reflectivity - QCd
&*NOAA_MRMS_MERGED_CREF_QC_CONUS, // Continental United States
&*NOAA_MRMS_MERGED_CREF_QC_HAWAII, // Hawaii
&*NOAA_MRMS_MERGED_CREF_QC_ALASKA, // Alaska FSS
&*NOAA_MRMS_MERGED_CREF_QC_CARIB, // Caribbean
&*NOAA_MRMS_MERGED_CREF_QC_GUAM, // Guam
// -- NOAA MRMS - Merged RhoHV (Differential Reflectivity) @ 3km
&*NOAA_MRMS_MERGED_RHOHV_3KM_CONUS,
&*ROC_KCRP_TEST,
];
for layer in layers {
self.register_layer(layer);
}
}
pub fn remove_unregistered_layers(&mut self) {
// remove any
self.active_layers
.retain(|u| self.registered_sources.contains_key(&u.source_id));
}
}
impl TileSource for LayerSource {
fn tile_url(&self, tile_id: TileId) -> String {
self.tile_url
.replace("{z}", &tile_id.zoom.to_string())
.replace("{x}", &tile_id.x.to_string())
.replace("{y}", &tile_id.y.to_string())
}
fn attribution(&self) -> Attribution {
Attribution {
text: self.source.clone().leak(),
url: self.source_link.clone().leak(),
logo_light: None,
logo_dark: None,
}
}
fn tile_size(&self) -> u32 {
self.tile_size
}
fn max_zoom(&self) -> u8 {
self.max_zoom
}
}
impl Tiles for LayerManager {
fn at(&mut self, tile_id: TileId) -> Vec<TextureWithUv> {
let mut layers = vec![];
for layer in self.active_layers.iter().rev().filter(|u| u.visible) {
let layer = self
.individual_sources
.entry(layer.source_id)
.or_insert_with(|| {
WHttpTiles(Rc::new(RefCell::new(HttpTiles::new(
self.registered_sources
.get(&layer.source_id)
.unwrap()
.clone(),
self.ctx.as_ref().unwrap().clone(),
))))
});
let tiles_here = layer.0.borrow_mut().at(tile_id);
layers.extend(tiles_here);
}
layers
}
fn attribution(&self) -> Attribution {
Attribution {
text: "our sources",
url: "https://weather.ax/credits",
logo_light: None,
logo_dark: None,
}
}
fn tile_size(&self) -> u32 {
256
}
}

View file

@ -1,92 +0,0 @@
Copyright (c) 2016 The Inter Project Authors (https://github.com/rsms/inter)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION AND CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Some files were not shown because too many files have changed in this diff Show more