use std::collections::HashMap;
use galileo_mvt::{MvtFeature, MvtGeometry};
use serde::{Deserialize, Serialize};
use crate::render::point_paint::PointPaint;
use crate::render::text::TextStyle;
use crate::render::{LineCap, LinePaint, PolygonPaint};
use crate::Color;
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct VectorTileStyle {
pub rules: Vec<StyleRule>,
pub background: Color,
}
impl VectorTileStyle {
pub fn get_style_rule(&self, layer_name: &str, feature: &MvtFeature) -> Option<&StyleRule> {
self.rules.iter().find(|&rule| {
let correct_geometry_type = match feature.geometry {
MvtGeometry::Point(_)
if matches!(
rule.symbol,
VectorTileSymbol::Point(_) | VectorTileSymbol::Label(_)
) =>
{
true
}
MvtGeometry::LineString(_) if matches!(rule.symbol, VectorTileSymbol::Line(_)) => {
true
}
MvtGeometry::Polygon(_) if matches!(rule.symbol, VectorTileSymbol::Polygon(_)) => {
true
}
_ => false,
};
if !correct_geometry_type {
return false;
}
if rule.layer_name.as_ref().is_some_and(|v| v != layer_name) {
return false;
}
let filter_check_passed = rule
.properties
.iter()
.all(|(key, value)| feature.properties.get(key).is_some_and(|v| v.eq_str(value)));
filter_check_passed
})
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct StyleRule {
pub layer_name: Option<String>,
#[serde(default)]
pub properties: HashMap<String, String>,
#[serde(default)]
pub symbol: VectorTileSymbol,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum VectorTileSymbol {
None,
#[serde(rename = "point")]
Point(VectorTilePointSymbol),
#[serde(rename = "line")]
Line(VectorTileLineSymbol),
#[serde(rename = "polygon")]
Polygon(VectorTilePolygonSymbol),
#[serde(rename = "label")]
Label(VectorTileLabelSymbol),
}
impl Default for VectorTileSymbol {
fn default() -> Self {
Self::None
}
}
impl VectorTileSymbol {
pub(crate) fn line(&self) -> Option<&VectorTileLineSymbol> {
match self {
Self::Line(symbol) => Some(symbol),
_ => None,
}
}
pub(crate) fn polygon(&self) -> Option<&VectorTilePolygonSymbol> {
match self {
Self::Polygon(symbol) => Some(symbol),
_ => None,
}
}
pub(crate) fn point(&self) -> Option<&VectorTilePointSymbol> {
match self {
Self::Point(symbol) => Some(symbol),
_ => None,
}
}
pub(crate) fn label(&self) -> Option<&VectorTileLabelSymbol> {
match self {
Self::Label(symbol) => Some(symbol),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
pub struct VectorTilePointSymbol {
pub size: f64,
pub color: Color,
}
impl From<VectorTilePointSymbol> for PointPaint<'_> {
fn from(value: VectorTilePointSymbol) -> Self {
PointPaint::circle(value.color, value.size as f32)
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
pub struct VectorTileLineSymbol {
pub width: f64,
pub stroke_color: Color,
}
impl From<VectorTileLineSymbol> for LinePaint {
fn from(value: VectorTileLineSymbol) -> Self {
Self {
color: value.stroke_color,
width: value.width,
offset: 0.0,
line_cap: LineCap::Butt,
}
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
pub struct VectorTilePolygonSymbol {
pub fill_color: Color,
}
impl From<VectorTilePolygonSymbol> for PolygonPaint {
fn from(value: VectorTilePolygonSymbol) -> Self {
Self {
color: value.fill_color,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VectorTileLabelSymbol {
pub pattern: String,
pub text_style: TextStyle,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn symbol_serialization_point() {
let symbol = VectorTileSymbol::Point(VectorTilePointSymbol {
size: 10.0,
color: Color::BLACK,
});
let json = serde_json::to_string_pretty(&symbol).unwrap();
eprintln!("{json}");
let value = serde_json::to_value(&symbol).unwrap();
assert!(value.as_object().unwrap().get("point").is_some());
assert!(value.as_object().unwrap().get("polygon").is_none());
}
#[test]
fn serialize_with_bincode() {
let rule = StyleRule {
layer_name: None,
properties: HashMap::new(),
symbol: VectorTileSymbol::None,
};
let serialized = bincode::serde::encode_to_vec(&rule, bincode::config::standard()).unwrap();
let _: (StyleRule, _) =
bincode::serde::decode_from_slice(&serialized, bincode::config::standard()).unwrap();
}
}