diff --git a/src/bin/app_state.rs b/src/bin/app_state.rs index 9b709b9..a7ea78a 100644 --- a/src/bin/app_state.rs +++ b/src/bin/app_state.rs @@ -35,10 +35,7 @@ impl AppState { } pub fn update_hovered_objects(&mut self, point: (f32, f32)) { - self.hovered_objects = Collider::get_hovered_objects(&self.tile_cache, &self.screen, self.zoom ,point) - .iter() - .map(|o| (**o).clone()) - .collect(); + self.hovered_objects = Collider::get_hovered_objects(&self.tile_cache, &self.screen, self.zoom ,point); } pub fn update_selected_hover_objects(&mut self) { diff --git a/src/bin/drawing/painter.rs b/src/bin/drawing/painter.rs index 720ccd9..30026fa 100644 --- a/src/bin/drawing/painter.rs +++ b/src/bin/drawing/painter.rs @@ -639,7 +639,7 @@ impl Painter { app_state.tile_cache.fetch_tiles(); for tile_id in tile_field.iter() { if !self.loaded_tiles.contains_key(&tile_id) { - app_state.tile_cache.request_tile(&tile_id, self.feature_collection.clone(), CONFIG.renderer.selection_tags.clone()); + app_state.tile_cache.request_tile(&tile_id, self.feature_collection.clone(), &CONFIG.renderer.selection_tags.clone()); let tile_cache = &mut app_state.tile_cache; if let Some(tile) = tile_cache.try_get_tile(&tile_id) { diff --git a/src/lib/cache.rs b/src/lib/cache.rs index 8580e38..db378a7 100644 --- a/src/lib/cache.rs +++ b/src/lib/cache.rs @@ -62,12 +62,13 @@ impl TileCache { &mut self, tile_id: &TileId, feature_collection: Arc>, - selection_tags: Vec + selection_tags: &Vec ) { let id = self.id; self.id += 1; let loader = self.loaders.iter().filter(|l| l.2 == *tile_id).next(); + let selection_tags = selection_tags.clone(); if !self.cache.contains_key(&tile_id) && loader.is_none() { let tile_id_clone = tile_id.clone(); diff --git a/src/lib/drawing/drawable_layer.rs b/src/lib/drawing/drawable_layer.rs deleted file mode 100644 index 14130c9..0000000 --- a/src/lib/drawing/drawable_layer.rs +++ /dev/null @@ -1,8 +0,0 @@ -use core::ops::Range; - -#[derive(Debug, Clone)] -pub struct DrawableLayer { - pub id: u32, - pub indices_range: Range, - pub features: Vec<(u32, Range)> -} \ No newline at end of file diff --git a/src/lib/drawing/mod.rs b/src/lib/drawing/mod.rs index 50a9642..1d2ca16 100644 --- a/src/lib/drawing/mod.rs +++ b/src/lib/drawing/mod.rs @@ -1,11 +1,9 @@ mod line_tesselator; mod mesh; mod vertex; -mod drawable_layer; mod drawable_tile; pub use line_tesselator::*; pub use mesh::*; pub use vertex::*; -pub use drawable_layer::*; pub use drawable_tile::*; \ No newline at end of file diff --git a/src/lib/feature/collection.rs b/src/lib/feature/collection.rs index e7fc890..712c8ca 100644 --- a/src/lib/feature/collection.rs +++ b/src/lib/feature/collection.rs @@ -18,17 +18,25 @@ impl FeatureCollection { &self.features } - pub fn get_feature_id(&mut self, selector: &crate::css::Selector) -> Option { + fn get_feature_id(&mut self, selector: &crate::css::Selector) -> Option { self.features.iter_mut().enumerate().find(|(_, feature)| &feature.selector == selector).map(|(i, _)| i as u32) } - pub fn add_feature(&mut self, mut feature: Feature) -> u32 { + fn add_feature(&mut self, mut feature: Feature) -> u32 { assert!(self.features.len() < self.n_features_max as usize); feature.id = self.features.len() as u32; self.features.push(feature); self.features.len() as u32 - 1 } + pub fn ensure_feature(&mut self, selector: &Selector) -> u32 { + if let Some(feature_id) = self.get_feature_id(selector) { + feature_id + } else { + self.add_feature(Feature::new(selector.clone(), 0)) + } + } + pub fn is_visible(&self, feature_id: u32) -> bool { let feature = &self.features[feature_id as usize]; let bga = feature.style.background_color.a; diff --git a/src/lib/interaction/collider.rs b/src/lib/interaction/collider.rs index f50e7b2..a6fa308 100644 --- a/src/lib/interaction/collider.rs +++ b/src/lib/interaction/collider.rs @@ -7,8 +7,8 @@ pub struct Collider { } impl Collider { - pub fn get_hovered_objects<'a>(cache: &'a TileCache, screen: &Screen, zoom: f32, point: (f32, f32)) -> Vec<&'a Object> { - let mut objects = vec![]; + pub fn get_hovered_objects<'a>(cache: &'a TileCache, screen: &Screen, zoom: f32, point: (f32, f32)) -> Vec { + let mut return_objects = vec![]; let tile_field = screen.get_tile_boundaries_for_zoom_level(zoom, 1); for tile_id in tile_field.iter() { @@ -27,17 +27,21 @@ impl Collider { if tile_point.x >= 0.0 && tile_point.x <= tile.extent as f32 && tile_point.y >= 0.0 && tile_point.y <= tile.extent as f32 { - let object_ids = tile.collider.get_hovered_objects(&tile_point); - for object_id in object_ids { - objects.push(&tile.objects[object_id]) + if let Ok(collider) = tile.collider.try_read() { + if let Ok(objects) = tile.objects.try_read() { + let object_ids = collider.get_hovered_objects(&tile_point); + for object_id in object_ids { + return_objects.push(objects[object_id].clone()) + } + } } - return objects + return return_objects } } else { log::trace!("[Intersection pass] Could not read tile {} from cache.", tile_id); } } - objects + return_objects } } \ No newline at end of file diff --git a/src/lib/vector_tile/tile.rs b/src/lib/vector_tile/tile.rs index 4cc6cdf..60414c8 100644 --- a/src/lib/vector_tile/tile.rs +++ b/src/lib/vector_tile/tile.rs @@ -2,6 +2,9 @@ use std::sync::{ Arc, RwLock, }; +use std::thread::{ + spawn, +}; use std::collections::HashMap; use std::ops::Range; use quick_protobuf::{ @@ -26,9 +29,9 @@ pub struct Tile { pub tile_id: TileId, pub mesh: VertexBuffers, pub extent: u16, - pub objects: Vec, + pub objects: Arc>>, pub features: Vec<(u32, Range)>, - pub collider: TileCollider, + pub collider: Arc>, } pub fn layer_num(name: &str) -> u32 { @@ -37,18 +40,18 @@ pub fn layer_num(name: &str) -> u32 { "water" => 1, "waterway" => 2, "landuse" => 3, - // "mountain_peak" => 4, + "mountain_peak" => 4, "park" => 5, "boundary" => 6, - // "aeroway" => 7, + "aeroway" => 7, "transportation" => 8, "building" => 9, - // "water_name" => 10, - // "transportation_name" => 11, - // "place" => 12, - // "housenumber" => 13, - // "poi" => 14, - // "aerodrome_label" => 15, + "water_name" => 10, + "transportation_name" => 11, + "place" => 12, + "housenumber" => 13, + "poi" => 14, + "aerodrome_label" => 15, _ => 19, } } @@ -56,112 +59,48 @@ pub fn layer_num(name: &str) -> u32 { impl Tile { pub fn from_mbvt( tile_id: &TileId, - data: &Vec, + pbf_data: &Vec, feature_collection: Arc>, selection_tags: Vec ) -> Self { - // let t = std::time::Instant::now(); - - // we can build a bytes reader directly out of the bytes - let mut reader = BytesReader::from_bytes(&data); - - let tile = super::vector_tile::Tile::from_reader(&mut reader, &data).expect("Cannot read Tile object."); + // Read tile data from the pbf data. + let mut reader = BytesReader::from_bytes(&pbf_data); + let tile = super::vector_tile::Tile::from_reader(&mut reader, &pbf_data).expect("Cannot read Tile object."); let mut objects = Vec::new(); - let mut mesh: VertexBuffers = VertexBuffers::with_capacity(100_000, 100_000); + let mut mesh: VertexBuffers = VertexBuffers::with_capacity(10_000, 10_000); let mut builder = MeshBuilder::new(&mut mesh, LayerVertexCtor::new(tile_id, 1.0)); let extent = tile.layers[0].extent as u16; let mut features = vec![]; - let mut current_feature_id; - - // Add a background rectangle to each tile - let selector = Selector::new().with_type("background"); - - let mut path_builder = Path::builder(); - path_builder.move_to((-10.0, -10.0).into()); - path_builder.line_to((-10.0, extent as f32 + 10.0).into()); - path_builder.line_to((extent as f32 + 10.0, extent as f32 + 10.0).into()); - path_builder.line_to((extent as f32 + 10.0, -10.0).into()); - path_builder.close(); - let path = path_builder.build(); - let index_start_before = builder.get_current_index(); - { - let mut feature_collection = feature_collection.write().unwrap(); - current_feature_id = if let Some(feature_id) = feature_collection.get_feature_id(&selector) { - feature_id - } else { - feature_collection.add_feature(Feature::new(selector.clone(), 0)) - }; - builder.set_current_feature_id(current_feature_id); - } - FillTessellator::new().tessellate_path( - &path, - &FillOptions::tolerance(0.0001).with_normals(true), + // Add a background feature to the tile data. + let ( + mut current_feature_id, + object, + range + ) = Self::create_background_feature( &mut builder, - ).expect("This is a bug. Please report it."); - - features.push((current_feature_id, index_start_before..builder.get_current_index())); - - objects.push(Object::new( - selector, - path.points().iter().cloned().collect(), - ObjectType::Polygon - )); + feature_collection.clone(), + extent + ); + features.push((current_feature_id, range)); + objects.push(object); // Transform all features of the tile. - for layer in tile.layers { - let layer_id = layer_num(&layer.name); - let mut map: std::collections::HashMap)>> = HashMap::new(); // Preevaluate the selectors and group features by the selector they belong to. - for feature in layer.features { - let mut selector = Selector::new() - .with_type("layer".to_string()) - .with_any("name".to_string(), layer.name.to_string()); - - let mut tags = std::collections::HashMap::new(); - - for tag in feature.tags.chunks(2) { - let key = layer.keys[tag[0] as usize].to_string(); - let value = layer.values[tag[1] as usize].clone(); - if selection_tags.contains(&key) { - match &key[..] { - "class" => { - selector.classes.push(value.string_value.clone().unwrap().to_string()); - }, - _ => { - selector = selector.with_any(key.clone(), { - value.string_value.map_or(String::new(), |v| v.to_string()) - + &value.float_value.map_or(String::new(), |v| v.to_string()) - + &value.double_value.map_or(String::new(), |v| v.to_string()) - + &value.int_value.map_or(String::new(), |v| v.to_string()) - + &value.uint_value.map_or(String::new(), |v| v.to_string()) - + &value.sint_value.map_or(String::new(), |v| v.to_string()) - + &value.bool_value.map_or(String::new(), |v| v.to_string()) - }); - }, - } - } else { - tags.insert(key.clone(), { - value.string_value.map_or(String::new(), |v| v.to_string()) - + &value.float_value.map_or(String::new(), |v| v.to_string()) - + &value.double_value.map_or(String::new(), |v| v.to_string()) - + &value.int_value.map_or(String::new(), |v| v.to_string()) - + &value.uint_value.map_or(String::new(), |v| v.to_string()) - + &value.sint_value.map_or(String::new(), |v| v.to_string()) - + &value.bool_value.map_or(String::new(), |v| v.to_string()) - }); - } - } + for feature in &layer.features { + let (selector, tags) = Self::classify(&layer, &feature, &selection_tags); let paths = geometry_commands_to_paths( feature.type_pb, &feature.geometry ); + // If we have a valid object at hand, insert it into the object list + let object_type = match feature.type_pb { GeomType::POLYGON => Some(ObjectType::Polygon), GeomType::LINESTRING => Some(ObjectType::Line), @@ -169,12 +108,14 @@ impl Tile { _ => None, }; - object_type.map(|ot| objects.push(Object::new_with_tags( - selector.clone(), - paths[0].points().iter().cloned().collect(), - tags, - ot - ))); + if let Some(ot) = object_type { + objects.push(Object::new_with_tags( + selector.clone(), + paths[0].points().iter().cloned().collect(), + tags, + ot + )); + } if let Some(value) = map.get_mut(&selector) { value.push((feature.type_pb, paths)); @@ -185,18 +126,16 @@ impl Tile { // Transform all the features on a per selector basis. let mut inner_features = vec![]; - for (selector, fs) in map { + for (selector, features) in map { let index_start_before = builder.get_current_index(); - for feature in fs { - { + for feature in features { + // Set the current feature id. + current_feature_id = { + // Scope the lock guard real tight to ensure it's released quickly. let mut feature_collection = feature_collection.write().unwrap(); - current_feature_id = if let Some(feature_id) = feature_collection.get_feature_id(&selector) { - feature_id - } else { - feature_collection.add_feature(Feature::new(selector.clone(), layer_id)) - }; - builder.set_current_feature_id(current_feature_id); - } + feature_collection.ensure_feature(&selector) + }; + builder.set_current_feature_id(current_feature_id); paths_to_drawable( &mut builder, @@ -213,14 +152,26 @@ impl Tile { features.extend(inner_features); } - let mut collider = TileCollider::new(); + let collider = Arc::new(RwLock::new(TileCollider::new())); + let collider_keep = collider.clone(); + let objects = Arc::new(RwLock::new(objects)); + let objects_keep = objects.clone(); - for object_id in 0..objects.len() { - if objects[object_id].points.len() >= 2 { - collider.add_object(object_id, &objects[object_id]); + spawn(move|| { + if let Ok(objects) = objects_keep.read() { + match collider_keep.write() { + Ok(mut collider) => { + for object_id in 0..objects.len() { + if objects[object_id].points.len() >= 2 { + collider.add_object(object_id, &objects[object_id]); + } + } + collider.update(); + }, + Err(_e) => log::error!("Could not aquire collider lock. Not loading the objects of this tile."), + } } - } - collider.update(); + }); Self { tile_id: tile_id.clone(), @@ -231,4 +182,96 @@ impl Tile { collider, } } + + /// Creates a rectangle the size of a tile to be used as the background of a tile. + /// Could also display a texture in the future (speak swisstopo). + fn create_background_feature<'l>( + builder: &mut MeshBuilder<'l>, + feature_collection: Arc>, + extent: u16 + ) -> (u32, Object, Range) { + // Create a new background type selector. + let selector = Selector::new().with_type("background"); + + // Create a rectangular path. + let mut path_builder = Path::builder(); + path_builder.move_to((-10.0, -10.0).into()); + path_builder.line_to((-10.0, extent as f32 + 10.0).into()); + path_builder.line_to((extent as f32 + 10.0, extent as f32 + 10.0).into()); + path_builder.line_to((extent as f32 + 10.0, -10.0).into()); + path_builder.close(); + let path = path_builder.build(); + + // Set the current feature id. + let current_feature_id = { + // Scope the lock guard real tight to ensure it's released quickly. + let mut feature_collection = feature_collection.write().unwrap(); + feature_collection.ensure_feature(&selector) + }; + builder.set_current_feature_id(current_feature_id); + + // Remember buffer index before. + let index_start_before = builder.get_current_index(); + + // Tesselate path. + FillTessellator::new().tessellate_path( + &path, + &FillOptions::tolerance(0.0001).with_normals(true), + builder, + ).expect("This is a bug. Please report it."); + + let object = Object::new( + selector, + path.points().iter().cloned().collect(), + ObjectType::Polygon + ); + + (current_feature_id, object, index_start_before..builder.get_current_index()) + } + + /// Create a selector and a list of tags form MBVT information. + fn classify( + layer: &vector_tile::mod_Tile::Layer, + feature: &vector_tile::mod_Tile::Feature, + selection_tags: &Vec + ) -> (Selector, HashMap) { + let mut selector = Selector::new() + .with_type("layer".to_string()) + .with_any("name".to_string(), layer.name.to_string()); + + let mut tags = HashMap::new(); + + for tag in feature.tags.chunks(2) { + let key = layer.keys[tag[0] as usize].to_string(); + let value = layer.values[tag[1] as usize].clone(); + match &key[..] { + "class" => { + selector.classes.push(value.string_value.clone().unwrap().to_string()); + }, + _ => { + if selection_tags.contains(&key) { + selector = selector.with_any(key.clone(), value.to_string()); + } else { + tags.insert(key.clone(), value.to_string()); + } + }, + } + } + + (selector, tags) + } +} + +impl<'a> std::string::ToString for vector_tile::mod_Tile::Value<'a> { + fn to_string(&self) -> String { + // We can make the safe assumption that only ever one property is Some(_). + // So we just unwrap all the strings and concat it into one. + self.string_value.clone().map_or(String::new(), |v| v.to_string()) + + &self.float_value.map_or(String::new(), |v| v.to_string()) + + &self.double_value.map_or(String::new(), |v| v.to_string()) + + &self.int_value.map_or(String::new(), |v| v.to_string()) + + &self.uint_value.map_or(String::new(), |v| v.to_string()) + + &self.sint_value.map_or(String::new(), |v| v.to_string()) + + &self.bool_value.map_or(String::new(), |v| v.to_string()) + } } \ No newline at end of file