maplibre/
map.rs

1use std::rc::Rc;
2
3use thiserror::Error;
4
5use crate::{
6    context::MapContext,
7    coords::{LatLon, WorldCoords, Zoom},
8    environment::Environment,
9    kernel::Kernel,
10    plugin::Plugin,
11    render::{
12        builder::{
13            InitializationResult, InitializedRenderer, RendererBuilder, UninitializedRenderer,
14        },
15        error::RenderError,
16        graph::RenderGraphError,
17        view_state::ViewState,
18    },
19    schedule::{Schedule, Stage, StageError},
20    style::Style,
21    tcs::world::World,
22    window::{HeadedMapWindow, MapWindow, MapWindowConfig, WindowCreateError},
23};
24
25#[derive(Error, Debug)]
26pub enum MapError {
27    /// No need to set renderer again
28    #[error("renderer was already set for this map")]
29    RendererAlreadySet,
30    #[error("renderer is not fully initialized")]
31    RendererNotReady,
32    #[error("initializing render graph failed")]
33    RenderGraphInit(RenderGraphError),
34    #[error("initializing device failed")]
35    DeviceInit(RenderError),
36    #[error("creating window failed")]
37    Window(#[from] WindowCreateError),
38    #[error("executing stage must not error")]
39    StageError(#[from] StageError),
40}
41
42pub enum CurrentMapContext {
43    Ready(MapContext),
44    Pending {
45        style: Style,
46        renderer_builder: RendererBuilder,
47    },
48}
49
50pub struct Map<E: Environment> {
51    kernel: Rc<Kernel<E>>,
52    schedule: Schedule,
53    map_context: CurrentMapContext,
54    window: <E::MapWindowConfig as MapWindowConfig>::MapWindow,
55
56    plugins: Vec<Box<dyn Plugin<E>>>,
57}
58
59impl<E: Environment> Map<E>
60where
61    <<E as Environment>::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
62{
63    pub fn new(
64        style: Style,
65        kernel: Kernel<E>,
66        renderer_builder: RendererBuilder,
67        plugins: Vec<Box<dyn Plugin<E>>>,
68    ) -> Result<Self, MapError> {
69        let schedule = Schedule::default();
70
71        let window = kernel.map_window_config().create()?;
72
73        let kernel = Rc::new(kernel);
74
75        let map = Self {
76            kernel,
77            schedule,
78            map_context: CurrentMapContext::Pending {
79                style,
80                renderer_builder,
81            },
82            window,
83            plugins,
84        };
85        Ok(map)
86    }
87
88    pub async fn initialize_renderer(&mut self) -> Result<(), MapError> {
89        match &mut self.map_context {
90            CurrentMapContext::Ready(_) => Err(MapError::RendererAlreadySet),
91            CurrentMapContext::Pending {
92                style,
93                renderer_builder,
94            } => {
95                let init_result = renderer_builder
96                    .clone() // Cloning because we want to be able to build multiple times maybe
97                    .build()
98                    .initialize_renderer::<E::MapWindowConfig>(&self.window)
99                    .await
100                    .map_err(MapError::DeviceInit)?;
101
102                let window_size = self.window.size();
103
104                let center = style.center.unwrap_or_default();
105                let initial_zoom = style.zoom.map(Zoom::new).unwrap_or_default();
106                let view_state = ViewState::new(
107                    window_size,
108                    WorldCoords::from_lat_lon(LatLon::new(center[0], center[1]), initial_zoom),
109                    initial_zoom,
110                    cgmath::Deg::<f64>(style.pitch.unwrap_or_default()),
111                    cgmath::Rad(0.6435011087932844),
112                );
113
114                let mut world = World::default();
115
116                match init_result {
117                    InitializationResult::Initialized(InitializedRenderer {
118                        mut renderer, ..
119                    }) => {
120                        for plugin in &self.plugins {
121                            plugin.build(
122                                &mut self.schedule,
123                                self.kernel.clone(),
124                                &mut world,
125                                &mut renderer.render_graph,
126                            );
127                        }
128
129                        self.map_context = CurrentMapContext::Ready(MapContext {
130                            world,
131                            view_state,
132                            style: std::mem::take(style),
133                            renderer,
134                        });
135                    }
136                    InitializationResult::Uninitialized(UninitializedRenderer { .. }) => {}
137                    _ => panic!("Rendering context gone"),
138                };
139                Ok(())
140            }
141        }
142    }
143
144    pub fn window_mut(&mut self) -> &mut <E::MapWindowConfig as MapWindowConfig>::MapWindow {
145        &mut self.window
146    }
147    pub fn window(&self) -> &<E::MapWindowConfig as MapWindowConfig>::MapWindow {
148        &self.window
149    }
150
151    pub fn is_initialized(&self) -> bool {
152        match &self.map_context {
153            CurrentMapContext::Ready(_) => true,
154            CurrentMapContext::Pending { .. } => false,
155        }
156    }
157
158    /// Resets the complete state of this map - a new renderer and schedule needs to be created.
159    /// The complete state of the app is reset.
160    pub fn reset(&mut self) {
161        self.schedule.clear();
162        match &self.map_context {
163            CurrentMapContext::Ready(c) => {
164                self.map_context = CurrentMapContext::Pending {
165                    style: c.style.clone(),
166                    renderer_builder: RendererBuilder::new()
167                        .with_renderer_settings(c.renderer.settings.clone())
168                        .with_wgpu_settings(c.renderer.wgpu_settings.clone()),
169                }
170            }
171            CurrentMapContext::Pending { .. } => {}
172        }
173    }
174
175    #[tracing::instrument(name = "update_and_redraw", skip_all)]
176    pub fn run_schedule(&mut self) -> Result<(), MapError> {
177        match &mut self.map_context {
178            CurrentMapContext::Ready(map_context) => {
179                self.schedule.run(map_context)?;
180                Ok(())
181            }
182            CurrentMapContext::Pending { .. } => Err(MapError::RendererNotReady),
183        }
184    }
185
186    pub fn context(&self) -> Result<&MapContext, MapError> {
187        match &self.map_context {
188            CurrentMapContext::Ready(map_context) => Ok(map_context),
189            CurrentMapContext::Pending { .. } => Err(MapError::RendererNotReady),
190        }
191    }
192
193    pub fn context_mut(&mut self) -> Result<&mut MapContext, MapError> {
194        match &mut self.map_context {
195            CurrentMapContext::Ready(map_context) => Ok(map_context),
196            CurrentMapContext::Pending { .. } => Err(MapError::RendererNotReady),
197        }
198    }
199
200    pub fn kernel(&self) -> &Rc<Kernel<E>> {
201        &self.kernel
202    }
203}