Skip to content

Commit 168b4af

Browse files
committed
feat: Support for line layers in MaplibreLayer
Convert maplibre line layers to Galileo vt style rules. At this point only stroke color and width properties are supported. Also, refactored a little how values are parsed.
1 parent a0c206d commit 168b4af

8 files changed

Lines changed: 271 additions & 47 deletions

File tree

galileo-maplibre/src/layer/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,25 @@ pub mod vector_tile;
1515

1616
pub(crate) const UNSUPPORTED: &str = "[maplibre:unsupported]";
1717

18+
/// Logs a debug message when an unsupported `Option`al style property is set (i.e. `Some`).
19+
///
20+
/// Usage: `log_unsupported!(layer.paint.some_field);`
21+
///
22+
/// The field path is used as the property name in the message, so no extra string is needed.
23+
macro_rules! log_unsupported {
24+
($field:expr) => {
25+
if $field.is_some() {
26+
log::debug!(
27+
"{} '{}' is not supported yet; ignored",
28+
$crate::layer::UNSUPPORTED,
29+
stringify!($field),
30+
);
31+
}
32+
};
33+
}
34+
35+
pub(crate) use log_unsupported;
36+
1837
/// A Galileo [`Layer`] that renders a Maplibre style definition.
1938
///
2039
/// Internally owns one or more Galileo layers derived from the style's sources and renders them

galileo-maplibre/src/layer/vector_tile.rs

Lines changed: 87 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ use galileo::layer::vector_tile_layer::expressions::{
99
StepValue, StyleValue,
1010
};
1111
use galileo::layer::vector_tile_layer::style::{
12-
StyleRule, VectorTilePolygonSymbol, VectorTileStyle, VectorTileSymbol,
12+
StyleRule, VectorTileLineSymbol, VectorTilePolygonSymbol, VectorTileStyle, VectorTileSymbol,
1313
};
1414
use galileo::tile_schema::{TileSchema, TileSchemaBuilder, VerticalDirection};
1515
use serde::Deserialize;
1616

17-
use crate::layer::UNSUPPORTED;
17+
use crate::layer::{UNSUPPORTED, log_unsupported};
1818
use crate::style::color::MlColor;
19-
use crate::style::layer::{FillLayer, Layer as MaplibreStyleLayer};
19+
use crate::style::layer::{FillLayer, Layer as MaplibreStyleLayer, LineLayer};
2020
use crate::style::source::{TileScheme, VectorSource};
2121
use crate::style::value::{FunctionStop, MlStyleValue};
2222

@@ -92,22 +92,19 @@ fn get_background(layers: &[&MaplibreStyleLayer]) -> StyleValue<Color> {
9292
}
9393
};
9494

95-
let Some(color) = &layer.paint.background_color else {
96-
return DEFAULT_TILE_BACKGROUND;
97-
};
98-
99-
get_color_value(color, layer.paint.background_opacity.as_ref())
100-
.unwrap_or(DEFAULT_TILE_BACKGROUND)
95+
get_color_value(
96+
&layer.paint.background_color,
97+
&layer.paint.background_opacity,
98+
)
99+
.unwrap_or(DEFAULT_TILE_BACKGROUND)
101100
}
102101

103102
fn get_color_value(
104103
color: &MlStyleValue<MlColor>,
105-
opacity: Option<&MlStyleValue<f64>>,
104+
opacity: &MlStyleValue<f64>,
106105
) -> Option<StyleValue<Color>> {
107106
let galileo_color = get_galileo_value(color)?;
108-
let galileo_opacity = opacity
109-
.and_then(get_galileo_value)
110-
.unwrap_or(StyleValue::Simple(1.0));
107+
let galileo_opacity = get_galileo_value(opacity).unwrap_or(StyleValue::Simple(1.0));
111108

112109
match (galileo_color, galileo_opacity) {
113110
(StyleValue::Simple(c), StyleValue::Simple(o)) => Some((*c).with_alpha_float(o).into()),
@@ -194,11 +191,11 @@ fn build_rules(layers: &[&MaplibreStyleLayer], tile_schema: &TileSchema) -> Vec<
194191
MaplibreStyleLayer::Fill(fill) => {
195192
if let Some(rule) = fill_rule(fill, tile_schema) {
196193
rules.push(rule);
197-
log::debug!(
198-
"Maplibre layer '{}' of type '{}' is added as a VT style rule",
199-
layer.id(),
200-
layer.type_name()
201-
);
194+
}
195+
}
196+
MaplibreStyleLayer::Line(line) => {
197+
if let Some(rule) = line_rule(line, tile_schema) {
198+
rules.push(rule);
202199
}
203200
}
204201
other => {
@@ -208,8 +205,16 @@ fn build_rules(layers: &[&MaplibreStyleLayer], tile_schema: &TileSchema) -> Vec<
208205
other.type_name(),
209206
other.id(),
210207
);
208+
209+
continue;
211210
}
212211
}
212+
213+
log::trace!(
214+
"Maplibre layer '{}' of type '{}' is added as a VT style rule",
215+
layer.id(),
216+
layer.type_name()
217+
);
213218
}
214219
rules
215220
}
@@ -227,9 +232,16 @@ fn fill_rule(fill: &FillLayer, tile_schema: &TileSchema) -> Option<StyleRule> {
227232
}
228233
};
229234

