Skip to content

Commit fbeece4

Browse files
committed
feat: Support match and get maplibre expression
* Add new Expr::Match expression to select one value from a list * Add conversion methods from maplibre match and get expression to galileo counterparts
1 parent 694fcc3 commit fbeece4

File tree

3 files changed

+97
-8
lines changed

3 files changed

+97
-8
lines changed

galileo-maplibre/src/style/expression.rs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ use std::fmt;
2424

2525
use galileo::expr::{
2626
ControlPoint, CubicBezierInterpolation, ExponentialInterpolation, Expr, ExprGeometryType,
27-
ExprValue, LinearInterpolation,
27+
ExprValue, LinearInterpolation, MatchCase, MatchExpr,
2828
};
2929
use serde::de::{self, SeqAccess, Visitor};
3030
use serde::{Deserialize, Deserializer, Serialize};
3131
use serde_json::Value;
3232

3333
use crate::layer::{UNSUPPORTED, log_unsupported};
34+
use crate::style::color::parse_css_color;
3435

3536
/// The interpolation curve used by [`Expr::Interpolate`] and its color-space
3637
/// variants.
@@ -250,7 +251,7 @@ pub enum MlExpr {
250251
/// Retrieves a feature property (or a property from `object_expr`).
251252
Get {
252253
/// The property name expression (typically a string literal).
253-
property: Box<MlExpr>,
254+
property: String,
254255
/// Optional object to retrieve the property from.
255256
object: Option<Box<MlExpr>>,
256257
},
@@ -809,11 +810,22 @@ impl MlExpr {
809810
})
810811
}
811812

813+
fn literal(v: &Value) -> Option<ExprValue<String>> {
814+
match v {
815+
Value::Bool(v) => Some(ExprValue::Boolean(*v)),
816+
Value::Number(v) => Some(ExprValue::Number(v.as_f64()?)),
817+
Value::String(v) => Some(
818+
parse_css_color(v)
819+
.map(ExprValue::from)
820+
.unwrap_or_else(|| ExprValue::String(v.clone())),
821+
),
822+
Value::Null => Some(ExprValue::Null),
823+
_ => None,
824+
}
825+
}
826+
812827
Some(match self {
813-
MlExpr::Literal(Value::Bool(v)) => ExprValue::Boolean(*v).into(),
814-
MlExpr::Literal(Value::Number(v)) => ExprValue::Number(v.as_f64()?).into(),
815-
MlExpr::Literal(Value::String(v)) => ExprValue::String(v.clone()).into(),
816-
MlExpr::Literal(Value::Null) => ExprValue::Null.into(),
828+
MlExpr::Literal(l) => literal(l)?.into(),
817829
MlExpr::All(parts) => Expr::All(
818830
parts
819831
.iter()
@@ -842,6 +854,39 @@ impl MlExpr {
842854
stops,
843855
} => interpolation_to_galileo(interpolation, input, stops)?,
844856
MlExpr::Zoom => Expr::Zoom,
857+
MlExpr::Get { property, object } => {
858+
if object.is_some() {
859+
log_unsupported!(format!("get from an object"));
860+
return None;
861+
}
862+
863+
Expr::Get(property.clone())
864+
}
865+
MlExpr::Match {
866+
input,
867+
branches,
868+
fallback,
869+
} => Expr::Match(Box::new(MatchExpr {
870+
input: input.to_galileo_expr()?,
871+
cases: branches
872+
.iter()
873+
.map(|(values, out)| {
874+
let entries = match values {
875+
Value::Array(entries) => entries.clone(),
876+
v => vec![v.clone()],
877+
};
878+
879+
Some(MatchCase {
880+
in_values: entries
881+
.into_iter()
882+
.map(|v| literal(&v))
883+
.collect::<Option<Vec<_>>>()?,
884+
out: out.to_galileo_expr()?,
885+
})
886+
})
887+
.collect::<Option<Vec<_>>>()?,
888+
fallback: fallback.to_galileo_expr()?,
889+
})),
845890
_ => {
846891
log::debug!("{UNSUPPORTED} Expression {self:?} is not supported yet");
847892
return None;
@@ -1052,7 +1097,10 @@ fn parse_expr_array(mut arr: Vec<Value>) -> Result<MlExpr, String> {
10521097
}
10531098

10541099
"get" => {
1055-
let property = box_expr(take_arg(&mut args, 1, "get")?)?;
1100+
let property = take_arg(&mut args, 1, "get")?
1101+
.as_str()
1102+
.ok_or_else(|| "Expected string value for get argument".to_string())?
1103+
.to_owned();
10561104
let object = if args.is_empty() {
10571105
None
10581106
} else {
@@ -1510,7 +1558,7 @@ mod tests {
15101558
let e = parse(json!(["get", "name"]));
15111559
match e {
15121560
MlExpr::Get { property, .. } => {
1513-
assert!(matches!(*property, MlExpr::Literal(Value::String(_))));
1561+
assert_eq!(property, "name");
15141562
}
15151563
other => panic!("expected Get, got {other:?}"),
15161564
}

galileo/src/expr/match_expr.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
use crate::expr::{Expr, ExprFeature, ExprValue, ExprView};
4+
5+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6+
pub struct MatchExpr {
7+
pub input: Expr,
8+
pub cases: Vec<MatchCase>,
9+
pub fallback: Expr,
10+
}
11+
12+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13+
pub struct MatchCase {
14+
pub in_values: Vec<ExprValue<String>>,
15+
pub out: Expr,
16+
}
17+
18+
impl MatchExpr {
19+
pub fn eval<'a>(&'a self, f: &'a impl ExprFeature, v: ExprView) -> ExprValue<&'a str> {
20+
let input = self.input.eval(f, v);
21+
let fallback = self.fallback.eval(f, v);
22+
if input.is_null() {
23+
return fallback;
24+
}
25+
26+
for case in &self.cases {
27+
for in_value in &case.in_values {
28+
if in_value.borrowed() == input {
29+
return case.out.eval(f, v);
30+
}
31+
}
32+
}
33+
34+
fallback
35+
}
36+
}

galileo/src/expr/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use serde::{Deserialize, Deserializer, Serialize};
66

77
mod interpolation;
88
pub use interpolation::*;
9+
mod match_expr;
10+
pub use match_expr::*;
911

1012
mod value;
1113
pub use value::{ExprGeometryType, ExprValue};
@@ -43,6 +45,8 @@ pub enum Expr {
4345
InterpolateExp(Box<ExponentialInterpolation>),
4446
InterpolateCubicBezier(Box<CubicBezierInterpolation>),
4547

48+
Match(Box<MatchExpr>),
49+
4650
WithOpacity(WithOpacityExpr),
4751
}
4852

@@ -183,6 +187,7 @@ impl Expr {
183187
Expr::InterpolateLinear(ip) => ip.eval(f, v),
184188
Expr::InterpolateExp(ip) => ip.eval(f, v),
185189
Expr::InterpolateCubicBezier(ip) => ip.eval(f, v),
190+
Expr::Match(m) => m.eval(f, v),
186191
Expr::WithOpacity(wo) => wo.eval(f, v),
187192
}
188193
}

0 commit comments

Comments
 (0)