use std::{cell::RefCell, ops::Deref, rc::Rc};
use crate::{
context::MapContext,
coords::{WorldCoords, WorldTileCoords, Zoom, ZoomLevel, TILE_SIZE},
headless::environment::HeadlessEnvironment,
io::{
apc::{Context, IntoMessage, Message, SendError},
source_client::SourceFetchError,
source_type::{SourceType, TessellateSource},
},
kernel::Kernel,
map::MapError,
plugin::Plugin,
render::{eventually::Eventually, Renderer},
schedule::{Schedule, Stage},
style::Style,
tcs::world::World,
vector::{
process_vector_tile, AvailableVectorLayerData, DefaultVectorTransferables,
LayerTessellated, ProcessVectorContext, VectorBufferPool, VectorLayerData,
VectorLayersDataComponent, VectorTileRequest, VectorTransferables,
},
view_state::ViewState,
};
pub struct HeadlessMap {
kernel: Rc<Kernel<HeadlessEnvironment>>,
schedule: Schedule,
map_context: MapContext,
}
impl HeadlessMap {
pub fn new(
style: Style,
mut renderer: Renderer,
kernel: Kernel<HeadlessEnvironment>,
plugins: Vec<Box<dyn Plugin<HeadlessEnvironment>>>,
) -> Result<Self, MapError> {
let window_size = renderer.state().surface().size();
let view_state = ViewState::new(
window_size,
WorldCoords::from((TILE_SIZE / 2., TILE_SIZE / 2.)),
Zoom::default(),
cgmath::Deg(0.0),
cgmath::Deg(110.0),
);
let mut world = World::default();
let mut schedule = Schedule::default();
let kernel = Rc::new(kernel);
for plugin in &plugins {
plugin.build(
&mut schedule,
kernel.clone(),
&mut world,
&mut renderer.render_graph,
);
}
Ok(Self {
kernel,
map_context: MapContext {
style,
view_state,
world,
renderer,
},
schedule,
})
}
pub fn render_tile(
&mut self,
layers: Vec<Box<<DefaultVectorTransferables as VectorTransferables>::LayerTessellated>>,
) {
let context = &mut self.map_context;
let tiles = &mut context.world.tiles;
tiles
.spawn_mut((0, 0, ZoomLevel::default()).into())
.expect("unable to spawn tile")
.insert(VectorLayersDataComponent {
done: true,
layers: layers
.into_iter()
.map(|layer| {
VectorLayerData::Available(AvailableVectorLayerData {
coords: layer.coords,
source_layer: layer.layer_data.name,
buffer: layer.buffer,
feature_indices: layer.feature_indices,
})
})
.collect::<Vec<_>>(),
});
self.schedule.run(context);
let resources = &mut context.world.resources;
let tiles = &mut context.world.tiles;
tiles.clear();
let pool = resources
.query_mut::<&mut Eventually<VectorBufferPool>>() .expect("VectorBufferPool not found")
.expect_initialized_mut("VectorBufferPool not initialized");
pool.clear();
}
pub async fn fetch_tile(&self, coords: WorldTileCoords) -> Result<Box<[u8]>, SourceFetchError> {
let source_client = self.kernel.source_client();
let data = source_client
.fetch(
&coords,
&SourceType::Tessellate(TessellateSource::default()),
)
.await?
.into_boxed_slice();
Ok(data)
}
pub async fn process_tile(
&self,
tile_data: Box<[u8]>,
source_layers: &[&str],
) -> Vec<Box<<DefaultVectorTransferables as VectorTransferables>::LayerTessellated>> {
let context = HeadlessContext::default();
let mut processor =
ProcessVectorContext::<DefaultVectorTransferables, HeadlessContext>::new(context);
let target_coords = WorldTileCoords::default(); process_vector_tile(
&tile_data,
VectorTileRequest {
coords: target_coords,
layers: source_layers
.iter()
.map(|layer| layer.to_string())
.collect(),
},
&mut processor,
)
.expect("Failed to process!");
let messages = processor.take_context().messages.deref().take();
let layers = messages.into_iter()
.filter(|message| message.tag() == <DefaultVectorTransferables as VectorTransferables>::LayerTessellated::message_tag())
.map(|message| message.into_transferable::<<DefaultVectorTransferables as VectorTransferables>::LayerTessellated>())
.collect::<Vec<_>>();
layers
}
}
#[derive(Default, Clone)]
pub struct HeadlessContext {
pub messages: Rc<RefCell<Vec<Message>>>,
}
impl Context for HeadlessContext {
fn send<T: IntoMessage>(&self, message: T) -> Result<(), SendError> {
self.messages.deref().borrow_mut().push(message.into());
Ok(())
}
}