Add --center-lat, --center-lon, --zoom CLI arguments
Allow overriding the map centre and OSM zoom level from the command line, keeping the existing values as defaults. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
22
src/main.rs
22
src/main.rs
@@ -34,6 +34,18 @@ struct Args {
|
||||
/// Disable OpenStreetMap base layer (use flat green background)
|
||||
#[arg(long)]
|
||||
no_basemap: bool,
|
||||
|
||||
/// Map centre latitude in degrees (default: 52.56)
|
||||
#[arg(long, value_name = "LAT", default_value_t = render::DEFAULT_CENTER_LAT)]
|
||||
center_lat: f64,
|
||||
|
||||
/// Map centre longitude in degrees (default: 13.08)
|
||||
#[arg(long, value_name = "LON", default_value_t = render::DEFAULT_CENTER_LON)]
|
||||
center_lon: f64,
|
||||
|
||||
/// OSM tile zoom level (default: 10)
|
||||
#[arg(long, value_name = "ZOOM", default_value_t = render::DEFAULT_OSM_ZOOM)]
|
||||
zoom: u8,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@@ -134,12 +146,12 @@ fn main() -> Result<()> {
|
||||
let basemap = if args.no_basemap {
|
||||
None
|
||||
} else {
|
||||
let proj = render::make_projection();
|
||||
let proj = render::make_projection(args.center_lat, args.center_lon, args.zoom);
|
||||
let tile_cache = tiles::fetch_tiles(
|
||||
&proj,
|
||||
render::MAP_W,
|
||||
render::MAP_H,
|
||||
render::OSM_ZOOM,
|
||||
args.zoom,
|
||||
&PathBuf::from(CACHE_DIR),
|
||||
)?;
|
||||
Some(tiles::rasterize_basemap(
|
||||
@@ -153,12 +165,12 @@ fn main() -> Result<()> {
|
||||
let labels = if args.no_basemap {
|
||||
None
|
||||
} else {
|
||||
let proj = render::make_projection();
|
||||
let proj = render::make_projection(args.center_lat, args.center_lon, args.zoom);
|
||||
let label_cache = tiles::fetch_label_tiles(
|
||||
&proj,
|
||||
render::MAP_W,
|
||||
render::MAP_H,
|
||||
render::OSM_ZOOM,
|
||||
args.zoom,
|
||||
&PathBuf::from(CACHE_DIR),
|
||||
)?;
|
||||
Some(tiles::rasterize_labels(
|
||||
@@ -200,7 +212,7 @@ fn main() -> Result<()> {
|
||||
let out_name = format!("clct_{:06}.png", step + 1);
|
||||
let out_path = cache_dir.join(&out_name);
|
||||
|
||||
render::render_frame(&out_path, &lats, &lons, &cloud, &title, basemap.as_deref(), labels.as_deref())
|
||||
render::render_frame(&out_path, &lats, &lons, &cloud, &title, basemap.as_deref(), labels.as_deref(), args.center_lat, args.center_lon, args.zoom)
|
||||
.with_context(|| format!("Rendering frame {}", step))?;
|
||||
|
||||
eprintln!(" [{:02}/{:02}] {}", step + 1, args.prediction_hours, out_name);
|
||||
|
||||
@@ -15,27 +15,27 @@ pub const MARGIN: u32 = 8;
|
||||
pub const MAP_W: u32 = IMG_WIDTH - LEGEND_W - MARGIN * 2;
|
||||
pub const MAP_H: u32 = IMG_HEIGHT - TITLE_H - MARGIN;
|
||||
|
||||
/// Map image centre and OSM zoom level.
|
||||
pub const CENTER_LAT: f64 = 52.56;
|
||||
pub const CENTER_LON: f64 = 13.08;
|
||||
/// OSM tile zoom level. The viewport half-extents are derived from this so that
|
||||
/// Default map image centre and OSM zoom level.
|
||||
pub const DEFAULT_CENTER_LAT: f64 = 52.56;
|
||||
pub const DEFAULT_CENTER_LON: f64 = 13.08;
|
||||
/// Default OSM tile zoom level. The viewport half-extents are derived from this so that
|
||||
/// one tile pixel maps to exactly one output pixel, eliminating interpolation
|
||||
/// artefacts on the OSM base layer.
|
||||
pub const OSM_ZOOM: u8 = 10;
|
||||
pub const DEFAULT_OSM_ZOOM: u8 = 10;
|
||||
|
||||
/// Build the orthographic projection whose viewport is sized so that the
|
||||
/// output resolution matches OSM tiles at `OSM_ZOOM` exactly.
|
||||
/// output resolution matches OSM tiles at `zoom` exactly.
|
||||
///
|
||||
/// At zoom `z`, Web-Mercator tiles cover `360 / 2^z` degrees of longitude per
|
||||
/// tile, each 256 px wide. Near the centre latitude `φ`, 1° of longitude ≈
|
||||
/// `cos φ` arc-degrees, so the arc-degree scale is:
|
||||
/// `pixels_per_arc_deg = 256 · 2^z / (360 · cos φ)`
|
||||
/// Half-extents follow directly from the map pixel dimensions.
|
||||
pub fn make_projection() -> OrthoProjection {
|
||||
let scale = f64::from(256u32 << OSM_ZOOM as u32) / (360.0 * CENTER_LAT.to_radians().cos());
|
||||
pub fn make_projection(center_lat: f64, center_lon: f64, zoom: u8) -> OrthoProjection {
|
||||
let scale = f64::from(256u32 << zoom as u32) / (360.0 * center_lat.to_radians().cos());
|
||||
let half_w = MAP_W as f64 / (2.0 * scale);
|
||||
let half_h = MAP_H as f64 / (2.0 * scale);
|
||||
OrthoProjection::new(CENTER_LAT, CENTER_LON, half_w, half_h)
|
||||
OrthoProjection::new(center_lat, center_lon, half_w, half_h)
|
||||
}
|
||||
|
||||
const BACKGROUND: RGBColor = RGBColor(240, 248, 255); // pale sky-blue
|
||||
@@ -179,8 +179,11 @@ pub fn render_frame(
|
||||
title: &str,
|
||||
basemap: Option<&[[u8; 3]]>,
|
||||
labels: Option<&[[u8; 4]]>,
|
||||
center_lat: f64,
|
||||
center_lon: f64,
|
||||
zoom: u8,
|
||||
) -> Result<()> {
|
||||
let proj = make_projection();
|
||||
let proj = make_projection(center_lat, center_lon, zoom);
|
||||
|
||||
let root = BitMapBackend::new(output_path, (IMG_WIDTH, IMG_HEIGHT)).into_drawing_area();
|
||||
root.fill(&BACKGROUND).context("fill background")?;
|
||||
|
||||
Reference in New Issue
Block a user