maplibre/vector/
request_system.rs1use std::{borrow::Cow, collections::HashSet, marker::PhantomData, rc::Rc};
4
5use crate::{
6 context::MapContext,
7 environment::{Environment, OffscreenKernel},
8 io::{
9 apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, ProcedureError},
10 source_type::{SourceType, TessellateSource},
11 },
12 kernel::Kernel,
13 render::{tile_view_pattern::DEFAULT_TILE_SIZE, view_state::ViewStatePadding},
14 sdf::SymbolLayersDataComponent,
15 style::layer::StyleLayer,
16 tcs::system::{System, SystemResult},
17 vector::{
18 process_vector::{process_vector_tile, ProcessVectorContext, VectorTileRequest},
19 transferables::{LayerMissing, VectorTransferables},
20 VectorLayerBucketComponent,
21 },
22};
23
24pub struct RequestSystem<E: Environment, T> {
25 kernel: Rc<Kernel<E>>,
26 phantom_t: PhantomData<T>,
27}
28
29impl<E: Environment, T> RequestSystem<E, T> {
30 pub fn new(kernel: &Rc<Kernel<E>>) -> Self {
31 Self {
32 kernel: kernel.clone(),
33 phantom_t: Default::default(),
34 }
35 }
36}
37
38impl<E: Environment, T: VectorTransferables> System for RequestSystem<E, T> {
39 fn name(&self) -> Cow<'static, str> {
40 "vector_request".into()
41 }
42
43 fn run(
44 &mut self,
45 MapContext {
46 style,
47 view_state,
48 world,
49 ..
50 }: &mut MapContext,
51 ) -> SystemResult {
52 let _tiles = &mut world.tiles;
53 let view_region = view_state.create_view_region(
54 view_state.zoom().zoom_level(DEFAULT_TILE_SIZE),
55 ViewStatePadding::Loose,
56 );
57
58 if view_state.did_camera_change() || view_state.did_zoom_change() {
59 if let Some(view_region) = &view_region {
60 for coords in view_region.iter() {
63 if coords.build_quad_key().is_none() {
64 continue;
65 }
66
67 if world
69 .tiles
70 .query::<&VectorLayerBucketComponent>(coords)
71 .is_some()
72 {
73 continue;
74 }
75
76 world
77 .tiles
78 .spawn_mut(coords)
79 .unwrap()
80 .insert(VectorLayerBucketComponent::default())
81 .insert(SymbolLayersDataComponent::default());
82
83 tracing::event!(tracing::Level::ERROR, %coords, "tile request started: {coords}");
84 log::info!("tile request started: {coords}");
85
86 self.kernel
87 .apc()
88 .call(
89 Input::TileRequest {
90 coords,
91 style: style.clone(), },
93 fetch_vector_apc::<
94 E::OffscreenKernelEnvironment,
95 T,
96 <E::AsyncProcedureCall as AsyncProcedureCall<
97 E::OffscreenKernelEnvironment,
98 >>::Context,
99 >,
100 )
101 .unwrap(); }
103 }
104 }
105 Ok(())
106 }
107}
108
109pub fn fetch_vector_apc<K: OffscreenKernel, T: VectorTransferables, C: Context + Clone + Send>(
110 input: Input,
111 context: C,
112 kernel: K,
113) -> AsyncProcedureFuture {
114 Box::pin(async move {
115 let Input::TileRequest { coords, style } = input else {
116 return Err(ProcedureError::IncompatibleInput);
117 };
118
119 let requested_layers: HashSet<StyleLayer> = style.layers.iter().cloned().collect();
120
121 let client = kernel.source_client();
122
123 if !style.layers.is_empty() {
124 let context = context.clone();
125 let source = SourceType::Tessellate(TessellateSource::default());
126 match client.fetch(&coords, &source).await {
127 Ok(data) => {
128 let data = data.into_boxed_slice();
129
130 let mut pipeline_context = ProcessVectorContext::<T, C>::new(context);
131 process_vector_tile(
132 &data,
133 VectorTileRequest {
134 coords,
135 layers: requested_layers,
136 },
137 &mut pipeline_context,
138 )
139 .map_err(|e| ProcedureError::Execution(Box::new(e)))?;
140 }
141 Err(e) => {
142 log::error!("{e:?}");
143 for to_load in &requested_layers {
144 context
145 .send_back(<T as VectorTransferables>::LayerMissing::build_from(
146 coords,
147 to_load.id.clone(),
148 ))
149 .map_err(ProcedureError::Send)?;
150 }
151 }
152 }
153 }
154
155 Ok(())
156 })
157}