1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
//! StaticMap is a library for rendering images of tile based maps.
//!
//! StaticMap uses a builder pattern for building maps, lines and markers.
//!
//! To get started, build a map instance using [StaticMapBuilder][StaticMapBuilder]
//! and find the tool builders in the [tools][tools] module.
//!
//! ### Features:
//! - Render a map to a PNG image.
//! - Draw features on a map, such as:
//! - Lines
//! - Circles
//! - PNG icons
//!
//! ## Example
//! ```rust
//! use staticmap::{
//! tools::{Color, LineBuilder},
//! StaticMapBuilder, Error,
//! };
//!
//! fn main() -> Result<(), Error> {
//! let mut map = StaticMapBuilder::default()
//! .width(300)
//! .height(400)
//! .padding((10, 0))
//! .build()?;
//!
//! let lat: &[f64] = &[52.5, 48.9];
//! let lon: Vec<f64> = vec![13.4, 2.3];
//!
//! let red = Color::new(true, 255, 0, 0, 255);
//!
//! let line = LineBuilder::default()
//! .lat_coordinates(lat.to_vec())
//! .lon_coordinates(lon)
//! .width(3.)
//! .simplify(true)
//! .color(red)
//! .build()?;
//!
//! map.add_tool(line);
//! map.save_png("line.png")?;
//!
//! Ok(())
//! }
//! ```
#![warn(missing_docs)]
mod bounds;
mod error;
mod map;
/// Tools for drawing features onto the map.
pub mod tools;
pub use bounds::Bounds;
pub use error::Error;
pub use map::{StaticMap, StaticMapBuilder};
use std::f64::consts::PI;
type Result<T> = std::result::Result<T, Error>;
/// Longitude to x coordinate.
pub fn lon_to_x(mut lon: f64, zoom: u8) -> f64 {
if !(-180_f64..180_f64).contains(&lon) {
lon = (lon + 180_f64) % 360_f64 - 180_f64;
}
((lon + 180_f64) / 360_f64) * 2_f64.powi(zoom.into())
}
/// Latitude to y coordinate.
pub fn lat_to_y(mut lat: f64, zoom: u8) -> f64 {
if !(-90_f64..90_f64).contains(&lat) {
lat = (lat + 90_f64) % 180_f64 - 90_f64;
}
(1_f64 - ((lat * PI / 180_f64).tan() + 1_f64 / (lat * PI / 180_f64).cos()).ln() / PI) / 2_f64
* 2_f64.powi(zoom.into())
}
/// X to longitude coordinate.
pub fn x_to_lon(x: f64, zoom: u8) -> f64 {
x / 2_f64.powi(zoom.into()) * 360_f64 - 180_f64
}
/// Y to latitude coordinate.
pub fn y_to_lat(y: f64, zoom: u8) -> f64 {
(PI * (1_f64 - 2_f64 * y / 2_f64.powi(zoom.into())))
.sinh()
.atan()
/ PI
* 180_f64
}
fn simplify(points: Vec<(f64, f64)>, tolerance: f64) -> Vec<(f64, f64)> {
if points.len() < 2 {
return points;
}
let (simplified_points, points) = points.split_at(1);
let mut simplified_points = simplified_points.to_vec();
for point in points {
if let Some(a) = simplified_points.last() {
let x = ((a.0 - point.0).powi(2) + (a.1 - point.1).powi(2)).sqrt();
if x > tolerance {
simplified_points.push(*point)
}
}
}
let last_point = points
.last()
.expect("Internal logic error - 'points' must have at least two points");
simplified_points.push(*last_point);
simplified_points
}