maplibre/raster/
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::{RasterSource, SourceType},
11 },
12 kernel::Kernel,
13 raster::{
14 process_raster::{process_raster_tile, ProcessRasterContext, RasterTileRequest},
15 transferables::{LayerRasterMissing, RasterTransferables},
16 RasterLayersDataComponent,
17 },
18 render::{tile_view_pattern::DEFAULT_TILE_SIZE, view_state::ViewStatePadding},
19 style::layer::LayerPaint,
20 tcs::system::{System, SystemResult},
21};
22
23pub struct RequestSystem<E: Environment, T: RasterTransferables> {
24 kernel: Rc<Kernel<E>>,
25 phantom_t: PhantomData<T>,
26}
27
28impl<E: Environment, T: RasterTransferables> RequestSystem<E, T> {
29 pub fn new(kernel: &Rc<Kernel<E>>) -> Self {
30 Self {
31 kernel: kernel.clone(),
32 phantom_t: Default::default(),
33 }
34 }
35}
36
37impl<E: Environment, T: RasterTransferables> System for RequestSystem<E, T> {
38 fn name(&self) -> Cow<'static, str> {
39 "raster_request".into()
40 }
41
42 fn run(
43 &mut self,
44 MapContext {
45 style,
46 view_state,
47 world,
48 ..
49 }: &mut MapContext,
50 ) -> SystemResult {
51 let view_region = view_state.create_view_region(
52 view_state.zoom().zoom_level(DEFAULT_TILE_SIZE),
53 ViewStatePadding::Loose,
54 );
55
56 if view_state.did_camera_change() || view_state.did_zoom_change() {
57 if let Some(view_region) = &view_region {
58 for coords in view_region.iter() {
61 if coords.build_quad_key().is_none() {
62 continue;
63 }
64
65 if world
67 .tiles
68 .query::<&RasterLayersDataComponent>(coords)
69 .is_some()
70 {
71 continue;
72 }
73
74 world
75 .tiles
76 .spawn_mut(coords)
77 .expect("unable to spawn a raster tile")
78 .insert(RasterLayersDataComponent::default());
79
80 tracing::event!(tracing::Level::ERROR, %coords, "tile request started: {coords}");
81 log::info!("tile request started: {coords}");
82
83 self.kernel
84 .apc()
85 .call(
86 Input::TileRequest {
87 coords,
88 style: style.clone(), },
90 fetch_raster_apc::<
91 E::OffscreenKernelEnvironment,
92 T,
93 <E::AsyncProcedureCall as AsyncProcedureCall<
94 E::OffscreenKernelEnvironment,
95 >>::Context,
96 >,
97 )
98 .expect("unable to call APC"); }
100 }
101 }
102
103 view_state.update_references();
104
105 Ok(())
106 }
107}
108pub fn fetch_raster_apc<K: OffscreenKernel, T: RasterTransferables, C: Context + Clone + Send>(
109 input: Input,
110 context: C,
111 kernel: K,
112) -> AsyncProcedureFuture {
113 Box::pin(async move {
114 let Input::TileRequest { coords, style } = input else {
115 return Err(ProcedureError::IncompatibleInput);
116 };
117
118 let raster_layers: HashSet<String> = style
119 .layers
120 .iter()
121 .filter_map(|layer| {
122 if matches!(layer.paint, Some(LayerPaint::Raster(_))) {
123 layer.source_layer.clone()
124 } else {
125 None
126 }
127 })
128 .collect();
129
130 let client = kernel.source_client();
131
132 if !raster_layers.is_empty() {
133 let context = context.clone();
134 let source = SourceType::Raster(RasterSource::default());
135
136 match client.fetch(&coords, &source).await {
137 Ok(data) => {
138 let data = data.into_boxed_slice();
139
140 let mut process_context = ProcessRasterContext::<T, C>::new(context);
141
142 process_raster_tile(&data, RasterTileRequest { coords }, &mut process_context)
143 .map_err(|e| ProcedureError::Execution(Box::new(e)))?;
144 }
145 Err(e) => {
146 log::error!("{e:?}");
147
148 context
149 .send_back(<T as RasterTransferables>::LayerRasterMissing::build_from(
150 coords,
151 ))
152 .map_err(ProcedureError::Send)?;
153 }
154 }
155 }
156
157 Ok(())
158 })
159}