use std::collections::HashMap;
use svg::node::Node;
use svg::node::element::Group;
use crate::components::bar::{Bar, BarBlock, BarLabelPosition};
use crate::colors::Color;
use crate::{Scale, BarDatum};
use crate::scales::ScaleType;
use crate::components::DatumRepresentation;
use crate::views::View;
use crate::chart::Orientation;
use crate::components::legend::{LegendEntry, LegendMarkerType};
pub struct VerticalBarView<'a> {
label_position: BarLabelPosition,
labels_visible: bool,
rounding_precision: Option<usize>,
entries: Vec<Bar>,
keys: Vec<String>,
colors: Vec<Color>,
color_map: HashMap<String, String>,
x_scale: Option<&'a dyn Scale<String>>,
y_scale: Option<&'a dyn Scale<f32>>,
custom_data_label: String,
}
impl<'a> VerticalBarView<'a> {
pub fn new() -> Self {
Self {
label_position: BarLabelPosition::EndOutside,
labels_visible: true,
rounding_precision: None,
entries: Vec::new(),
keys: Vec::new(),
colors: Color::color_scheme_10(),
color_map: HashMap::new(),
x_scale: None,
y_scale: None,
custom_data_label: String::new(),
}
}
pub fn set_x_scale(mut self, scale: &'a impl Scale<String>) -> Self {
self.x_scale = Some(scale);
self
}
pub fn set_y_scale(mut self, scale: &'a impl Scale<f32>) -> Self {
self.y_scale = Some(scale);
self
}
pub fn set_keys(mut self, keys: Vec<String>) -> Self {
self.keys = keys;
self
}
pub fn set_label_position(mut self, label_position: BarLabelPosition) -> Self {
self.label_position = label_position;
self
}
pub fn set_colors(mut self, colors: Vec<Color>) -> Self {
self.colors = colors;
self
}
pub fn set_label_visibility(mut self, label_visibility: bool) -> Self {
self.labels_visible = label_visibility;
self
}
pub fn set_custom_data_label(mut self, label: String) -> Self {
self.custom_data_label = label;
self
}
pub fn set_label_rounding_precision(mut self, nr_of_digits: usize) -> Self {
self.rounding_precision = Some(nr_of_digits);
self
}
pub fn load_data(mut self, data: &Vec<impl BarDatum>) -> Result<Self, String> {
match self.x_scale {
Some(scale) if scale.get_type() == ScaleType::Band => {},
_ => return Err("The X axis scale should be a Band scale.".to_string()),
}
match self.y_scale {
Some(scale) if scale.get_type() == ScaleType::Linear => {},
_ => return Err("The Y axis scale should be a Linear scale.".to_string()),
}
if self.keys.len() == 0 {
self.keys = Self::extract_keys(&data);
}
let mut categories: HashMap<String, Vec<(&String, f32)>> = HashMap::new();
for (i, key) in self.keys.iter_mut().enumerate() {
self.color_map.insert(key.clone(), self.colors[i % self.colors.len()].as_hex());
for entry in data.iter() {
if entry.get_key() == *key {
let entry_category = entry.get_category();
if !categories.contains_key(&entry_category) {
categories.insert(entry.get_category(), Vec::new());
}
if let Some(category_entries) = categories.get_mut(&entry_category) {
category_entries.push((key, entry.get_value()));
}
}
}
}
let mut bars = Vec::new();
let y_range_is_reversed = self.y_scale.unwrap().is_range_reversed();
for (category, key_value_pairs) in categories.iter_mut() {
let mut value_acc = 0_f32;
let mut bar_blocks = Vec::new();
let mut stacked_start = self.y_scale.unwrap().scale(&value_acc);
let mut stacked_end = stacked_start;
for (key, value) in key_value_pairs.iter() {
value_acc += *value;
if y_range_is_reversed {
stacked_end = stacked_start;
stacked_start = self.y_scale.unwrap().scale(&value_acc);
} else {
stacked_start = stacked_end;
stacked_end = self.y_scale.unwrap().scale(&value_acc);
}
bar_blocks.push(BarBlock::new(stacked_start, stacked_end, *value, self.color_map.get(*key).unwrap().clone()));
}
let bar = Bar::new(bar_blocks, Orientation::Vertical, category.to_string(), self.label_position, self.labels_visible, self.rounding_precision, self.x_scale.unwrap().bandwidth().unwrap(), self.x_scale.unwrap().scale(category));
bars.push(bar);
}
for bar in bars {
self.add_bar(bar);
}
Ok(self)
}
fn extract_keys(data: &Vec<impl BarDatum>) -> Vec<String> {
let mut keys = Vec::new();
let mut map = HashMap::new();
for datum in data.iter() {
match map.insert(datum.get_key(), 0) {
Some(_) => {},
None => keys.push(datum.get_key()),
}
}
keys
}
fn add_bar(&mut self, bar: Bar) {
self.entries.push(bar);
}
}
impl<'a> View<'a> for VerticalBarView<'a> {
fn to_svg(&self) -> Result<Group, String> {
let mut group = Group::new();
for entry in self.entries.iter() {
let child_svg = entry.to_svg()?;
group.append(child_svg);
}
Ok(group)
}
fn get_legend_entries(&self) -> Vec<LegendEntry> {
let mut entries = Vec::new();
if self.keys.len() == 1 && self.keys[0].len() == 0 {
entries.push(LegendEntry::new(LegendMarkerType::Square, self.color_map.get(&self.keys[0]).unwrap().clone(), String::from("none"), self.custom_data_label.clone()));
} else {
for key in self.keys.iter() {
entries.push(LegendEntry::new(LegendMarkerType::Square, self.color_map.get(key).unwrap().clone(), String::from("none"), key.clone()));
}
}
entries
}
}