Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ There are several satellites to choose from, each covering a different region of
- Meteosat 9 (Africa, Middle East, India, Central Asia)
- Meteosat 10 (Atlantic Ocean, Africa, Europe)

For each satellite, you can select a "sector" and a "product". The sector determines which part of the planet to zoom in on (defaults to 'full_disk', which is the entire Earth), and the product determines how the various black-and-white sensors are combined to produce the final image. The default is 'geocolor', which is an approximation of visible light, with some enhancement at night.

It's also possible to specify a custom background image, if desired.

## Warning - Data Usage
Expand Down Expand Up @@ -74,6 +76,8 @@ Description=Run Satpaper on login.
Environment=SATPAPER_SATELLITE=goes-east
Environment=SATPAPER_RESOLUTION_X=2560
Environment=SATPAPER_RESOLUTION_Y=1440
Environment=SATPAPER_PRODUCT=geocolor
Environment=SATPAPER_SECTOR=full_disk
Environment=SATPAPER_DISK_SIZE=94
Environment=SATPAPER_TARGET_PATH=/var/home/colonial/.local/share/backgrounds/

Expand Down Expand Up @@ -120,6 +124,10 @@ launchctl start $HOME/Library/LaunchAgents/com.satpaper.plist
<string>2560</string>
<key>SATPAPER_RESOLUTION_Y</key>
<string>1440</string>
<key>SATPAPER_PRODUCT</key>
<string>geocolor</string>
<key>SATPAPER_SECTOR</key>
<string>full_disk</string>
<key>SATPAPER_DISK_SIZE</key>
<string>94</string>
<key>SATPAPER_TARGET_PATH</key>
Expand Down
2 changes: 2 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ services:
dockerfile: ./docker/Dockerfile
environment:
- SATPAPER_SATELLITE=goes-east
- SATPAPER_PRODUCT=geocolor
- SATPAPER_SECTOR=full_disk
- SATPAPER_RESOLUTION_X=2560
- SATPAPER_RESOLUTION_Y=1440
- SATPAPER_DISK_SIZE=95
Expand Down
35 changes: 29 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ pub struct Config {
/// The Y resolution/height of the generated wallpaper.
#[arg(short = 'y', long, env = "SATPAPER_RESOLUTION_Y")]
pub resolution_y: u32,

/// The product requested. (geocolor, etc)
#[arg(short = 'p', long, env = "SATPAPER_PRODUCT", default_value = "geocolor")]
pub product: String,

/// The sector requested. (full_disk, conus, etc)
#[arg(short = 'c', long, env = "SATPAPER_SECTOR", default_value = "full_disk")]
pub sector: String,

/// The size of the "disk" (Earth) relative to the generated wallpaper's
/// smaller dimension.
///
Expand Down Expand Up @@ -102,8 +111,8 @@ impl Satellite {
}
}

pub fn tile_image(self) -> Image<Box<[u8]>, 3> {
Image::alloc(self.tile_size(), self.tile_size()).boxed()
pub fn tile_image(self, sector: &String) -> Image<Box<[u8]>, 3> {
Copy link
Owner

@Colonial-Dev Colonial-Dev Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It honestly doesn't matter here, but since you said you're new to Rust - it'd be more idiomatic to take an &str (a reference to a string slice, which may be constant/static/refer to a subset of another string) rather than a &String (a reference to a heap-allocated/owned string buffer.) A String can be freely coerced to a &str, but the inverse is not true.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(If you want to get really fancy, there's also impl AsRef<str> (e.x. sector: impl AsRef<str>) - that makes your function generic over any type that can be cheaply dereferenced to a &str. But that's way overkill for here - just a thing to remember if you ever write some library code.)

Image::alloc(self.tile_size(sector), self.tile_size(sector)).boxed()
}

pub fn tile_count(self) -> u32 {
Expand All @@ -115,13 +124,27 @@ impl Satellite {
}
}

pub fn tile_size(self) -> u32 {
pub fn tile_size(self, sector: &String) -> u32 {
use Satellite::*;

match self {
GOESEast | GOESWest => 678,
Himawari => 688,
Meteosat9 | Meteosat10 => 464,
GOESEast | GOESWest => match sector.as_str() {
"full_disk" => 678,
"conus" => 625,
"mesoscale_01" => 500,
"mesoscale_02" => 500,
_ => 678
},
Himawari => match sector.as_str() {
"full_disk" => 688,
"japan" => 750,
"mesoscale_01" => 500,
_ => 688
},
Meteosat9 | Meteosat10 => match sector.as_str() {
"full_disk" => 464,
_ => 464
},
}
}
}
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ mod tests {
satellite: Satellite::GOESEast,
resolution_x: 2556,
resolution_y: 1440,
product: "geocolor",
sector: "full_disk",
disk_size: 95,
target_path: ".".into(),
wallpaper_command: None,
Expand Down
20 changes: 12 additions & 8 deletions src/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ use super::{
pub type Image<T> = Img<T, 3>;

const SLIDER_BASE_URL: &str = "https://rammb-slider.cira.colostate.edu";
const SLIDER_SECTOR: &str = "full_disk";
const SLIDER_PRODUCT: &str = "geocolor";

const TIMEOUT: Duration = Duration::from_secs(30);

Expand Down Expand Up @@ -55,8 +53,10 @@ fn download(config: &Config) -> Result<Image<Box<[u8]>>> {
.map(|(x, y)| -> Result<_> {
// year:04 i am hilarious
let url = format!(
"{SLIDER_BASE_URL}/data/imagery/{year:04}/{month:02}/{day:02}/{}---{SLIDER_SECTOR}/{SLIDER_PRODUCT}/{}/{:02}/{x:03}_{y:03}.png",
"{SLIDER_BASE_URL}/data/imagery/{year:04}/{month:02}/{day:02}/{}---{}/{}/{}/{:02}/{x:03}_{y:03}.png",
config.satellite.id(),
config.sector,
config.product,
time.as_int(),
config.satellite.max_zoom()
);
Expand All @@ -74,7 +74,7 @@ fn download(config: &Config) -> Result<Image<Box<[u8]>>> {
let reader = resp.into_reader();
let dec = png::Decoder::new(reader);
let mut reader = dec.read_info()?;
let mut buf = config.satellite.tile_image();
let mut buf = config.satellite.tile_image(&config.sector);
let info = reader.next_frame(unsafe { buf.buffer_mut() })?;
debug_assert!(matches!(info.color_type, png::ColorType::Rgb));
let buf = buf.scale::<Lanczos3>(tile_size, tile_size);
Expand Down Expand Up @@ -289,8 +289,10 @@ where
impl Time {
pub fn fetch(config: &Config) -> Result<Self> {
let url = format!(
"{SLIDER_BASE_URL}/data/json/{}/{SLIDER_SECTOR}/{SLIDER_PRODUCT}/latest_times.json",
config.satellite.id()
"{SLIDER_BASE_URL}/data/json/{}/{}/{}/latest_times.json",
config.satellite.id(),
config.sector,
config.product
);

let json = ureq::get(&url)
Expand All @@ -316,8 +318,10 @@ struct Date {
impl Date {
pub fn fetch(config: &Config) -> Result<Self> {
let url = format!(
"{SLIDER_BASE_URL}/data/json/{}/{SLIDER_SECTOR}/{SLIDER_PRODUCT}/available_dates.json",
config.satellite.id()
"{SLIDER_BASE_URL}/data/json/{}/{}/{}/available_dates.json",
config.satellite.id(),
config.sector,
config.product
);

let json = ureq::get(&url)
Expand Down
Loading