230-
let fill_color = fill.paint.fill_color.as_ref()?;
235+
let fill_color = &fill.paint.fill_color;
231236
let fill_opacity = &fill.paint.fill_opacity;
232-
let color = get_color_value(fill_color, fill_opacity.as_ref())?;
237+
let color = get_color_value(fill_color, fill_opacity)?;
238+
239+
log_unsupported!(fill.paint.fill_antialias);
240+
log_unsupported!(fill.paint.fill_outline_color);
241+
log_unsupported!(fill.paint.fill_pattern);
242+
log_unsupported!(fill.paint.fill_translate);
243+
log_unsupported!(fill.paint.fill_translate_anchor);
244+
log_unsupported!(fill.paint.fill_emissive_strength);
233245

234246
let min_resolution = fill
235247
.maxzoom
@@ -247,6 +259,61 @@ fn fill_rule(fill: &FillLayer, tile_schema: &TileSchema) -> Option<StyleRule> {
247259
})
248260
}
249261

262+
/// Converts a [`LineLayer`] to a [`StyleRule`], or logs and returns `None` if unsupported.
263+
fn line_rule(line: &LineLayer, tile_schema: &TileSchema) -> Option<StyleRule> {
264+
if line.paint.line_dasharray.is_some() {
265+
log::debug!(
266+
"{UNSUPPORTED} Line dasharray is not supported yet; skipping layer {}",
267+
line.id
268+
);
269+
return None;
270+
}
271+
272+
log_unsupported!(line.paint.line_blur);
273+
log_unsupported!(line.paint.line_gap_width);
274+
log_unsupported!(line.paint.line_gradient);
275+
log_unsupported!(line.paint.line_pattern);
276+
log_unsupported!(line.paint.line_translate);
277+
log_unsupported!(line.paint.line_translate_anchor);
278+
log_unsupported!(line.paint.line_emissive_strength);
279+
log_unsupported!(line.paint.line_offset);
280+
281+
let source_layer = match &line.source_layer {
282+
Some(l) => l.clone(),
283+
None => {
284+
log::debug!(
285+
"{UNSUPPORTED} Line layer '{}' has no source-layer; skipping.",
286+
line.id
287+
);
288+
return None;
289+
}
290+
};
291+
292+
let stroke_color = &line.paint.line_color;
293+
let stroke_opacity = &line.paint.line_opacity;
294+
let color = get_color_value(stroke_color, stroke_opacity).unwrap_or(Color::TRANSPARENT.into());
295+
let stroke_width = &line.paint.line_width;
296+
let width = get_galileo_value(stroke_width).unwrap_or(1.0.into());
297+
298+
let min_resolution = line
299+
.maxzoom
300+
.and_then(|lod| tile_schema.lod_resolution(lod.round() as u32));
301+
let max_resolution = line
302+
.minzoom
303+
.and_then(|lod| tile_schema.lod_resolution(lod.round() as u32));
304+
305+
Some(StyleRule {
306+
layer_name: Some(source_layer),
307+
symbol: VectorTileSymbol::Line(VectorTileLineSymbol {
308+
width,
309+
stroke_color: color,
310+
}),
311+
min_resolution,
312+
max_resolution,
313+
..Default::default()
314+
})
315+
}
316+
250317
/// Converts WGS84 bounding box `[west, south, east, north]` (degrees) to a Web Mercator [`Rect`]
251318
/// in projected meters, suitable for use with [`TileSchemaBuilder::tile_bounds`].
252319
fn wgs84_bounds_to_mercator(bounds: [f64; 4]) -> Option<Rect> {

galileo-maplibre/src/style/color.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use galileo::Color;
66
use serde::{Deserialize, Deserializer};
77

88
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
9-
pub struct MlColor(Color);
9+
pub struct MlColor(pub(crate) Color);
1010

1111
impl<'de> Deserialize<'de> for MlColor {
1212
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {

galileo-maplibre/src/style/layer/background.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,31 @@ use serde_json::Value;
55

66
use super::common::CommonLayout;
77
use crate::style::color::MlColor;
8-
use crate::style::deserialize_opt_f64;
98
use crate::style::value::MlStyleValue;
9+
use crate::style::{
10+
default_one, default_transparent, deser_default_one, deser_default_transparent,
11+
deserialize_opt_f64,
12+
};
1013

1114
/// Paint properties for a background layer.
12-
#[derive(Debug, Clone, PartialEq, Deserialize, Default)]
15+
#[derive(Debug, Clone, PartialEq, Deserialize)]
1316
pub struct BackgroundPaint {
1417
/// The colour with which the background will be drawn.
15-
#[serde(rename = "background-color")]
16-
pub background_color: Option<MlStyleValue<MlColor>>,
18+
#[serde(
19+
rename = "background-color",
20+
default = "default_transparent",
21+
deserialize_with = "deser_default_transparent"
22+
)]
23+
pub background_color: MlStyleValue<MlColor>,
24+
1725
/// The opacity at which the background will be drawn.
18-
#[serde(rename = "background-opacity")]
19-
pub background_opacity: Option<MlStyleValue<f64>>,
26+
#[serde(
27+
rename = "background-opacity",
28+
default = "default_one",
29+
deserialize_with = "deser_default_one"
30+
)]
31+
pub background_opacity: MlStyleValue<f64>,
32+
2033
/// Name of image in sprite to use for drawing an image background.
2134
#[serde(rename = "background-pattern")]
2235
pub background_pattern: Option<Value>,
@@ -25,6 +38,17 @@ pub struct BackgroundPaint {
2538
pub background_emissive_strength: Option<Value>,
2639
}
2740

