1use std::{
2 borrow::Cow,
3 collections::{HashMap, HashSet},
4 marker::PhantomData,
5};
6
7use geozero::{
8 mvt::{tile, Message},
9 GeozeroDatasource,
10};
11use thiserror::Error;
12
13use crate::{
14 coords::WorldTileCoords,
15 io::{
16 apc::{Context, SendError},
17 geometry_index::{IndexProcessor, IndexedGeometry, TileIndex},
18 },
19 render::{
20 shaders::{ShaderSymbolVertex, ShaderSymbolVertexNew},
21 ShaderVertex,
22 },
23 sdf::{tessellation::TextTessellator, tessellation_new::TextTessellatorNew, Feature},
24 style::layer::{LayerPaint, StyleLayer},
25 vector::{
26 tessellation::{IndexDataType, OverAlignedVertexBuffer, ZeroTessellator},
27 transferables::{
28 LayerIndexed, LayerMissing, LayerTessellated, SymbolLayerTessellated, TileTessellated,
29 VectorTransferables,
30 },
31 },
32};
33
34#[derive(Error, Debug)]
35pub enum ProcessVectorError {
36 #[error("sending data back through context failed")]
38 SendError(SendError),
39 #[error("decoding failed")]
41 Decoding(Cow<'static, str>),
42}
43
44pub struct VectorTileRequest {
46 pub coords: WorldTileCoords,
47 pub layers: HashSet<StyleLayer>,
48}
49
50fn resolve_feature_properties(
53 layer: &tile::Layer,
54 feature: &tile::Feature,
55) -> HashMap<String, String> {
56 let mut props = HashMap::new();
57 for pair in feature.tags.chunks(2) {
58 let [key_idx, value_idx] = [pair[0], pair[1]];
59 let Some(key) = layer.keys.get(key_idx as usize) else {
60 continue;
61 };
62 let Some(value) = layer.values.get(value_idx as usize) else {
63 continue;
64 };
65 let val_str = if let Some(ref v) = value.string_value {
66 v.clone()
67 } else if let Some(v) = value.float_value {
68 v.to_string()
69 } else if let Some(v) = value.double_value {
70 v.to_string()
71 } else if let Some(v) = value.int_value {
72 v.to_string()
73 } else if let Some(v) = value.uint_value {
74 v.to_string()
75 } else if let Some(v) = value.sint_value {
76 v.to_string()
77 } else if let Some(v) = value.bool_value {
78 v.to_string()
79 } else {
80 continue;
81 };
82 props.insert(key.clone(), val_str);
83 }
84 props
85}
86
87fn evaluate_filter(filter: &serde_json::Value, props: &HashMap<String, String>) -> bool {
91 let Some(arr) = filter.as_array() else {
92 return true; };
94 let Some(op) = arr.first().and_then(|v| v.as_str()) else {
95 return true;
96 };
97 match op {
98 "all" => arr[1..].iter().all(|f| evaluate_filter(f, props)),
99 "any" => arr[1..].iter().any(|f| evaluate_filter(f, props)),
100 "none" => !arr[1..].iter().any(|f| evaluate_filter(f, props)),
101 "==" if arr.len() >= 3 => {
102 let key = arr[1].as_str().unwrap_or("");
103 let expected = arr[2].as_str().map(|s| s.to_string()).unwrap_or_else(|| {
104 arr[2].as_f64().map(|n| n.to_string()).unwrap_or_default()
106 });
107 props.get(key).map(|v| v == &expected).unwrap_or(false)
108 }
109 "!=" if arr.len() >= 3 => {
110 let key = arr[1].as_str().unwrap_or("");
111 let expected = arr[2]
112 .as_str()
113 .map(|s| s.to_string())
114 .unwrap_or_else(|| arr[2].as_f64().map(|n| n.to_string()).unwrap_or_default());
115 props.get(key).map(|v| v != &expected).unwrap_or(true)
116 }
117 "has" if arr.len() >= 2 => {
118 let key = arr[1].as_str().unwrap_or("");
119 props.contains_key(key)
120 }
121 "!has" if arr.len() >= 2 => {
122 let key = arr[1].as_str().unwrap_or("");
123 !props.contains_key(key)
124 }
125 "in" if arr.len() >= 3 => {
126 let key = arr[1].as_str().unwrap_or("");
127 let Some(val) = props.get(key) else {
128 return false;
129 };
130 arr[2..]
131 .iter()
132 .any(|v| v.as_str().map(|s| s == val).unwrap_or(false))
133 }
134 "!in" if arr.len() >= 3 => {
135 let key = arr[1].as_str().unwrap_or("");
136 let Some(val) = props.get(key) else {
137 return true;
138 };
139 !arr[2..]
140 .iter()
141 .any(|v| v.as_str().map(|s| s == val).unwrap_or(false))
142 }
143 _ => {
144 log::warn!("unsupported filter operator: {op}");
145 true
146 }
147 }
148}
149
150fn apply_filter_to_layer(layer: &mut tile::Layer, filter: &serde_json::Value) {
152 let keep: Vec<bool> = layer
155 .features
156 .iter()
157 .map(|feature| {
158 let props = resolve_feature_properties(layer, feature);
159 evaluate_filter(filter, &props)
160 })
161 .collect();
162 let mut idx = 0;
163 layer.features.retain(|_| {
164 let pass = keep[idx];
165 idx += 1;
166 pass
167 });
168}
169
170pub fn process_vector_tile<T: VectorTransferables, C: Context>(
171 data: &[u8],
172 tile_request: VectorTileRequest,
173 context: &mut ProcessVectorContext<T, C>,
174) -> Result<(), ProcessVectorError> {
175 let mut tile = geozero::mvt::Tile::decode(data)
176 .map_err(|e| ProcessVectorError::Decoding(e.to_string().into()))?;
177
178 let coords = &tile_request.coords;
180
181 for style_layer in &tile_request.layers {
182 let id = &style_layer.id;
183 if let (Some(paint), Some(source_layer)) = (&style_layer.paint, &style_layer.source_layer) {
184 if let Some(layer) = tile
185 .layers
186 .iter_mut()
187 .find(|layer| &layer.name == source_layer)
188 {
189 let mut filtered_layer = layer.clone();
192
193 if let Some(filter) = &style_layer.filter {
195 apply_filter_to_layer(&mut filtered_layer, filter);
196 }
197
198 let original_layer = filtered_layer.clone();
199 let layer = &mut filtered_layer;
200
201 match paint {
202 LayerPaint::Line(_) | LayerPaint::Fill(_) => {
203 let mut tessellator = ZeroTessellator::<IndexDataType>::default();
204 match paint {
205 LayerPaint::Fill(p) => {
206 tessellator.style_property = p.fill_color.clone()
207 }
208 LayerPaint::Line(p) => {
209 tessellator.style_property = p.line_color.clone();
210 tessellator.is_line_layer = true;
211 }
212 LayerPaint::Background(p) => {
213 tessellator.style_property = p.background_color.clone()
214 }
215 _ => {}
216 }
217
218 if let Err(e) = layer.process(&mut tessellator) {
219 context.layer_missing(coords, &source_layer)?;
220
221 tracing::error!("tesselation for layer source {source_layer} at {coords} failed {e:?}");
222 } else {
223 context.layer_tesselation_finished(
224 coords,
225 tessellator.buffer.into(),
226 tessellator.feature_indices,
227 tessellator.feature_colors,
228 original_layer,
229 id.clone(),
230 )?;
231 }
232 }
233 LayerPaint::Symbol(symbol_paint) => {
234 let mut tessellator = TextTessellator::<IndexDataType>::default();
235 let text_field = symbol_paint
236 .text_field
237 .clone()
238 .unwrap_or_else(|| "name".to_string());
239 let mut tessellator_new = TextTessellatorNew::new(text_field);
240
241 if let Err(e) = layer.process(&mut tessellator_new) {
242 context.layer_missing(coords, &source_layer)?;
243
244 tracing::error!("tesselation for layer source {source_layer} at {coords} failed {e:?}");
245 } else {
246 tessellator_new.finish();
247 context.symbol_layer_tesselation_finished(
248 coords,
249 tessellator.quad_buffer.into(),
250 tessellator_new.quad_buffer.into(),
251 tessellator_new.features,
252 original_layer,
253 id.clone(),
254 )?;
255 }
256 }
257 _ => {
258 log::warn!("unhandled style layer type in {id}");
259 }
260 }
261 } else {
262 log::warn!("layer source {source_layer} not found in vector tile");
263 }
264 } else {
265 log::error!("vector style layer {id} misses a required attribute");
266 }
267 }
268
269 let coords = &tile_request.coords;
271 let available_layers: HashSet<_> = tile
272 .layers
273 .iter()
274 .map(|layer| layer.name.clone())
275 .collect::<HashSet<_>>();
276
277 for layer in tile_request.layers {
278 if let Some(source_layer) = layer.source_layer {
279 if !available_layers.contains(&source_layer) {
280 context.layer_missing(coords, &source_layer)?;
281 tracing::info!(
282 "requested source layer {source_layer} at {coords} not found in tile"
283 );
284 }
285 }
286 }
287
288 let mut index = IndexProcessor::new();
290
291 for layer in &mut tile.layers {
292 layer.process(&mut index).unwrap();
293 }
294
295 context.layer_indexing_finished(&tile_request.coords, index.get_geometries())?;
296
297 tracing::info!("tile tessellated at {coords} finished");
299 context.tile_finished(coords)?;
300
301 Ok(())
302}
303
304pub struct ProcessVectorContext<T: VectorTransferables, C: Context> {
305 context: C,
306 phantom_t: PhantomData<T>,
307}
308
309impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
310 pub fn new(context: C) -> Self {
311 Self {
312 context,
313 phantom_t: Default::default(),
314 }
315 }
316}
317
318impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
319 pub fn take_context(self) -> C {
320 self.context
321 }
322
323 fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), ProcessVectorError> {
324 self.context
325 .send_back(T::TileTessellated::build_from(*coords))
326 .map_err(|e| ProcessVectorError::SendError(e))
327 }
328
329 fn layer_missing(
330 &mut self,
331 coords: &WorldTileCoords,
332 layer_name: &str,
333 ) -> Result<(), ProcessVectorError> {
334 self.context
335 .send_back(T::LayerMissing::build_from(*coords, layer_name.to_owned()))
336 .map_err(|e| ProcessVectorError::SendError(e))
337 }
338
339 fn layer_tesselation_finished(
340 &mut self,
341 coords: &WorldTileCoords,
342 buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
343 feature_indices: Vec<u32>,
344 feature_colors: Vec<[f32; 4]>,
345 layer_data: tile::Layer,
346 style_layer_id: String,
347 ) -> Result<(), ProcessVectorError> {
348 self.context
349 .send_back(T::LayerTessellated::build_from(
350 *coords,
351 buffer,
352 feature_indices,
353 feature_colors,
354 layer_data,
355 style_layer_id,
356 ))
357 .map_err(|e| ProcessVectorError::SendError(e))
358 }
359
360 fn symbol_layer_tesselation_finished(
361 &mut self,
362 coords: &WorldTileCoords,
363 buffer: OverAlignedVertexBuffer<ShaderSymbolVertex, IndexDataType>,
364 new_buffer: OverAlignedVertexBuffer<ShaderSymbolVertexNew, IndexDataType>,
365 features: Vec<Feature>,
366 layer_data: tile::Layer,
367 style_layer_id: String,
368 ) -> Result<(), ProcessVectorError> {
369 self.context
370 .send_back(T::SymbolLayerTessellated::build_from(
371 *coords,
372 buffer,
373 new_buffer,
374 features,
375 layer_data,
376 style_layer_id,
377 ))
378 .map_err(|e| ProcessVectorError::SendError(e))
379 }
380
381 fn layer_indexing_finished(
382 &mut self,
383 coords: &WorldTileCoords,
384 geometries: Vec<IndexedGeometry<f64>>,
385 ) -> Result<(), ProcessVectorError> {
386 self.context
387 .send_back(T::LayerIndexed::build_from(
388 *coords,
389 TileIndex::Linear { list: geometries },
390 ))
391 .map_err(|e| ProcessVectorError::SendError(e))
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::ProcessVectorContext;
398 use crate::{
399 coords::ZoomLevel,
400 io::apc::tests::DummyContext,
401 vector::{
402 process_vector::{process_vector_tile, VectorTileRequest},
403 DefaultVectorTransferables,
404 },
405 };
406
407 #[test] #[ignore]
409 fn test() {
410 let _output = process_vector_tile(
411 &[0],
412 VectorTileRequest {
413 coords: (0, 0, ZoomLevel::default()).into(),
414 layers: Default::default(),
415 },
416 &mut ProcessVectorContext::<DefaultVectorTransferables, _>::new(DummyContext),
417 );
418 }
419}