Skip to content

Commit 94ef453

Browse files
committed
feat: Improve serialization/deserialization of expressions
1 parent 8b22b49 commit 94ef453

7 files changed

Lines changed: 101 additions & 76 deletions

File tree

galileo-maplibre/src/layer/vector_tile.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ where
146146
None => Expr::Zoom,
147147
};
148148

149-
Some(Expr::InterpolateExp(Box::new(ExponentialInterpolation {
149+
Some(Expr::Exponential(Box::new(ExponentialInterpolation {
150150
base: function.base,
151151
input,
152152
control_points,

galileo-maplibre/src/style/expression.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ fn interpolation_to_galileo(
901901
stops: &[(f64, MlExpr)],
902902
) -> Option<Expr> {
903903
Some(match interpolation {
904-
Interpolation::Linear => Expr::InterpolateLinear(Box::new(LinearInterpolation {
904+
Interpolation::Linear => Expr::Linear(Box::new(LinearInterpolation {
905905
input: input.to_galileo_expr()?,
906906
control_points: stops
907907
.iter()
@@ -914,7 +914,7 @@ fn interpolation_to_galileo(
914914
.collect::<Option<Vec<_>>>()?,
915915
})),
916916
Interpolation::Exponential { base } => {
917-
Expr::InterpolateExp(Box::new(ExponentialInterpolation {
917+
Expr::Exponential(Box::new(ExponentialInterpolation {
918918
base: *base,
919919
input: input.to_galileo_expr()?,
920920
control_points: stops
@@ -929,7 +929,7 @@ fn interpolation_to_galileo(
929929
}))
930930
}
931931
Interpolation::CubicBezier { x1, y1, x2, y2 } => {
932-
Expr::InterpolateCubicBezier(Box::new(CubicBezierInterpolation {
932+
Expr::CubicBezier(Box::new(CubicBezierInterpolation {
933933
curve_params: [*x1, *y1, *x2, *y2],
934934
input: input.to_galileo_expr()?,
935935
control_points: stops

galileo/examples/vector_tiles_expressions.rs

Lines changed: 34 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
use std::sync::Arc;
55

66
use egui::FontDefinitions;
7-
use galileo::expr::{
8-
ControlPoint, CubicBezierInterpolation, ExponentialInterpolation, Expr, LinearInterpolation,
9-
};
7+
use galileo::MapBuilder;
108
use galileo::layer::VectorTileLayer;
119
use galileo::layer::data_provider::remove_parameters_modifier;
1210
use galileo::layer::vector_tile_layer::VectorTileLayerBuilder;
@@ -16,9 +14,9 @@ use galileo::layer::vector_tile_layer::style::{
1614
use galileo::render::text::RustybuzzRasterizer;
1715
use galileo::render::text::text_service::TextService;
1816
use galileo::tile_schema::{TileIndex, TileSchema, TileSchemaBuilder};
19-
use galileo::{Color, MapBuilder};
2017
use galileo_egui::{EguiMap, EguiMapState};
2118
use parking_lot::RwLock;
19+
use serde_json::json;
2220

2321
#[cfg(not(target_arch = "wasm32"))]
2422
fn main() {
@@ -128,24 +126,17 @@ fn linear_interpolation_style() -> StyleRule {
128126
min_resolution: None,
129127
filter: None,
130128
symbol: VectorTileSymbol::Polygon(VectorTilePolygonSymbol {
131-
fill_color: Expr::InterpolateLinear(Box::new(LinearInterpolation {
132-
input: Expr::Zoom,
133-
control_points: vec![
134-
ControlPoint {
135-
input: 2.0.into(),
136-
output: Color::try_from_hex("#81C4ec").unwrap().into(),
137-
},
138-
ControlPoint {
139-
input: 5.0.into(),
140-
output: Color::try_from_hex("#29546d").unwrap().into(),
141-
},
142-
ControlPoint {
143-
input: 8.0.into(),
144-
output: Color::try_from_hex("#3d835c").unwrap().into(),
145-
},
146-
],
129+
fill_color: serde_json::from_value(json!({
130+
"Linear": {
131+
"input": "Zoom",
132+
"control_points": [
133+
{"input": 2, "output": "#81c4ecff"},
134+
{"input": 5, "output": "#29546dff"},
135+
{"input": 8, "output": "#3d835cff"}
136+
]
137+
}
147138
}))
148-
.into(),
139+
.unwrap(),
149140
}),
150141
}
151142
}
@@ -158,25 +149,18 @@ fn exponential_interpolation_style() -> StyleRule {
158149
min_resolution: None,
159150
filter: None,
160151
symbol: VectorTileSymbol::Polygon(VectorTilePolygonSymbol {
161-
fill_color: Expr::InterpolateExp(Box::new(ExponentialInterpolation {
162-
base: 2.0,
163-
input: Expr::Zoom,
164-
control_points: vec![
165-
ControlPoint {
166-
input: 2.0.into(),
167-
output: Color::try_from_hex("#81C4ec").unwrap().into(),
168-
},
169-
ControlPoint {
170-
input: 5.0.into(),
171-
output: Color::try_from_hex("#29546d").unwrap().into(),
172-
},
173-
ControlPoint {
174-
input: 8.0.into(),
175-
output: Color::try_from_hex("#3d835c").unwrap().into(),
176-
},
177-
],
152+
fill_color: serde_json::from_value(json!({
153+
"Exponential": {
154+
"base": 2,
155+
"input": "Zoom",
156+
"control_points": [
157+
{"input": 2, "output": "#81c4ecff"},
158+
{"input": 5, "output": "#29546dff"},
159+
{"input": 8, "output": "#3d835cff"}
160+
]
161+
}
178162
}))
179-
.into(),
163+
.unwrap(),
180164
}),
181165
}
182166
}
@@ -189,25 +173,18 @@ fn cubic_interpolation_style() -> StyleRule {
189173
min_resolution: None,
190174
filter: None,
191175
symbol: VectorTileSymbol::Polygon(VectorTilePolygonSymbol {
192-
fill_color: Expr::InterpolateCubicBezier(Box::new(CubicBezierInterpolation {
193-
curve_params: [0.25, 0.0, 0.75, 1.0],
194-
input: Expr::Zoom,
195-
control_points: vec![
196-
ControlPoint {
197-
input: 2.0.into(),
198-
output: Color::try_from_hex("#81C4ec").unwrap().into(),
199-
},
200-
ControlPoint {
201-
input: 5.0.into(),
202-
output: Color::try_from_hex("#29546d").unwrap().into(),
203-
},
204-
ControlPoint {
205-
input: 8.0.into(),
206-
output: Color::try_from_hex("#3d835c").unwrap().into(),
207-
},
208-
],
176+
fill_color: serde_json::from_value(json!({
177+
"CubicBezier": {
178+
"curve_params": [0.25, 0.0, 0.75, 1.0],
179+
"input": "Zoom",
180+
"control_points": [
181+
{"input": 2, "output": "#81c4ecff"},
182+
{"input": 5, "output": "#29546dff"},
183+
{"input": 8, "output": "#3d835cff"}
184+
]
185+
}
209186
}))
210-
.into(),
187+
.unwrap(),
211188
}),
212189
}
213190
}

galileo/src/color.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ use serde::{Deserialize, Serialize};
22

33
/// Color representation.
44
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
5-
#[serde(from = "String", into = "String")]
5+
#[serde(try_from = "String", into = "String")]
66
pub struct Color {
77
r: u8,
88
g: u8,
99
b: u8,
1010
a: u8,
1111
}
1212

13-
impl From<String> for Color {
14-
fn from(value: String) -> Self {
15-
Self::try_from_hex(&value).unwrap_or(Color::rgba(0, 0, 0, 255))
13+
impl TryFrom<String> for Color {
14+
type Error = String;
15+
16+
fn try_from(value: String) -> Result<Self, Self::Error> {
17+
Self::try_from_hex(value.as_ref())
18+
.ok_or_else(|| format!("Invalid hex color value: {value}"))
1619
}
1720
}
1821

galileo/src/expr/mod.rs

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ pub mod parser;
2121
/// An expression that can be evaluated against a feature and a view to produce an [`ExprValue`].
2222
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2323
pub enum Expr {
24-
Literal(ExprValue<String>),
25-
2624
All(Vec<Expr>),
2725
Any(Vec<Expr>),
2826
Not(Box<Expr>),
@@ -43,13 +41,16 @@ pub enum Expr {
4341
GeomType,
4442
Zoom,
4543

46-
InterpolateLinear(Box<LinearInterpolation>),
47-
InterpolateExp(Box<ExponentialInterpolation>),
48-
InterpolateCubicBezier(Box<CubicBezierInterpolation>),
44+
Linear(Box<LinearInterpolation>),
45+
Exponential(Box<ExponentialInterpolation>),
46+
CubicBezier(Box<CubicBezierInterpolation>),
4947

5048
Match(Box<MatchExpr>),
5149

5250
WithOpacity(WithOpacityExpr),
51+
52+
#[serde(untagged)]
53+
Literal(ExprValue<String>),
5354
}
5455

5556
#[derive(Debug, Clone, PartialEq, Serialize)]
@@ -208,9 +209,9 @@ impl Expr {
208209
.z_index
209210
.map(|z_level| (z_level as f64).into())
210211
.unwrap_or(ExprValue::Null),
211-
Expr::InterpolateLinear(ip) => ip.eval(f, v),
212-
Expr::InterpolateExp(ip) => ip.eval(f, v),
213-
Expr::InterpolateCubicBezier(ip) => ip.eval(f, v),
212+
Expr::Linear(ip) => ip.eval(f, v),
213+
Expr::Exponential(ip) => ip.eval(f, v),
214+
Expr::CubicBezier(ip) => ip.eval(f, v),
214215
Expr::Match(m) => m.eval(f, v),
215216
Expr::WithOpacity(wo) => wo.eval(f, v),
216217
}
@@ -245,6 +246,8 @@ impl WithOpacityExpr {
245246

246247
#[cfg(test)]
247248
mod tests {
249+
use insta::assert_snapshot;
250+
248251
use super::*;
249252

250253
#[test]
@@ -279,4 +282,45 @@ mod tests {
279282
let json = r#""@@@ not valid @@@""#;
280283
assert!(serde_json::from_str::<NumExpr>(json).is_err());
281284
}
285+
286+
#[test]
287+
fn serialization_of_complex_expressions() {
288+
let expr = Expr::Exponential(Box::new(ExponentialInterpolation {
289+
base: 2.0,
290+
input: Expr::Zoom,
291+
control_points: vec![
292+
ControlPoint {
293+
input: 10.0.into(),
294+
output: Color::BLACK.into(),
295+
},
296+
ControlPoint {
297+
input: 20.0.into(),
298+
output: Color::RED.into(),
299+
},
300+
],
301+
}));
302+
303+
let json = serde_json::to_string_pretty(&expr).unwrap();
304+
let deser: Expr = serde_json::from_str(&json).unwrap();
305+
assert_eq!(deser, expr);
306+
307+
assert_snapshot!(json, @r##"
308+
{
309+
"Exponential": {
310+
"base": 2.0,
311+
"input": "Zoom",
312+
"control_points": [
313+
{
314+
"input": 10.0,
315+
"output": "#000000FF"
316+
},
317+
{
318+
"input": 20.0,
319+
"output": "#FF0000FF"
320+
}
321+
]
322+
}
323+
}
324+
"##);
325+
}
282326
}

galileo/src/expr/parser.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl<'src> FuncContext<'src> {
126126
if index >= self.args.len() {
127127
return Err(Rich::custom(
128128
self.func_name_span,
129-
format!("function {} missing argumnt {}", self.func_name, index + 1),
129+
format!("function {} missing argument {}", self.func_name, index + 1),
130130
));
131131
}
132132

@@ -177,7 +177,7 @@ impl_get_arg!(
177177
FuncArgument::Expression(Expr::Literal(ExprValue::String(v))) => v,
178178
"String"
179179
);
180-
impl_get_arg!(Vec<Expr>, FuncArgument::Array(v) => v, "[Array]");
180+
impl_get_arg!(Vec<Expr>, FuncArgument::Array(v) => v, "Expression Array");
181181

182182
#[derive(Debug)]
183183
enum FuncArgument {
@@ -577,7 +577,7 @@ mod tests {
577577

578578
#[test]
579579
fn parser_error_invalid_argument_type_array() {
580-
ass!(s("any(true)"), @r#""expected `[Array]` argument at position `1` of function `any`, but got `true` at 4..8""#);
580+
ass!(s("any(true)"), @r#""expected `Expression Array` argument at position `1` of function `any`, but got `true` at 4..8""#);
581581
}
582582

583583
#[test]

galileo/src/expr/value.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub enum ExprGeometryType {
1313
}
1414

1515
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
16+
#[serde(untagged)]
1617
pub enum ExprValue<S> {
1718
Null,
1819
Boolean(bool),

0 commit comments

Comments
 (0)