41+
impl Default for BackgroundPaint {
42+
fn default() -> Self {
43+
Self {
44+
background_color: default_transparent(),
45+
background_opacity: default_one(),
46+
background_pattern: Default::default(),
47+
background_emissive_strength: Default::default(),
48+
}
49+
}
50+
}
51+
2852
/// Layout properties for a background layer (visibility only).
2953
pub type BackgroundLayout = CommonLayout;
3054

galileo-maplibre/src/style/layer/fill.rs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ use serde_json::Value;
55

66
use super::common::CommonLayout;
77
use crate::style::color::MlColor;
8-
use crate::style::deserialize_opt_f64;
98
use crate::style::value::MlStyleValue;
9+
use crate::style::{
10+
default_one, default_transparent, deser_default_one, deser_default_transparent,
11+
deserialize_opt_f64,
12+
};
1013

1114
/// Paint properties for a `fill` layer.
12-
#[derive(Debug, Clone, PartialEq, Deserialize, Default)]
15+
#[derive(Debug, Clone, PartialEq, Deserialize)]
1316
pub struct FillPaint {
1417
/// Whether or not the fill should be antialiased. Supports expressions.
1518
#[serde(rename = "fill-antialias", skip_serializing_if = "Option::is_none")]
@@ -18,22 +21,24 @@ pub struct FillPaint {
1821
/// Fill colour.
1922
#[serde(
2023
rename = "fill-color",
21-
default,
24+
default = "default_transparent",
25+
deserialize_with = "deser_default_transparent",
2226
skip_serializing_if = "Option::is_none"
2327
)]
24-
pub fill_color: Option<MlStyleValue<MlColor>>,
28+
pub fill_color: MlStyleValue<MlColor>,
2529

2630
/// Outline colour.
31+
#[serde(rename = "fill-outline-color", skip_serializing_if = "Option::is_none")]
32+
pub fill_outline_color: Option<Value>,
33+
34+
/// Opacity of the entire fill layer. Supports expressions.
2735
#[serde(
28-
rename = "fill-outline-color",
29-
default,
36+
rename = "fill-opacity",
37+
default = "default_one",
38+
deserialize_with = "deser_default_one",
3039
skip_serializing_if = "Option::is_none"
3140
)]
32-
pub fill_outline_color: Option<MlStyleValue<MlColor>>,
33-
34-
/// Opacity of the entire fill layer. Supports expressions.
35-
#[serde(rename = "fill-opacity", skip_serializing_if = "Option::is_none")]
36-
pub fill_opacity: Option<MlStyleValue<f64>>,
41+
pub fill_opacity: MlStyleValue<f64>,
3742

3843
/// Name of image in sprite to use for drawing the fill pattern.
3944
#[serde(rename = "fill-pattern", skip_serializing_if = "Option::is_none")]
@@ -58,6 +63,21 @@ pub struct FillPaint {
5863
pub fill_emissive_strength: Option<Value>,
5964
}
6065

66+
impl Default for FillPaint {
67+
fn default() -> Self {
68+
Self {
69+
fill_antialias: Default::default(),
70+
fill_color: default_transparent(),
71+
fill_outline_color: Default::default(),
72+
fill_opacity: default_one(),
73+
fill_pattern: Default::default(),
74+
fill_translate: Default::default(),
75+
fill_translate_anchor: Default::default(),
76+
fill_emissive_strength: Default::default(),
77+
}
78+
}
79+
}
80+
6181
/// Layout properties for a `fill` layer (visibility only).
6282
pub type FillLayout = CommonLayout;
6383

0 commit comments

Comments
 (0)