1use std::collections::HashMap;
8
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12#[derive(Debug, Error)]
18pub enum StyleError {
19 #[error("invalid style version {0}; must be 8")]
21 InvalidVersion(u8),
22
23 #[error("unknown layer type: {0}")]
25 UnknownLayerType(String),
26
27 #[error("color parse error: {0}")]
29 ColorParseError(String),
30
31 #[error("invalid filter: {0}")]
33 InvalidFilter(String),
34
35 #[error("serde error: {0}")]
37 SerdeError(#[from] serde_json::Error),
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
46#[serde(rename_all = "camelCase")]
47pub struct StyleSpec {
48 pub version: u8,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub name: Option<String>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub metadata: Option<serde_json::Value>,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub center: Option<[f64; 2]>,
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub zoom: Option<f64>,
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub bearing: Option<f64>,
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub pitch: Option<f64>,
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub light: Option<Light>,
71 pub sources: HashMap<String, Source>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub sprite: Option<String>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub glyphs: Option<String>,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub transition: Option<Transition>,
82 pub layers: Vec<Layer>,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
92#[serde(tag = "type", rename_all = "kebab-case")]
93pub enum Source {
94 Vector {
96 #[serde(skip_serializing_if = "Option::is_none")]
98 url: Option<String>,
99 #[serde(skip_serializing_if = "Option::is_none")]
101 tiles: Option<Vec<String>>,
102 #[serde(rename = "minzoom", skip_serializing_if = "Option::is_none")]
104 min_zoom: Option<u8>,
105 #[serde(rename = "maxzoom", skip_serializing_if = "Option::is_none")]
107 max_zoom: Option<u8>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 attribution: Option<String>,
111 },
112 Raster {
114 #[serde(skip_serializing_if = "Option::is_none")]
116 url: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none")]
119 tiles: Option<Vec<String>>,
120 #[serde(rename = "tileSize", skip_serializing_if = "Option::is_none")]
122 tile_size: Option<u32>,
123 #[serde(rename = "minzoom", skip_serializing_if = "Option::is_none")]
125 min_zoom: Option<u8>,
126 #[serde(rename = "maxzoom", skip_serializing_if = "Option::is_none")]
128 max_zoom: Option<u8>,
129 },
130 #[serde(rename = "raster-dem")]
132 RasterDem {
133 #[serde(skip_serializing_if = "Option::is_none")]
135 url: Option<String>,
136 #[serde(default)]
138 encoding: DemEncoding,
139 },
140 #[serde(rename = "geojson")]
142 GeoJson {
143 data: serde_json::Value,
145 #[serde(rename = "maxzoom", skip_serializing_if = "Option::is_none")]
147 max_zoom: Option<u8>,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 cluster: Option<bool>,
151 #[serde(rename = "clusterRadius", skip_serializing_if = "Option::is_none")]
153 cluster_radius: Option<u32>,
154 },
155 Image {
157 url: String,
159 coordinates: [[f64; 2]; 4],
161 },
162 Video {
164 urls: Vec<String>,
166 coordinates: [[f64; 2]; 4],
168 },
169}
170
171#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
173#[serde(rename_all = "lowercase")]
174pub enum DemEncoding {
175 #[default]
177 Mapbox,
178 Terrarium,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
188#[serde(rename_all = "camelCase")]
189pub struct Layer {
190 pub id: String,
192 #[serde(rename = "type")]
194 pub layer_type: LayerType,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub source: Option<String>,
198 #[serde(rename = "source-layer", skip_serializing_if = "Option::is_none")]
200 pub source_layer: Option<String>,
201 #[serde(rename = "minzoom", skip_serializing_if = "Option::is_none")]
203 pub min_zoom: Option<f64>,
204 #[serde(rename = "maxzoom", skip_serializing_if = "Option::is_none")]
206 pub max_zoom: Option<f64>,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub filter: Option<Filter>,
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub layout: Option<Layout>,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub paint: Option<Paint>,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
220#[serde(rename_all = "kebab-case")]
221pub enum LayerType {
222 Background,
224 Fill,
226 Line,
228 Symbol,
230 Raster,
232 Circle,
234 FillExtrusion,
236 Heatmap,
238 Hillshade,
240 Sky,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
250pub enum GeomFilter {
251 Point,
253 LineString,
255 Polygon,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
261#[serde(untagged)]
262pub enum Filter {
263 All(#[serde(skip)] Vec<Filter>),
265 Any(#[serde(skip)] Vec<Filter>),
267 None(#[serde(skip)] Vec<Filter>),
269 Eq {
271 property: String,
273 value: serde_json::Value,
275 },
276 Ne {
278 property: String,
280 value: serde_json::Value,
282 },
283 Lt {
285 property: String,
287 value: f64,
289 },
290 Lte {
292 property: String,
294 value: f64,
296 },
297 Gt {
299 property: String,
301 value: f64,
303 },
304 Gte {
306 property: String,
308 value: f64,
310 },
311 In {
313 property: String,
315 values: Vec<serde_json::Value>,
317 },
318 Has(String),
320 NotHas(String),
322 GeometryType(GeomFilter),
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
332#[serde(rename_all = "lowercase")]
333pub enum Interpolation {
334 Linear,
336 Exponential(f64),
338 CubicBezier([f64; 4]),
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
344#[serde(untagged)]
345pub enum Expression {
346 Get(String),
348 Has(String),
350 Literal(serde_json::Value),
352 Array(Vec<Expression>),
354 Case {
356 conditions: Vec<(Expression, Expression)>,
358 fallback: Box<Expression>,
360 },
361 Match {
363 input: Box<Expression>,
365 cases: Vec<(serde_json::Value, Expression)>,
367 fallback: Box<Expression>,
369 },
370 Interpolate {
372 interpolation: Interpolation,
374 input: Box<Expression>,
376 stops: Vec<(f64, Expression)>,
378 },
379 Step {
381 input: Box<Expression>,
383 default: Box<Expression>,
385 stops: Vec<(f64, Expression)>,
387 },
388 Zoom,
390 Add(Box<Expression>, Box<Expression>),
392 Subtract(Box<Expression>, Box<Expression>),
394 Multiply(Box<Expression>, Box<Expression>),
396 Divide(Box<Expression>, Box<Expression>),
398 Coalesce(Vec<Expression>),
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
404#[serde(untagged)]
405pub enum PropertyValue<T: Clone> {
406 Literal(T),
408 Expression(Expression),
410}
411
412#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
418pub struct Color {
419 pub r: u8,
421 pub g: u8,
423 pub b: u8,
425 pub a: f32,
427}
428
429impl Color {
430 pub fn parse(s: &str) -> Result<Self, StyleError> {
432 let s = s.trim();
433 if let Some(hex) = s.strip_prefix('#') {
434 Self::parse_hex(hex)
435 } else if let Some(inner) = s.strip_prefix("rgba(").and_then(|t| t.strip_suffix(')')) {
436 Self::parse_rgba(inner)
437 } else if let Some(inner) = s.strip_prefix("rgb(").and_then(|t| t.strip_suffix(')')) {
438 Self::parse_rgb(inner)
439 } else {
440 Err(StyleError::ColorParseError(format!(
441 "unsupported color format: {s}"
442 )))
443 }
444 }
445
446 fn parse_hex(hex: &str) -> Result<Self, StyleError> {
447 let err = || StyleError::ColorParseError(format!("invalid hex color: #{hex}"));
448 match hex.len() {
449 6 => {
450 let r = u8::from_str_radix(&hex[0..2], 16).map_err(|_| err())?;
451 let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| err())?;
452 let b = u8::from_str_radix(&hex[4..6], 16).map_err(|_| err())?;
453 Ok(Color { r, g, b, a: 1.0 })
454 }
455 3 => {
456 let r = u8::from_str_radix(&hex[0..1].repeat(2), 16).map_err(|_| err())?;
457 let g = u8::from_str_radix(&hex[1..2].repeat(2), 16).map_err(|_| err())?;
458 let b = u8::from_str_radix(&hex[2..3].repeat(2), 16).map_err(|_| err())?;
459 Ok(Color { r, g, b, a: 1.0 })
460 }
461 _ => Err(err()),
462 }
463 }
464
465 fn parse_rgb(inner: &str) -> Result<Self, StyleError> {
466 let parts: Vec<&str> = inner.split(',').collect();
467 if parts.len() != 3 {
468 return Err(StyleError::ColorParseError(format!(
469 "rgb() expects 3 components, got {}",
470 parts.len()
471 )));
472 }
473 let parse_u8 = |s: &str| -> Result<u8, StyleError> {
474 s.trim()
475 .parse::<u8>()
476 .map_err(|_| StyleError::ColorParseError(format!("invalid channel value: {s}")))
477 };
478 Ok(Color {
479 r: parse_u8(parts[0])?,
480 g: parse_u8(parts[1])?,
481 b: parse_u8(parts[2])?,
482 a: 1.0,
483 })
484 }
485
486 fn parse_rgba(inner: &str) -> Result<Self, StyleError> {
487 let parts: Vec<&str> = inner.split(',').collect();
488 if parts.len() != 4 {
489 return Err(StyleError::ColorParseError(format!(
490 "rgba() expects 4 components, got {}",
491 parts.len()
492 )));
493 }
494 let parse_u8 = |s: &str| -> Result<u8, StyleError> {
495 s.trim()
496 .parse::<u8>()
497 .map_err(|_| StyleError::ColorParseError(format!("invalid channel value: {s}")))
498 };
499 let a: f32 = parts[3]
500 .trim()
501 .parse()
502 .map_err(|_| StyleError::ColorParseError(format!("invalid alpha: {}", parts[3])))?;
503 Ok(Color {
504 r: parse_u8(parts[0])?,
505 g: parse_u8(parts[1])?,
506 b: parse_u8(parts[2])?,
507 a,
508 })
509 }
510
511 pub fn to_css(&self) -> String {
513 format!("rgba({},{},{},{:.6})", self.r, self.g, self.b, self.a)
514 }
515}
516
517#[derive(Debug, Clone, Default, Serialize, Deserialize)]
523pub struct Paint(pub HashMap<String, serde_json::Value>);
524
525impl Paint {
526 fn get_pv<T>(&self, key: &str) -> Option<PropertyValue<T>>
528 where
529 T: Clone + for<'de> Deserialize<'de>,
530 {
531 let v = self.0.get(key)?;
532 serde_json::from_value::<PropertyValue<T>>(v.clone()).ok()
533 }
534
535 pub fn fill_color(&self) -> Option<PropertyValue<Color>> {
537 self.get_pv("fill-color")
538 }
539
540 pub fn fill_opacity(&self) -> Option<PropertyValue<f64>> {
542 self.get_pv("fill-opacity")
543 }
544
545 pub fn line_color(&self) -> Option<PropertyValue<Color>> {
547 self.get_pv("line-color")
548 }
549
550 pub fn line_width(&self) -> Option<PropertyValue<f64>> {
552 self.get_pv("line-width")
553 }
554
555 pub fn line_opacity(&self) -> Option<PropertyValue<f64>> {
557 self.get_pv("line-opacity")
558 }
559
560 pub fn circle_color(&self) -> Option<PropertyValue<Color>> {
562 self.get_pv("circle-color")
563 }
564
565 pub fn circle_radius(&self) -> Option<PropertyValue<f64>> {
567 self.get_pv("circle-radius")
568 }
569
570 pub fn raster_opacity(&self) -> Option<PropertyValue<f64>> {
572 self.get_pv("raster-opacity")
573 }
574
575 pub fn raster_hue_rotate(&self) -> Option<PropertyValue<f64>> {
577 self.get_pv("raster-hue-rotate")
578 }
579
580 pub fn background_color(&self) -> Option<PropertyValue<Color>> {
582 self.get_pv("background-color")
583 }
584}
585
586#[derive(Debug, Clone, Default, Serialize, Deserialize)]
592pub struct Layout(pub HashMap<String, serde_json::Value>);
593
594impl Layout {
595 fn get_str(&self, key: &str) -> Option<&str> {
596 self.0.get(key)?.as_str()
597 }
598
599 fn get_pv<T>(&self, key: &str) -> Option<PropertyValue<T>>
600 where
601 T: Clone + for<'de> Deserialize<'de>,
602 {
603 let v = self.0.get(key)?;
604 serde_json::from_value::<PropertyValue<T>>(v.clone()).ok()
605 }
606
607 pub fn visibility(&self) -> Visibility {
609 match self.get_str("visibility") {
610 Some("none") => Visibility::None,
611 _ => Visibility::Visible,
612 }
613 }
614
615 pub fn line_cap(&self) -> LineCap {
617 match self.get_str("line-cap") {
618 Some("round") => LineCap::Round,
619 Some("square") => LineCap::Square,
620 _ => LineCap::Butt,
621 }
622 }
623
624 pub fn line_join(&self) -> LineJoin {
626 match self.get_str("line-join") {
627 Some("round") => LineJoin::Round,
628 Some("miter") => LineJoin::Miter,
629 _ => LineJoin::Bevel,
630 }
631 }
632
633 pub fn symbol_placement(&self) -> SymbolPlacement {
635 match self.get_str("symbol-placement") {
636 Some("line") => SymbolPlacement::Line,
637 Some("line-center") => SymbolPlacement::LineCenter,
638 _ => SymbolPlacement::Point,
639 }
640 }
641
642 pub fn text_field(&self) -> Option<PropertyValue<String>> {
644 self.get_pv("text-field")
645 }
646
647 pub fn text_size(&self) -> Option<PropertyValue<f64>> {
649 self.get_pv("text-size")
650 }
651
652 pub fn icon_image(&self) -> Option<PropertyValue<String>> {
654 self.get_pv("icon-image")
655 }
656}
657
658#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
664#[serde(rename_all = "lowercase")]
665pub enum Visibility {
666 #[default]
668 Visible,
669 None,
671}
672
673#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
675#[serde(rename_all = "lowercase")]
676pub enum LineCap {
677 #[default]
679 Butt,
680 Round,
682 Square,
684}
685
686#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
688#[serde(rename_all = "lowercase")]
689pub enum LineJoin {
690 #[default]
692 Bevel,
693 Round,
695 Miter,
697}
698
699#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
701#[serde(rename_all = "kebab-case")]
702pub enum SymbolPlacement {
703 #[default]
705 Point,
706 Line,
708 LineCenter,
710}
711
712#[derive(Debug, Clone, Serialize, Deserialize)]
718pub struct Transition {
719 #[serde(default)]
721 pub duration: u32,
722 #[serde(default)]
724 pub delay: u32,
725}
726
727#[derive(Debug, Clone, Serialize, Deserialize)]
733pub struct Light {
734 #[serde(default)]
736 pub anchor: LightAnchor,
737 pub color: Color,
739 #[serde(default = "default_intensity")]
741 pub intensity: f64,
742 #[serde(default = "default_light_position")]
744 pub position: [f64; 3],
745}
746
747fn default_intensity() -> f64 {
748 0.5
749}
750
751fn default_light_position() -> [f64; 3] {
752 [1.15, 210.0, 30.0]
753}
754
755#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
757#[serde(rename_all = "lowercase")]
758pub enum LightAnchor {
759 #[default]
761 Viewport,
762 Map,
764}
765
766#[derive(Debug, Clone)]
772pub struct ValidationError {
773 pub layer_id: Option<String>,
775 pub message: String,
777}
778
779pub struct StyleValidator;
781
782impl StyleValidator {
783 pub fn validate(spec: &StyleSpec) -> Vec<ValidationError> {
785 let mut errors: Vec<ValidationError> = Vec::new();
786
787 if spec.version != 8 {
789 errors.push(ValidationError {
790 layer_id: None,
791 message: format!("style version must be 8, got {}", spec.version),
792 });
793 }
794
795 let mut seen_ids: HashMap<&str, usize> = HashMap::new();
797 for (idx, layer) in spec.layers.iter().enumerate() {
798 if let Some(prev) = seen_ids.insert(layer.id.as_str(), idx) {
799 errors.push(ValidationError {
800 layer_id: Some(layer.id.clone()),
801 message: format!(
802 "duplicate layer id '{}' (first at index {prev}, repeated at index {idx})",
803 layer.id
804 ),
805 });
806 }
807 }
808
809 for layer in &spec.layers {
810 if let Some(src) = &layer.source {
812 if !spec.sources.contains_key(src.as_str()) {
813 errors.push(ValidationError {
814 layer_id: Some(layer.id.clone()),
815 message: format!("layer references unknown source '{src}'"),
816 });
817 }
818 }
819
820 if let (Some(min), Some(max)) = (layer.min_zoom, layer.max_zoom) {
822 if min > max {
823 errors.push(ValidationError {
824 layer_id: Some(layer.id.clone()),
825 message: format!("minzoom ({min}) must be <= maxzoom ({max})"),
826 });
827 }
828 }
829
830 if layer.layer_type == LayerType::Background && layer.source.is_some() {
832 errors.push(ValidationError {
833 layer_id: Some(layer.id.clone()),
834 message: "background layer must not reference a source".to_string(),
835 });
836 }
837
838 let requires_source = matches!(
840 layer.layer_type,
841 LayerType::Fill | LayerType::Line | LayerType::Circle | LayerType::Symbol
842 );
843 if requires_source && layer.source.is_none() {
844 errors.push(ValidationError {
845 layer_id: Some(layer.id.clone()),
846 message: format!("{:?} layer requires a source", layer.layer_type),
847 });
848 }
849 }
850
851 errors
852 }
853}
854
855pub struct StyleRenderer;
861
862impl StyleRenderer {
863 pub fn eval_zoom_color(value: &PropertyValue<Color>, zoom: f64) -> Color {
869 match value {
870 PropertyValue::Literal(c) => c.clone(),
871 PropertyValue::Expression(expr) => Self::eval_expr_color(expr, zoom).unwrap_or(Color {
872 r: 0,
873 g: 0,
874 b: 0,
875 a: 1.0,
876 }),
877 }
878 }
879
880 fn eval_expr_color(expr: &Expression, zoom: f64) -> Option<Color> {
881 match expr {
882 Expression::Literal(v) => {
883 let s = v.as_str()?;
884 Color::parse(s).ok()
885 }
886 Expression::Interpolate {
887 interpolation,
888 input,
889 stops,
890 } => {
891 let input_val = Self::eval_expr_f64(input, zoom)?;
892 if stops.is_empty() {
893 return None;
894 }
895 let (lo_stop, lo_expr, hi_stop, hi_expr) =
897 Self::find_stops_color(stops, input_val)?;
898 let lo_c = Self::eval_expr_color(lo_expr, zoom)?;
899 let hi_c = Self::eval_expr_color(hi_expr, zoom)?;
900 let t = Self::interp_t(interpolation, input_val, lo_stop, hi_stop);
901 Some(lerp_color(&lo_c, &hi_c, t))
902 }
903 _ => None,
904 }
905 }
906
907 fn find_stops_color(
908 stops: &[(f64, Expression)],
909 input: f64,
910 ) -> Option<(f64, &Expression, f64, &Expression)> {
911 if stops.len() == 1 {
912 return Some((stops[0].0, &stops[0].1, stops[0].0, &stops[0].1));
913 }
914 let last = stops.last()?;
915 if input >= last.0 {
916 let second_last = &stops[stops.len() - 2];
917 return Some((second_last.0, &second_last.1, last.0, &last.1));
918 }
919 let first = stops.first()?;
920 if input <= first.0 {
921 let second = &stops[1];
922 return Some((first.0, &first.1, second.0, &second.1));
923 }
924 for i in 0..stops.len() - 1 {
925 if input >= stops[i].0 && input < stops[i + 1].0 {
926 return Some((stops[i].0, &stops[i].1, stops[i + 1].0, &stops[i + 1].1));
927 }
928 }
929 None
930 }
931
932 pub fn eval_zoom_f64(value: &PropertyValue<f64>, zoom: f64) -> f64 {
938 match value {
939 PropertyValue::Literal(v) => *v,
940 PropertyValue::Expression(expr) => Self::eval_expr_f64(expr, zoom).unwrap_or(0.0),
941 }
942 }
943
944 fn eval_expr_f64(expr: &Expression, zoom: f64) -> Option<f64> {
945 match expr {
946 Expression::Zoom => Some(zoom),
947 Expression::Literal(v) => v.as_f64(),
948 Expression::Interpolate {
949 interpolation,
950 input,
951 stops,
952 } => {
953 let input_val = Self::eval_expr_f64(input, zoom)?;
954 if stops.is_empty() {
955 return None;
956 }
957 if stops.len() == 1 {
958 return Self::eval_expr_f64(&stops[0].1, zoom);
959 }
960 let last = stops.last()?;
961 if input_val >= last.0 {
962 return Self::eval_expr_f64(&last.1, zoom);
963 }
964 let first = stops.first()?;
965 if input_val <= first.0 {
966 return Self::eval_expr_f64(&first.1, zoom);
967 }
968 for i in 0..stops.len() - 1 {
969 if input_val >= stops[i].0 && input_val < stops[i + 1].0 {
970 let lo = Self::eval_expr_f64(&stops[i].1, zoom)?;
971 let hi = Self::eval_expr_f64(&stops[i + 1].1, zoom)?;
972 let t =
973 Self::interp_t(interpolation, input_val, stops[i].0, stops[i + 1].0);
974 return Some(lo + t * (hi - lo));
975 }
976 }
977 None
978 }
979 Expression::Step {
980 input,
981 default,
982 stops,
983 } => {
984 let input_val = Self::eval_expr_f64(input, zoom)?;
985 let mut result = Self::eval_expr_f64(default, zoom)?;
986 for (stop, val) in stops {
987 if input_val >= *stop {
988 result = Self::eval_expr_f64(val, zoom)?;
989 }
990 }
991 Some(result)
992 }
993 Expression::Add(a, b) => {
994 Some(Self::eval_expr_f64(a, zoom)? + Self::eval_expr_f64(b, zoom)?)
995 }
996 Expression::Subtract(a, b) => {
997 Some(Self::eval_expr_f64(a, zoom)? - Self::eval_expr_f64(b, zoom)?)
998 }
999 Expression::Multiply(a, b) => {
1000 Some(Self::eval_expr_f64(a, zoom)? * Self::eval_expr_f64(b, zoom)?)
1001 }
1002 Expression::Divide(a, b) => {
1003 let divisor = Self::eval_expr_f64(b, zoom)?;
1004 if divisor == 0.0 {
1005 None
1006 } else {
1007 Some(Self::eval_expr_f64(a, zoom)? / divisor)
1008 }
1009 }
1010 _ => None,
1011 }
1012 }
1013
1014 fn interp_t(interp: &Interpolation, input: f64, lo: f64, hi: f64) -> f64 {
1016 let range = hi - lo;
1017 if range == 0.0 {
1018 return 0.0;
1019 }
1020 match interp {
1021 Interpolation::Linear => (input - lo) / range,
1022 Interpolation::Exponential(base) => {
1023 if (base - 1.0).abs() < f64::EPSILON {
1024 (input - lo) / range
1025 } else {
1026 (base.powf(input - lo) - 1.0) / (base.powf(range) - 1.0)
1027 }
1028 }
1029 Interpolation::CubicBezier(_) => {
1030 (input - lo) / range
1032 }
1033 }
1034 }
1035
1036 pub fn feature_matches_filter(
1038 filter: &Filter,
1039 properties: &HashMap<String, serde_json::Value>,
1040 ) -> bool {
1041 match filter {
1042 Filter::All(filters) => filters
1043 .iter()
1044 .all(|f| Self::feature_matches_filter(f, properties)),
1045 Filter::Any(filters) => filters
1046 .iter()
1047 .any(|f| Self::feature_matches_filter(f, properties)),
1048 Filter::None(filters) => !filters
1049 .iter()
1050 .any(|f| Self::feature_matches_filter(f, properties)),
1051 Filter::Eq { property, value } => properties.get(property.as_str()) == Some(value),
1052 Filter::Ne { property, value } => properties.get(property.as_str()) != Some(value),
1053 Filter::Lt { property, value } => properties
1054 .get(property.as_str())
1055 .and_then(|v| v.as_f64())
1056 .is_some_and(|v| v < *value),
1057 Filter::Lte { property, value } => properties
1058 .get(property.as_str())
1059 .and_then(|v| v.as_f64())
1060 .is_some_and(|v| v <= *value),
1061 Filter::Gt { property, value } => properties
1062 .get(property.as_str())
1063 .and_then(|v| v.as_f64())
1064 .is_some_and(|v| v > *value),
1065 Filter::Gte { property, value } => properties
1066 .get(property.as_str())
1067 .and_then(|v| v.as_f64())
1068 .is_some_and(|v| v >= *value),
1069 Filter::In { property, values } => properties
1070 .get(property.as_str())
1071 .is_some_and(|v| values.contains(v)),
1072 Filter::Has(property) => properties.contains_key(property.as_str()),
1073 Filter::NotHas(property) => !properties.contains_key(property.as_str()),
1074 Filter::GeometryType(_) => {
1075 true
1079 }
1080 }
1081 }
1082}
1083
1084fn lerp_color(a: &Color, b: &Color, t: f64) -> Color {
1089 let lerp_u8 = |lo: u8, hi: u8| -> u8 {
1090 let v = f64::from(lo) + t * (f64::from(hi) - f64::from(lo));
1091 v.round() as u8
1092 };
1093 Color {
1094 r: lerp_u8(a.r, b.r),
1095 g: lerp_u8(a.g, b.g),
1096 b: lerp_u8(a.b, b.b),
1097 a: a.a + t as f32 * (b.a - a.a),
1098 }
1099}