1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use std::sync::{
Arc,
RwLock,
};
use std::collections::HashMap;
use std::thread::{
JoinHandle,
spawn,
};
use std::sync::mpsc::{
channel,
Sender,
Receiver,
};
use super::*;
#[derive(Debug, Clone)]
pub struct CacheStats {
cached_tiles: usize,
loading_tiles: usize,
cached_objects: usize,
cached_features: usize,
cached_vertices: usize,
total_stats: TileStats,
}
/// A cache structure to hold all loaded `Tile`s.
pub struct TileCache {
cache: HashMap<TileId, Arc<RwLock<Tile>>>,
loaders: Vec<(u64, JoinHandle<Option<Tile>>, TileId)>,
channel: (Sender<u64>, Receiver<u64>),
id: u64,
}
impl TileCache {
/// Create a new `TileCache`.
pub fn new() -> Self {
Self {
cache: HashMap::new(),
loaders: vec![],
channel: channel(),
id: 0,
}
}
/// Check loaders for loaded tiles and insert them if there is any.
pub fn finalize_loaded_tiles(&mut self) {
// Get all pending messages and work them.
for id in self.channel.1.try_iter() {
let potential_loader = self.loaders
.iter()
.enumerate()
.find(|(_, l)| l.0 == id);
// Try finalizing the complete loader.
if let Some((i, _)) = potential_loader {
let loader = self.loaders.remove(i);
match loader.1.join() {
Ok(tile) => {
if let Some(tile) = tile {
self.cache.insert(loader.2, Arc::new(RwLock::new(tile)));
}
},
Err(e) => {
log::error!("Failed to join tile loader thread for {}. Reason:\r\n{:?}", loader.2, e);
}
}
}
}
}
/// Request a tile from the cache.
pub fn request_tile(
&mut self,
tile_id: &TileId,
feature_collection: Arc<RwLock<FeatureCollection>>,
selection_tags: &Vec<String>
) {
let id = self.id;
self.id += 1;
// Find the corresponding loader to the requested tile if there is any.
let loader = self.loaders.iter().find(|l| l.2 == *tile_id);
// Check if tile is not in the cache yet and is not currently being loaded.
if !self.cache.contains_key(&tile_id) && loader.is_none() {
// Clone values to be moved into the thread.
let tile_id_clone = tile_id.clone();
let tx = self.channel.0.clone();
// Make sure we load all tags we want to include.
let selection_tags = selection_tags.clone();
// Store a new loader.
self.loaders.push((
id,
// Spawn a new loader.
spawn(move|| {
// Try fetch and work the tile data.
if let Some(data) = fetch_tile_data(&tile_id_clone) {
// Create a new Tile from the fetched data.
let tile = Tile::from_mbvt(&tile_id_clone, &data, feature_collection, selection_tags);
// Signalize that the end of the tile loading process could not be signalized.
match tx.send(id) {
Err(_) => log::debug!("Could not send the tile load message. This most likely happened because the app was terminated."),
_ => (),
}
Some(tile)
} else {
None
}
}),
tile_id.clone(),
));
}
}
/// Get a `Tile` from the `TileCache`.
///
/// Returns `None` if the tile is not in the cache.
/// The user has to request the loading of the `Tile` on their own.
pub fn try_get_tile(&self, tile_id: &TileId) -> Option<Arc<RwLock<Tile>>> {
self.cache.get(&tile_id).cloned()
}
pub fn get_stats(&self) -> CacheStats {
let mut total_stats = TileStats::new();
for tile in &self.cache {
let read_tile = tile.1.read().unwrap();
total_stats = total_stats + *read_tile.stats();
}
CacheStats {
cached_tiles: self.cache.len(),
loading_tiles: self.loaders.len(),
cached_objects: 0,
cached_features: 0,
cached_vertices: 0,
total_stats,
}
}
}