use super::*;
impl GeoPoint {
pub fn new(lng: f64, lat: f64) -> Self {
Self { lng, lat }
}
}
impl Default for GeoBounds {
fn default() -> Self {
Self {
min_lng: f64::INFINITY,
min_lat: f64::INFINITY,
max_lng: f64::NEG_INFINITY,
max_lat: f64::NEG_INFINITY,
}
}
}
impl GeoBounds {
pub fn width(&self) -> f64 {
self.get_max_lng() - self.get_min_lng()
}
pub fn height(&self) -> f64 {
self.get_max_lat() - self.get_min_lat()
}
pub fn is_empty(&self) -> bool {
!(self.get_min_lng() <= self.get_max_lng() && self.get_min_lat() <= self.get_max_lat())
}
pub fn extend(&mut self, point: GeoPoint) {
self.set_min_lng(self.get_min_lng().min(point.lng));
self.set_min_lat(self.get_min_lat().min(point.lat));
self.set_max_lng(self.get_max_lng().max(point.lng));
self.set_max_lat(self.get_max_lat().max(point.lat));
}
}
impl MapRegion {
pub fn center(&self) -> Option<GeoPoint> {
let polygon: &RegionPolygon = self.get_polygons().first()?;
ring_center(&polygon.outer)
}
}
impl MapData {
pub fn from_regions(name: &str, regions: Vec<MapRegion>) -> Self {
let mut bounds: GeoBounds = GeoBounds::default();
for region in ®ions {
for polygon in ®ion.polygons {
for point in &polygon.outer {
bounds.extend(*point);
}
}
}
Self {
name: name.to_string(),
regions,
bounds,
}
}
pub fn from_geojson(name: &str, value: &DataValue) -> Result<Self, GeoError> {
parse_geojson(name, value)
}
pub fn region(&self, name: &str) -> Option<&MapRegion> {
self.get_regions()
.iter()
.find(|region: &&MapRegion| region.name == name)
}
}
impl MapRegistry {
pub fn new() -> Self {
Self {
maps: BTreeMap::new(),
}
}
pub fn register(&mut self, data: MapData) -> &mut Self {
self.get_mut_maps().insert(data.name.clone(), data);
self
}
pub fn register_geojson(
&mut self,
name: &str,
value: &DataValue,
) -> Result<&mut Self, GeoError> {
let data: MapData = MapData::from_geojson(name, value)?;
self.get_mut_maps().insert(name.to_string(), data);
Ok(self)
}
pub fn get(&self, name: &str) -> Option<&MapData> {
self.get_maps().get(name)
}
pub fn names(&self) -> Vec<String> {
self.get_maps().keys().cloned().collect()
}
pub fn len(&self) -> usize {
self.get_maps().len()
}
pub fn is_empty(&self) -> bool {
self.get_maps().is_empty()
}
pub fn clear(&mut self) {
self.get_mut_maps().clear();
}
}
impl GeoLayout {
pub fn compute(data: &MapData, rect: BBox, aspect_scale: f64) -> Self {
let bounds: GeoBounds = data.bounds;
let geo_width: f64 = bounds.width().max(f64::EPSILON);
let geo_height: f64 = bounds.height().max(f64::EPSILON);
let aspect: f64 = geo_width / geo_height * aspect_scale.max(f64::EPSILON);
let view: BBox = fit_aspect(rect, aspect);
let scale: f64 = (view.width() / geo_width).min(view.height() / geo_height);
let drawn_width: f64 = geo_width * scale;
let drawn_height: f64 = geo_height * scale;
let origin_x: f64 = view.min_x + (view.width() - drawn_width) * 0.5;
let origin_y: f64 = view.min_y + (view.height() - drawn_height) * 0.5;
let transform: Transform2D = Transform2D {
a: scale,
b: 0.0,
c: 0.0,
d: -scale,
e: origin_x - bounds.min_lng * scale,
f: origin_y + bounds.max_lat * scale,
};
let regions: Vec<GeoRegionLayout> = data
.regions
.iter()
.map(|region: &MapRegion| {
let path: Path = region_path(region, &transform);
let center: Point = region_center(region, &transform);
GeoRegionLayout {
name: region.name.clone(),
path,
center,
}
})
.collect();
Self {
name: data.name.clone(),
regions,
transform,
}
}
pub fn region(&self, name: &str) -> Option<&GeoRegionLayout> {
self.get_regions()
.iter()
.find(|region: &&GeoRegionLayout| region.name == name)
}
}
impl Display for GeoError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GeoError::MissingType => write!(formatter, "the map has no `type` field"),
GeoError::UnsupportedType(kind) => {
write!(formatter, "the map kind `{}` is not supported", kind)
}
GeoError::MissingGeometry => write!(formatter, "the map feature has no geometry"),
GeoError::MalformedGeometry(reason) => {
write!(formatter, "the map geometry is malformed: {}", reason)
}
}
}
}
impl Error for GeoError {}