A simple map viewer

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. use coord::{TileCoord, View};
  2. use image::DynamicImage;
  3. use image;
  4. use reqwest::Client;
  5. use std::cmp::Ordering;
  6. use std::cmp;
  7. use std::collections::hash_set::HashSet;
  8. use std::fs::File;
  9. use std::io::Write;
  10. use std::path::{Path, PathBuf};
  11. use std::sync::mpsc::TryRecvError;
  12. use std::sync::{Arc, mpsc, Mutex};
  13. use std::thread;
  14. use tile::Tile;
  15. use tile_source::TileSource;
  16. //TODO remember failed loading attempts
  17. #[derive(Debug)]
  18. pub struct TileLoader {
  19. client: Option<Client>,
  20. join_handle: thread::JoinHandle<()>,
  21. request_tx: mpsc::Sender<LoaderMessage>,
  22. result_rx: mpsc::Receiver<(Tile, Option<DynamicImage>)>,
  23. pending: HashSet<Tile>,
  24. use_network: bool,
  25. }
  26. impl TileLoader {
  27. pub fn new<F>(notice_func: F, use_network: bool) -> Self
  28. where F: Fn(Tile) + Sync + Send + 'static,
  29. {
  30. let (request_tx, request_rx) = mpsc::channel();
  31. let (result_tx, result_rx) = mpsc::channel();
  32. TileLoader {
  33. client: None,
  34. join_handle: thread::spawn(move || Self::work(&request_rx, &result_tx, notice_func, use_network)),
  35. request_tx,
  36. result_rx,
  37. pending: HashSet::new(),
  38. use_network,
  39. }
  40. }
  41. fn work<F>(
  42. request_rx: &mpsc::Receiver<LoaderMessage>,
  43. result_tx: &mpsc::Sender<(Tile, Option<DynamicImage>)>,
  44. notice_func: F,
  45. use_network: bool,
  46. )
  47. where F: Fn(Tile) + Sync + Send + 'static,
  48. {
  49. let mut queue: Vec<TileRequest> = vec![];
  50. let remote_queue: Arc<Mutex<Vec<TileRequest>>> = Arc::new(Mutex::new(vec![]));
  51. let mut view_opt: Option<View> = None;
  52. let arc_notice_func = Arc::new(notice_func);
  53. let (remote_request_tx, remote_request_rx) = mpsc::channel();
  54. {
  55. let arc_request_rx = Arc::new(Mutex::new(remote_request_rx));
  56. for id in 0..2 {
  57. let remote_queue = Arc::clone(&remote_queue);
  58. let arc_request_rx = Arc::clone(&arc_request_rx);
  59. let result_tx = result_tx.clone();
  60. let arc_notice_func = Arc::clone(&arc_notice_func);
  61. thread::spawn(move || Self::work_remote(id, &remote_queue, &arc_request_rx, &result_tx, &arc_notice_func));
  62. }
  63. }
  64. 'outer: while let Ok(message) = request_rx.recv() {
  65. let mut need_to_sort = true;
  66. match message {
  67. LoaderMessage::SetView(view) => {
  68. view_opt = Some(view);
  69. },
  70. LoaderMessage::GetTile(request) => {
  71. queue.push(request);
  72. }
  73. }
  74. loop {
  75. loop {
  76. let message = request_rx.try_recv();
  77. match message {
  78. Ok(LoaderMessage::SetView(view)) => {
  79. view_opt = Some(view);
  80. need_to_sort = true;
  81. },
  82. Ok(LoaderMessage::GetTile(request)) => {
  83. queue.push(request);
  84. need_to_sort = true;
  85. },
  86. Err(TryRecvError::Empty) => break,
  87. Err(TryRecvError::Disconnected) => break 'outer,
  88. }
  89. }
  90. if need_to_sort {
  91. if let Some(view) = view_opt {
  92. need_to_sort = false;
  93. queue.as_mut_slice().sort_by(|a, b| {
  94. compare_tiles(a.tile, b.tile, view)
  95. });
  96. if let Ok(mut remote_queue) = remote_queue.lock() {
  97. remote_queue.as_mut_slice().sort_by(|a, b| {
  98. compare_tiles(a.tile, b.tile, view)
  99. });
  100. }
  101. }
  102. }
  103. match queue.pop() {
  104. None => break,
  105. Some(request) => {
  106. match image::open(&request.path) {
  107. Ok(img) => {
  108. if result_tx.send((request.tile, Some(img))).is_err() {
  109. break 'outer;
  110. }
  111. arc_notice_func(request.tile);
  112. continue;
  113. },
  114. Err(_) => {
  115. if use_network {
  116. if let Ok(mut remote_queue) = remote_queue.lock() {
  117. //TODO restrict size of remote_queue
  118. remote_queue.push(request);
  119. if let Some(view) = view_opt {
  120. remote_queue.as_mut_slice().sort_by(|a, b| {
  121. compare_tiles(a.tile, b.tile, view)
  122. });
  123. }
  124. if let Err(e) = remote_request_tx.send(RemoteLoaderMessage::PopQueue) {
  125. //TODO what now? restart worker?
  126. error!("remote worker terminated, {}", e);
  127. }
  128. }
  129. } else if result_tx.send((request.tile, None)).is_err() {
  130. break 'outer;
  131. }
  132. },
  133. }
  134. },
  135. }
  136. }
  137. }
  138. }
  139. fn work_remote<F>(
  140. thread_id: u32,
  141. queue: &Arc<Mutex<Vec<TileRequest>>>,
  142. request_rx: &Arc<Mutex<mpsc::Receiver<RemoteLoaderMessage>>>,
  143. result_tx: &mpsc::Sender<(Tile, Option<DynamicImage>)>,
  144. notice_func: &Arc<F>,
  145. )
  146. where F: Fn(Tile) + Sync + Send + 'static,
  147. {
  148. let mut client_opt = None;
  149. loop {
  150. let message = request_rx.lock().ok().and_then(|r| r.recv().ok());
  151. match message {
  152. None => break,
  153. Some(RemoteLoaderMessage::PopQueue) => {
  154. let ele: Option<TileRequest> = queue.lock().ok().and_then(|mut q| q.pop());
  155. if let Some(request) = ele {
  156. if client_opt.is_none() {
  157. client_opt = Client::builder().build().ok();
  158. }
  159. info!("thread {}, download {:?}", thread_id, request.url);
  160. if let Some(Ok(mut response)) = client_opt.as_ref().map(|c| c.get(&request.url).send()) {
  161. let mut buf: Vec<u8> = vec![];
  162. if response.copy_to(&mut buf).is_ok() {
  163. if let Ok(img) = image::load_from_memory(&buf) {
  164. // successfully loaded tile
  165. if result_tx.send((request.tile, Some(img))).is_err() {
  166. break;
  167. }
  168. notice_func(request.tile);
  169. if request.write_to_file {
  170. if let Err(e) = Self::write_to_file(&request.path, &buf) {
  171. warn!("could not write file {}, {}", request.path.display(), e);
  172. }
  173. }
  174. continue;
  175. }
  176. }
  177. }
  178. // failed not load tile
  179. info!("thread {}, fail {:?}", thread_id, request.url);
  180. if result_tx.send((request.tile, None)).is_err() {
  181. break;
  182. }
  183. }
  184. },
  185. }
  186. }
  187. }
  188. pub fn async_request(&mut self, tile_coord: TileCoord, source: &TileSource, write_to_file: bool) {
  189. if tile_coord.zoom > source.max_tile_zoom() ||
  190. tile_coord.zoom < source.min_tile_zoom()
  191. {
  192. return;
  193. }
  194. let tile = Tile::new(tile_coord, source.id());
  195. if !self.pending.contains(&tile) {
  196. if let Some(url) = source.remote_tile_url(tile_coord) {
  197. if self.request_tx.send(LoaderMessage::GetTile(
  198. TileRequest {
  199. tile,
  200. url,
  201. path: source.local_tile_path(tile_coord),
  202. write_to_file,
  203. }
  204. )).is_ok()
  205. {
  206. self.pending.insert(tile);
  207. }
  208. }
  209. }
  210. }
  211. pub fn async_result(&mut self) -> Option<(Tile, DynamicImage)> {
  212. match self.result_rx.try_recv() {
  213. Err(_) => None,
  214. Ok((tile, None)) => {
  215. self.pending.remove(&tile);
  216. debug!("async_result none, pending.len: {}, {:?}", self.pending.len(), tile);
  217. None
  218. },
  219. Ok((tile, Some(img))) => {
  220. self.pending.remove(&tile);
  221. debug!("async_result some, pending.len: {}, {:?}", self.pending.len(), tile);
  222. Some((tile, img))
  223. },
  224. }
  225. }
  226. pub fn get_sync(&mut self, tile: TileCoord, source: &TileSource, write_to_file: bool) -> Option<DynamicImage> {
  227. if tile.zoom > source.max_tile_zoom() ||
  228. tile.zoom < source.min_tile_zoom()
  229. {
  230. return None;
  231. }
  232. match image::open(source.local_tile_path(tile)) {
  233. Ok(img) => {
  234. debug!("sync ok from path {:?}", tile);
  235. Some(img)
  236. },
  237. Err(_) => {
  238. if self.use_network {
  239. //TODO do not try to create a client every time when it failed before
  240. if self.client.is_none() {
  241. self.client = Client::builder().build().ok();
  242. }
  243. if let (Some(client), Some(url)) = (self.client.as_ref(), source.remote_tile_url(tile)) {
  244. if let Ok(mut response) = client.get(&url).send() {
  245. let mut buf: Vec<u8> = vec![];
  246. if response.copy_to(&mut buf).is_ok() {
  247. if let Ok(img) = image::load_from_memory(&buf) {
  248. if write_to_file {
  249. let path = source.local_tile_path(tile);
  250. if let Err(e) = Self::write_to_file(&path, &buf) {
  251. warn!("could not write file {}, {}", &path.display(), e);
  252. }
  253. }
  254. debug!("sync ok from network {:?}", tile);
  255. return Some(img);
  256. }
  257. }
  258. }
  259. }
  260. debug!("sync fail from network {:?}", tile);
  261. None
  262. } else {
  263. debug!("sync fail from path {:?}", tile);
  264. None
  265. }
  266. },
  267. }
  268. }
  269. pub fn set_view_location(&mut self, view: View) {
  270. let _ = self.request_tx.send(LoaderMessage::SetView(view));
  271. }
  272. fn write_to_file<P: AsRef<Path>>(path: P, img_data: &[u8]) -> ::std::io::Result<()> {
  273. if let Some(dir) = path.as_ref().parent() {
  274. ::std::fs::create_dir_all(dir)?;
  275. }
  276. let mut file = File::create(path)?;
  277. file.write_all(img_data)
  278. }
  279. }
  280. #[derive(Debug)]
  281. struct TileRequest {
  282. pub tile: Tile,
  283. pub url: String,
  284. pub path: PathBuf,
  285. pub write_to_file: bool,
  286. }
  287. #[derive(Debug)]
  288. enum LoaderMessage {
  289. GetTile(TileRequest),
  290. SetView(View),
  291. }
  292. #[derive(Debug)]
  293. enum RemoteLoaderMessage {
  294. PopQueue,
  295. }
  296. fn compare_tiles(a: Tile, b: Tile, view: View) -> Ordering {
  297. let source_a = view.source_id == a.source_id;
  298. let source_b = view.source_id == b.source_id;
  299. match (source_a, source_b) {
  300. (true, false) => Ordering::Greater,
  301. (false, true) => Ordering::Less,
  302. _ => {
  303. let zoom_diff_a = cmp::max(a.coord.zoom, view.zoom) - cmp::min(a.coord.zoom, view.zoom);
  304. let zoom_diff_b = cmp::max(b.coord.zoom, view.zoom) - cmp::min(b.coord.zoom, view.zoom);
  305. if zoom_diff_a < zoom_diff_b {
  306. Ordering::Greater
  307. } else if zoom_diff_a > zoom_diff_b {
  308. Ordering::Less
  309. } else {
  310. let map_a = a.coord.map_coord_center();
  311. let map_b = b.coord.map_coord_center();
  312. let diff_xa = (view.center.x - map_a.x).abs();
  313. let diff_xa = if diff_xa > 0.5 { 1.0 - diff_xa } else { diff_xa };
  314. let diff_xb = (view.center.x - map_b.x).abs();
  315. let diff_xb = if diff_xb > 0.5 { 1.0 - diff_xb } else { diff_xb };
  316. let diff_ya = view.center.y - map_a.y;
  317. let diff_yb = view.center.y - map_b.y;
  318. let center_diff_a = (diff_xa * diff_xa) + (diff_ya * diff_ya);
  319. let center_diff_b = (diff_xb * diff_xb) + (diff_yb * diff_yb);
  320. center_diff_b.partial_cmp(&center_diff_a).unwrap_or(Ordering::Equal)
  321. }
  322. },
  323. }
  324. }