1#[macro_use]
17extern crate lazy_static;
18extern crate regex;
19
20#[cfg(feature = "world_file")]
21extern crate world_image_file;
22
23use regex::Regex;
24use std::borrow::Borrow;
25use std::convert::TryFrom;
26use std::fs::File;
27use std::io::{BufRead, BufReader, Seek, SeekFrom};
28use std::ops::Deref;
29use std::str::FromStr;
30
31#[cfg(feature = "world_file")]
32use world_image_file::WorldFile;
33
34#[cfg(test)]
35mod tests;
36
37#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
39pub struct Tile {
40 zoom: u8,
41 x: u32,
42 y: u32,
43}
44
45impl Tile {
46 pub fn new(zoom: u8, x: u32, y: u32) -> Option<Tile> {
55 if zoom >= 100 {
56 None
57 } else if x < 2u32.pow(zoom as u32) && y < 2u32.pow(zoom as u32) {
58 Some(Tile {
59 zoom: zoom,
60 x: x,
61 y: y,
62 })
63 } else {
64 None
65 }
66 }
67
68 pub fn zoom(&self) -> u8 {
70 self.zoom
71 }
72
73 pub fn x(&self) -> u32 {
75 self.x
76 }
77
78 pub fn y(&self) -> u32 {
80 self.y
81 }
82
83 pub fn from_tms(tms: &str) -> Option<Tile> {
94 lazy_static! {
95 static ref RE: Regex = Regex::new(
96 "/?(?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})(\\.[a-zA-Z]{3,4})?$"
97 )
98 .unwrap();
99 }
100
101 let caps = RE.captures(tms)?;
102
103 let zoom = caps.name("zoom");
104 let x = caps.name("x");
105 let y = caps.name("y");
106 if zoom.is_none() || x.is_none() || y.is_none() {
107 return None;
108 }
109 let zoom = zoom.unwrap().as_str().parse();
110 let x = x.unwrap().as_str().parse();
111 let y = y.unwrap().as_str().parse();
112
113 if zoom.is_err() || x.is_err() || y.is_err() {
114 return None;
115 }
116 let zoom: u8 = zoom.unwrap();
117 let x: u32 = x.unwrap();
118 let y: u32 = y.unwrap();
119
120 Tile::new(zoom, x, y)
121 }
122
123 pub fn parent(&self) -> Option<Tile> {
140 match self.zoom {
141 0 => {
142 None
144 }
145 _ => Tile::new(self.zoom - 1, self.x / 2, self.y / 2),
146 }
147 }
148
149 pub fn subtiles(&self) -> Option<[Tile; 4]> {
163 match self.zoom {
164 std::u8::MAX => None,
165 _ => {
166 let z = self.zoom + 1;
167 let x = 2 * self.x;
168 let y = 2 * self.y;
169 Some([
170 Tile {
171 zoom: z,
172 x: x,
173 y: y,
174 },
175 Tile {
176 zoom: z,
177 x: x + 1,
178 y: y,
179 },
180 Tile {
181 zoom: z,
182 x: x,
183 y: y + 1,
184 },
185 Tile {
186 zoom: z,
187 x: x + 1,
188 y: y + 1,
189 },
190 ])
191 }
192 }
193 }
194
195 pub fn all_subtiles_iter(&self) -> AllSubTilesIterator {
197 AllSubTilesIterator::new_from_tile(&self)
198 }
199
200 pub fn centre_point(&self) -> LatLon {
202 tile_nw_lat_lon(self.zoom, (self.x as f32) + 0.5, (self.y as f32) + 0.5)
203 }
204
205 pub fn center_point(&self) -> LatLon {
207 self.centre_point()
208 }
209
210 pub fn nw_corner(&self) -> LatLon {
212 tile_nw_lat_lon(self.zoom, self.x as f32, self.y as f32)
213 }
214
215 pub fn ne_corner(&self) -> LatLon {
217 tile_nw_lat_lon(self.zoom, (self.x as f32) + 1.0, self.y as f32)
218 }
219
220 pub fn sw_corner(&self) -> LatLon {
222 tile_nw_lat_lon(self.zoom, self.x as f32, (self.y as f32) + 1.0)
223 }
224
225 pub fn se_corner(&self) -> LatLon {
227 tile_nw_lat_lon(self.zoom, (self.x as f32) + 1.0, (self.y as f32) + 1.0)
228 }
229
230 pub fn top(&self) -> f32 {
231 self.nw_corner().lat
232 }
233 pub fn bottom(&self) -> f32 {
234 self.sw_corner().lat
235 }
236 pub fn left(&self) -> f32 {
237 self.nw_corner().lon
238 }
239 pub fn right(&self) -> f32 {
240 self.se_corner().lon
241 }
242
243 pub fn tc_path<T: std::fmt::Display>(&self, ext: T) -> String {
245 let tc = xy_to_tc(self.x, self.y);
246 format!(
247 "{}/{}/{}/{}/{}/{}/{}.{}",
248 self.zoom, tc[0], tc[1], tc[2], tc[3], tc[4], tc[5], ext
249 )
250 }
251
252 pub fn mp_path<T: std::fmt::Display>(&self, ext: T) -> String {
254 let mp = xy_to_mp(self.x, self.y);
255 format!(
256 "{}/{}/{}/{}/{}.{}",
257 self.zoom, mp[0], mp[1], mp[2], mp[3], ext
258 )
259 }
260
261 pub fn ts_path<T: std::fmt::Display>(&self, ext: T) -> String {
263 let ts = xy_to_ts(self.x, self.y);
264 format!(
265 "{}/{}/{}/{}/{}.{}",
266 self.zoom, ts[0], ts[1], ts[2], ts[3], ext
267 )
268 }
269
270 pub fn zxy(&self) -> String {
272 format!("{}/{}/{}", self.zoom, self.x, self.y)
273 }
274
275 pub fn zxy_path<T: std::fmt::Display>(&self, ext: T) -> String {
277 format!("{}/{}/{}.{}", self.zoom, self.x, self.y, ext)
278 }
279
280 pub fn mt_path<T: std::fmt::Display>(&self, ext: T) -> String {
282 let tc = xy_to_mt(self.x, self.y);
283 format!(
284 "{}/{}/{}/{}/{}/{}.{}",
285 self.zoom, tc[0], tc[1], tc[2], tc[3], tc[4], ext
286 )
287 }
288
289 pub fn all() -> AllTilesIterator {
297 AllTilesIterator {
298 next_zoom: 0,
299 next_zorder: 0,
300 }
301 }
302
303 pub fn all_to_zoom(max_zoom: u8) -> AllTilesToZoomIterator {
307 AllTilesToZoomIterator {
308 max_zoom: max_zoom,
309 next_zoom: 0,
310 next_x: 0,
311 next_y: 0,
312 }
313 }
314
315 pub fn bbox(&self) -> BBox {
317 let nw = self.nw_corner();
318 let se = self.se_corner();
319
320 BBox::new_from_points(&nw, &se)
321 }
322
323 pub fn metatile(&self, scale: u8) -> Option<Metatile> {
324 Metatile::new(scale, self.zoom(), self.x(), self.y())
325 }
326
327 pub fn modtile_metatile(&self) -> Option<ModTileMetatile> {
328 ModTileMetatile::new(self.zoom(), self.x(), self.y())
329 }
330
331 #[cfg(feature = "world_file")]
332 pub fn world_file(&self) -> WorldFile {
334 let total_merc_width = 20037508.342789244;
335 let tile_merc_width = (2. * total_merc_width) / 2f64.powi(self.zoom as i32);
336 let scale = tile_merc_width / 256.;
337
338 WorldFile {
339 x_scale: scale,
340 y_scale: -scale,
341
342 x_skew: 0.,
343 y_skew: 0.,
344
345 x_coord: tile_merc_width * (self.x as f64) - total_merc_width,
346 y_coord: -tile_merc_width * (self.y as f64) + total_merc_width,
347 }
348 }
349}
350
351impl FromStr for Tile {
352 type Err = &'static str;
353
354 fn from_str(s: &str) -> Result<Self, Self::Err> {
355 lazy_static! {
356 static ref TILE_RE: Regex =
357 Regex::new("^(?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})$")
358 .unwrap();
359 }
360
361 let caps = TILE_RE.captures(s);
362
363 if caps.is_none() {
364 return Err("Tile Z/X/Y regex didn't match");
365 }
366 let caps = caps.unwrap();
367
368 let zoom = caps.name("zoom").unwrap().as_str().parse().unwrap();
370 let x = caps.name("x").unwrap().as_str().parse().unwrap();
371 let y = caps.name("y").unwrap().as_str().parse().unwrap();
372
373 match Tile::new(zoom, x, y) {
374 None => {
375 Err("Invalid X or Y for this zoom")
377 }
378 Some(t) => Ok(t),
379 }
380 }
381}
382
383pub struct AllTilesIterator {
385 next_zoom: u8,
386 next_zorder: u64,
387}
388
389impl Iterator for AllTilesIterator {
390 type Item = Tile;
391
392 fn next(&mut self) -> Option<Tile> {
393 let zoom = self.next_zoom;
394 let (x, y) = zorder_to_xy(self.next_zorder);
395 let tile = Tile::new(zoom, x, y);
396
397 let max_tile_no = 2u32.pow(zoom as u32) - 1;
398 if x == max_tile_no && y == max_tile_no {
399 self.next_zoom = zoom + 1;
401 self.next_zorder = 0;
402 } else {
403 self.next_zorder += 1;
404 }
405
406 tile
407 }
408}
409
410pub struct AllTilesToZoomIterator {
412 max_zoom: u8,
413 next_zoom: u8,
414 next_x: u32,
415 next_y: u32,
416}
417
418fn remaining_in_this_zoom(next_zoom: u8, next_x: u32, next_y: u32) -> Option<usize> {
419 if next_zoom == 0 && next_x == 0 && next_y == 0 {
420 return Some(1);
421 }
422
423 let max_tile_no = 2u32.pow(next_zoom as u32);
424 let remaining_in_column = max_tile_no - next_y;
425 let remaining_in_column = remaining_in_column as usize;
426 let remaining_rows = max_tile_no - next_x - 1;
427 let remaining_rows = remaining_rows as usize;
428
429 let remaining_after_this_column = remaining_rows.checked_mul(max_tile_no as usize)?;
430
431 remaining_in_column.checked_add(remaining_after_this_column)
432}
433
434impl Iterator for AllTilesToZoomIterator {
435 type Item = Tile;
436
437 fn next(&mut self) -> Option<Tile> {
438 if self.next_zoom > self.max_zoom {
439 return None;
440 }
441 let tile = Tile::new(self.next_zoom, self.next_x, self.next_y);
442 let max_tile_no = 2u32.pow(self.next_zoom as u32) - 1;
443 if self.next_y < max_tile_no {
444 self.next_y += 1;
445 } else if self.next_x < max_tile_no {
446 self.next_x += 1;
447 self.next_y = 0;
448 } else if self.next_zoom < std::u8::MAX {
449 self.next_zoom += 1;
450 self.next_x = 0;
451 self.next_y = 0;
452 }
453
454 tile
455 }
456
457 fn size_hint(&self) -> (usize, Option<usize>) {
458 if self.next_zoom > self.max_zoom {
459 return (0, Some(0));
460 }
461
462 let remaining_in_this_level =
463 remaining_in_this_zoom(self.next_zoom, self.next_x, self.next_y);
464 if remaining_in_this_level.is_none() {
465 return (std::usize::MAX, None);
466 }
467 let remaining_in_this_level = remaining_in_this_level.unwrap();
468
469 let mut total: usize = remaining_in_this_level as usize;
470 for i in (self.next_zoom + 1)..(self.max_zoom + 1) {
471 let tiles_this_zoom = num_tiles_in_zoom(i);
472 if tiles_this_zoom.is_none() {
473 return (std::usize::MAX, None);
474 }
475
476 let tiles_this_zoom = tiles_this_zoom.unwrap();
477
478 let new_total = total.checked_add(tiles_this_zoom);
479 if new_total.is_none() {
480 return (std::usize::MAX, None);
481 }
482 total = new_total.unwrap();
483 }
484
485 (total, Some(total))
487 }
488}
489
490pub struct AllSubTilesIterator {
491 _tiles: Vec<Tile>,
492}
493
494impl AllSubTilesIterator {
495 pub fn new_from_tile(base_tile: &Tile) -> Self {
496 let new_tiles = match base_tile.subtiles() {
497 None => Vec::new(),
498 Some(t) => vec![t[0], t[1], t[2], t[3]],
499 };
500 AllSubTilesIterator { _tiles: new_tiles }
501 }
502}
503
504impl Iterator for AllSubTilesIterator {
505 type Item = Tile;
506
507 fn next(&mut self) -> Option<Tile> {
508 if self._tiles.is_empty() {
509 return None;
510 }
511 let next = self._tiles.remove(0);
512 if let Some(subtiles) = next.subtiles() {
513 self._tiles.extend_from_slice(&subtiles);
514 }
515 Some(next)
516 }
517}
518
519#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
521pub struct Metatile {
522 scale: u8,
523 zoom: u8,
524 x: u32,
525 y: u32,
526}
527
528impl Metatile {
529 pub fn new(scale: u8, zoom: u8, x: u32, y: u32) -> Option<Self> {
530 if !scale.is_power_of_two() {
531 return None;
532 }
533 if zoom >= 100 {
534 None
535 } else if x < 2u32.pow(zoom as u32) && y < 2u32.pow(zoom as u32) {
536 let s = scale as u32;
537 let x = (x / s) * s;
538 let y = (y / s) * s;
539 Some(Metatile {
540 scale: scale,
541 zoom: zoom,
542 x: x,
543 y: y,
544 })
545 } else {
546 None
547 }
548 }
549
550 pub fn scale(&self) -> u8 {
551 self.scale
552 }
553
554 pub fn zoom(&self) -> u8 {
555 self.zoom
556 }
557
558 pub fn size(&self) -> u8 {
561 let num_tiles_in_zoom = 2u32.pow(self.zoom as u32);
562 if num_tiles_in_zoom < (self.scale as u32) {
563 num_tiles_in_zoom as u8
564 } else {
565 self.scale
566 }
567 }
568
569 pub fn centre_point(&self) -> LatLon {
571 tile_nw_lat_lon(
572 self.zoom,
573 (self.x as f32) + (self.size() as f32) / 2.,
574 (self.y as f32) + (self.size() as f32) / 2.,
575 )
576 }
577
578 pub fn center_point(&self) -> LatLon {
580 self.centre_point()
581 }
582
583 pub fn nw_corner(&self) -> LatLon {
585 tile_nw_lat_lon(self.zoom, self.x as f32, self.y as f32)
586 }
587
588 pub fn ne_corner(&self) -> LatLon {
590 tile_nw_lat_lon(
591 self.zoom,
592 (self.x + self.size() as u32) as f32,
593 self.y as f32,
594 )
595 }
596
597 pub fn sw_corner(&self) -> LatLon {
599 tile_nw_lat_lon(
600 self.zoom,
601 self.x as f32,
602 (self.y + self.size() as u32) as f32,
603 )
604 }
605
606 pub fn se_corner(&self) -> LatLon {
608 tile_nw_lat_lon(
609 self.zoom,
610 (self.x + self.size() as u32) as f32,
611 (self.y + self.size() as u32) as f32,
612 )
613 }
614
615 pub fn x(&self) -> u32 {
617 self.x
618 }
619
620 pub fn y(&self) -> u32 {
622 self.y
623 }
624
625 pub fn tiles(&self) -> Vec<Tile> {
626 let size = self.size() as u32;
627 (0..(size * size))
628 .map(|n| {
629 let (i, j) = (n / size, n % size);
631 Tile {
633 zoom: self.zoom,
634 x: self.x + i,
635 y: self.y + j,
636 }
637 })
638 .collect()
639 }
640
641 pub fn all(scale: u8) -> MetatilesIterator {
642 assert!(scale.is_power_of_two());
643 MetatilesIterator::all(scale)
644 }
645}
646
647impl FromStr for Metatile {
648 type Err = ();
649
650 fn from_str(s: &str) -> Result<Self, Self::Err> {
651 lazy_static! {
652 static ref METATILE_RE: Regex = Regex::new(
653 "^(?P<scale>[0-9]+) (?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})$"
654 )
655 .unwrap();
656 }
657
658 let caps = METATILE_RE.captures(s);
659
660 if caps.is_none() {
661 return Err(());
662 }
663 let caps = caps.unwrap();
664
665 let scale = caps.name("scale").unwrap().as_str().parse().unwrap();
667 let zoom = caps.name("zoom").unwrap().as_str().parse().unwrap();
668 let x = caps.name("x").unwrap().as_str().parse().unwrap();
669 let y = caps.name("y").unwrap().as_str().parse().unwrap();
670
671 match Metatile::new(scale, zoom, x, y) {
672 None => {
673 Err(())
675 }
676 Some(mt) => Ok(mt),
677 }
678 }
679}
680
681#[derive(Debug)]
683pub struct MetatilesIterator {
684 scale: u8,
685 curr_zoom: u8,
686 maxzoom: u8,
687 curr_zorder: u64,
688 bbox: Option<BBox>,
689
690 curr_zoom_width_height: Option<(u32, u32)>,
692 curr_zoom_start_xy: Option<(u32, u32)>,
693
694 total: Option<usize>,
696 tile_list_file: Option<BufReader<File>>,
697}
698
699impl MetatilesIterator {
700 pub fn all(scale: u8) -> Self {
701 MetatilesIterator {
702 scale: scale,
703 curr_zoom: 0,
704 curr_zorder: 0,
705 bbox: None,
706 maxzoom: 32,
707 curr_zoom_width_height: None,
708 curr_zoom_start_xy: None,
709 total: None,
710 tile_list_file: None,
711 }
712 }
713
714 pub fn new_for_bbox(scale: u8, bbox: &BBox) -> Self {
715 MetatilesIterator::new_for_bbox_zoom(scale, &Some(bbox.clone()), 0, 32)
716 }
717
718 pub fn new_for_bbox_zoom(scale: u8, bbox: &Option<BBox>, minzoom: u8, maxzoom: u8) -> Self {
720 let mut it = MetatilesIterator {
721 scale: scale,
722 curr_zoom: minzoom,
723 curr_zorder: 0,
724 bbox: bbox.clone(),
725 maxzoom: maxzoom,
726 curr_zoom_width_height: None,
727 curr_zoom_start_xy: None,
728 total: None,
729 tile_list_file: None,
730 };
731 it.set_zoom_width_height();
732 it.set_zoom_start_xy();
733
734 it
735 }
736
737 pub fn new_from_filelist(filename: String) -> Self {
738 let mut file = BufReader::new(File::open(&filename).unwrap());
739 file.seek(SeekFrom::Start(0)).unwrap();
740
741 let total = file.lines().count();
744
745 let file = BufReader::new(File::open(filename).unwrap());
746
747 MetatilesIterator {
748 scale: 0,
749 curr_zoom: 0,
750 curr_zorder: 0,
751 bbox: None,
752 maxzoom: 0,
753 curr_zoom_width_height: None,
754 curr_zoom_start_xy: None,
755 total: Some(total),
756 tile_list_file: Some(file),
757 }
758 }
759
760 fn set_zoom_width_height(&mut self) {
763 if let Some(ref bbox) = self.bbox {
764 let scale = self.scale as u32;
765 let zoom = self.curr_zoom;
766 let (x1, y1) = lat_lon_to_tile(bbox.top, bbox.left, zoom);
768 let (x1, y1) = (x1 / scale, y1 / scale);
769 let (x2, y2) = lat_lon_to_tile(bbox.bottom, bbox.right, zoom);
770 let (x2, y2) = (x2 / scale, y2 / scale);
771
772 let width = x2 - x1 + 1;
773 let height = y2 - y1 + 1;
774
775 self.curr_zoom_width_height = Some((width, height));
776 }
777 }
778
779 fn set_zoom_start_xy(&mut self) {
780 if self.bbox.is_none() {
781 return;
782 }
783
784 let top = match self.bbox {
785 None => 90.,
786 Some(ref b) => b.top,
787 };
788 let left = match self.bbox {
789 None => -180.,
790 Some(ref b) => b.left,
791 };
792 let (x1, y1) = lat_lon_to_tile(top, left, self.curr_zoom);
794 self.curr_zoom_start_xy = Some((x1 / self.scale as u32, y1 / self.scale as u32));
795 }
796
797 fn next_from_zorder(&mut self) -> Option<Metatile> {
798 #[allow(unused_assignments)]
800 let mut zoom = 0;
801 #[allow(unused_assignments)]
802 let mut x = 0;
803 #[allow(unused_assignments)]
804 let mut y = 0;
805
806 let scale = self.scale as u32;
807
808 loop {
809 if self.curr_zoom > self.maxzoom {
810 return None;
812 }
813
814 zoom = self.curr_zoom;
815 let (width, height) = match self.curr_zoom_width_height {
816 None => {
817 let max_num = 2u32.pow(zoom as u32);
818 let mut max = max_num / scale;
819 if max_num % scale > 0 {
820 max += 1
821 }
822 (max, max)
823 }
824 Some((width, height)) => (width, height),
825 };
826
827 let max_zorder_for_zoom = xy_to_zorder(width - 1, height - 1);
828
829 let (i, j) = zorder_to_xy(self.curr_zorder);
830 let bits = match self.curr_zoom_start_xy {
831 None => (i, j),
832 Some(start) => (start.0 + i, start.1 + j),
833 };
834 x = bits.0;
835 y = bits.1;
836
837 if self.curr_zorder > max_zorder_for_zoom {
838 self.curr_zoom = zoom + 1;
841 self.curr_zorder = 0;
842 self.set_zoom_start_xy();
843 self.set_zoom_width_height();
844 } else if i > width || j > height {
845 self.curr_zorder += 1;
850 continue;
851 } else {
852 self.curr_zorder += 1;
854 break;
855 }
856 }
857
858 let (x, y) = (x * scale, y * scale);
859 Metatile::new(self.scale, zoom, x, y)
860 }
861
862 fn next_from_file(&mut self) -> Option<Metatile> {
863 let mut s = String::new();
864 if let Some(ref mut file) = self.tile_list_file {
865 file.read_line(&mut s).unwrap();
866 }
867 let s = s.trim_end();
869
870 s.parse().ok()
871 }
872
873 pub fn total(&self) -> Option<usize> {
874 self.total
875 }
876}
877
878impl Iterator for MetatilesIterator {
879 type Item = Metatile;
880
881 fn next(&mut self) -> Option<Self::Item> {
882 if self.tile_list_file.is_some() {
883 self.next_from_file()
884 } else {
885 self.next_from_zorder()
886 }
887 }
888}
889
890#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
892pub struct ModTileMetatile {
893 inner: Metatile,
894}
895
896impl ModTileMetatile {
897 pub fn new(zoom: u8, x: u32, y: u32) -> Option<Self> {
898 match Metatile::new(8, zoom, x, y) {
899 None => None,
900 Some(inner) => Some(ModTileMetatile { inner: inner }),
901 }
902 }
903
904 pub fn path<T: std::fmt::Display>(&self, ext: T) -> String {
906 let mt = xy_to_mt(self.inner.x, self.inner.y);
907 format!(
908 "{}/{}/{}/{}/{}/{}.{}",
909 self.inner.zoom, mt[0], mt[1], mt[2], mt[3], mt[4], ext
910 )
911 }
912
913 pub fn x(&self) -> u32 {
915 self.inner.x
916 }
917
918 pub fn y(&self) -> u32 {
920 self.inner.y
921 }
922
923 pub fn zoom(&self) -> u8 {
925 self.inner.zoom
926 }
927
928 pub fn size(self) -> u8 {
931 self.inner.size()
932 }
933}
934
935impl From<ModTileMetatile> for Metatile {
936 fn from(mt: ModTileMetatile) -> Self {
937 mt.inner
938 }
939}
940
941impl TryFrom<Metatile> for ModTileMetatile {
942 type Error = &'static str;
943
944 fn try_from(mt: Metatile) -> Result<Self, Self::Error> {
945 if mt.scale == 8 {
946 Ok(ModTileMetatile { inner: mt })
947 } else {
948 Err("Can only convert scale 8 metatiles into ModTileMetatile")
949 }
950 }
951}
952
953impl Borrow<Metatile> for ModTileMetatile {
954 fn borrow(&self) -> &Metatile {
955 &self.inner
956 }
957}
958
959impl Deref for ModTileMetatile {
960 type Target = Metatile;
961
962 fn deref(&self) -> &Metatile {
963 &self.inner
964 }
965}
966
967fn tile_nw_lat_lon(zoom: u8, x: f32, y: f32) -> LatLon {
968 let n: f32 = 2f32.powi(zoom as i32);
969 let lon_deg: f32 = (x as f32) / n * 360f32 - 180f32;
970 let lat_rad: f32 = ((1f32 - 2f32 * (y as f32) / n) * std::f32::consts::PI)
971 .sinh()
972 .atan();
973 let lat_deg: f32 = lat_rad * 180f32 * std::f32::consts::FRAC_1_PI;
974
975 LatLon::new(lat_deg, lon_deg).unwrap()
978}
979
980pub fn lat_lon_to_tile(lat: f32, lon: f32, zoom: u8) -> (u32, u32) {
982 #[allow(non_snake_case)]
984 let MAX_LAT: f64 = std::f64::consts::PI.sinh().atan();
985
986 let lat: f64 = lat as f64;
987 let lat = lat.to_radians();
988
989 let lon: f64 = lon as f64;
990
991 let lat = if lat > MAX_LAT {
993 MAX_LAT
994 } else if lat < -MAX_LAT {
995 -MAX_LAT
996 } else {
997 lat
998 };
999
1000 let n: f64 = 2f64.powi(zoom as i32);
1001 let xtile: u32 = (n * ((lon + 180.) / 360.)).trunc() as u32;
1002 let ytile: u32 = (n * (1. - ((lat.tan() + (1. / lat.cos())).ln() / std::f64::consts::PI)) / 2.)
1003 .trunc() as u32;
1004
1005 (xtile, ytile)
1006}
1007
1008pub fn merc_location_to_tile_coords(x: f64, y: f64, zoom: u8) -> ((u32, u32), (u32, u32)) {
1011 let num_tiles = 2u32.pow(zoom as u32) as f64;
1012 let global_extent = 20_037_508.342789244;
1013 let tile_width = (2. * global_extent) / num_tiles;
1014
1015 (
1016 (
1018 ((x + global_extent) / tile_width) as u32,
1019 ((y + global_extent) / tile_width) as u32,
1020 ),
1021 (
1023 (((x + global_extent) % tile_width) / tile_width * 256.) as u32,
1024 (num_tiles - ((y + global_extent) % tile_width) / tile_width * 256. - 1.) as u32,
1025 ),
1026 )
1027}
1028
1029pub fn size_bbox_zoom(bbox: &BBox, zoom: u8) -> Option<usize> {
1032 let top_left_tile = lat_lon_to_tile(bbox.top(), bbox.left(), zoom);
1033 let bottom_right_tile = lat_lon_to_tile(bbox.bottom(), bbox.right(), zoom);
1034 let height = (bottom_right_tile.0 - top_left_tile.0) as usize + 1;
1035 let width = (bottom_right_tile.1 - top_left_tile.1) as usize + 1;
1036
1037 height.checked_mul(width)
1038}
1039
1040pub fn size_bbox_zoom_metatiles(bbox: &BBox, zoom: u8, metatile_scale: u8) -> Option<usize> {
1044 let metatile_scale = metatile_scale as u32;
1045 let top_left_tile = lat_lon_to_tile(bbox.top(), bbox.left(), zoom);
1046 let bottom_right_tile = lat_lon_to_tile(bbox.bottom(), bbox.right(), zoom);
1047 let bottom = (bottom_right_tile.0 / metatile_scale) * metatile_scale;
1048 let top = (top_left_tile.0 / metatile_scale) * metatile_scale;
1049 let left = (top_left_tile.1 / metatile_scale) * metatile_scale;
1050 let right = (bottom_right_tile.1 / metatile_scale) * metatile_scale;
1051
1052 let height = ((bottom - top) / metatile_scale as u32) as usize + 1;
1053 let width = ((right - left) / metatile_scale as u32) as usize + 1;
1054
1055 height.checked_mul(width)
1056}
1057
1058#[derive(PartialEq, Debug, Clone)]
1063pub struct LatLon {
1064 lat: f32,
1065 lon: f32,
1066}
1067
1068impl LatLon {
1069 pub fn new(lat: f32, lon: f32) -> Option<LatLon> {
1072 if lat <= 90f32 && lat >= -90f32 && lon <= 180f32 && lon >= -180f32 {
1073 Some(LatLon { lat: lat, lon: lon })
1074 } else {
1075 None
1076 }
1077 }
1078
1079 pub fn lat(&self) -> f32 {
1081 self.lat
1082 }
1083 pub fn lon(&self) -> f32 {
1085 self.lon
1086 }
1087
1088 pub fn to_3857(&self) -> (f32, f32) {
1090 let x = self.lon() * 20037508.34 / 180.;
1091 let pi = std::f32::consts::PI;
1092 let y = ((90. + self.lat()) * pi / 360.).tan().ln() / (pi / 180.);
1093 let y = y * 20037508.34 / 180.;
1094
1095 (x, y)
1096 }
1097
1098 pub fn tile(&self, zoom: u8) -> Tile {
1100 let (x, y) = lat_lon_to_tile(self.lat, self.lon, zoom);
1101 Tile::new(zoom, x, y).unwrap()
1102 }
1103}
1104
1105#[derive(PartialEq, Debug, Clone)]
1107pub struct BBox {
1108 top: f32,
1109 left: f32,
1110 bottom: f32,
1111 right: f32,
1112}
1113
1114impl BBox {
1115 pub fn new(top: f32, left: f32, bottom: f32, right: f32) -> Option<BBox> {
1118 if top <= 90.
1124 && top >= -90.
1125 && bottom <= 90.
1126 && bottom >= -90.
1127 && left <= 180.
1128 && left >= -180.
1129 && right <= 180.
1130 && right >= -180.
1131 {
1132 Some(BBox {
1133 top: top,
1134 left: left,
1135 bottom: bottom,
1136 right: right,
1137 })
1138 } else {
1139 None
1140 }
1141 }
1142
1143 pub fn new_from_points(topleft: &LatLon, bottomright: &LatLon) -> BBox {
1145 BBox {
1146 top: topleft.lat,
1147 left: topleft.lon,
1148 bottom: bottomright.lat,
1149 right: bottomright.lon,
1150 }
1151 }
1152
1153 pub fn new_from_tile(tile: &Tile) -> Self {
1155 tile.bbox()
1156 }
1157
1158 pub fn contains_point(&self, point: &LatLon) -> bool {
1160 point.lat <= self.top
1161 && point.lat > self.bottom
1162 && point.lon >= self.left
1163 && point.lon < self.right
1164 }
1165
1166 pub fn overlaps_bbox(&self, other: &BBox) -> bool {
1168 self.left < other.right
1170 && self.right > other.left
1171 && self.top > other.bottom
1172 && self.bottom < other.top
1173 }
1174
1175 pub fn tiles(&self) -> BBoxTilesIterator {
1177 BBoxTilesIterator::new(&self)
1178 }
1179
1180 pub fn metatiles(&self, scale: u8) -> MetatilesIterator {
1182 let bbox: BBox = (*self).clone();
1183 MetatilesIterator {
1184 curr_zoom: 0,
1185 maxzoom: 32,
1186 bbox: Some(bbox),
1187 curr_zorder: 0,
1188 scale: scale,
1189 curr_zoom_width_height: None,
1190 curr_zoom_start_xy: None,
1191 total: None,
1192 tile_list_file: None,
1193 }
1194 }
1195
1196 pub fn top(&self) -> f32 {
1198 self.top
1199 }
1200
1201 pub fn bottom(&self) -> f32 {
1203 self.bottom
1204 }
1205
1206 pub fn left(&self) -> f32 {
1208 self.left
1209 }
1210
1211 pub fn right(&self) -> f32 {
1213 self.right
1214 }
1215
1216 pub fn tiles_for_zoom(&self, zoom: u8) -> impl Iterator<Item = Tile> {
1218 let top_left_tile = lat_lon_to_tile(self.top, self.left, zoom);
1219 let bottom_right_tile = lat_lon_to_tile(self.bottom, self.right, zoom);
1220
1221 (top_left_tile.0..=bottom_right_tile.0)
1222 .flat_map(move |x| {
1223 (top_left_tile.1..=bottom_right_tile.1)
1224 .map(move |y| (x, y))
1225 })
1226 .map(move |(x, y)| Tile::new(zoom, x, y).unwrap())
1227 }
1228
1229 pub fn centre_point(&self) -> LatLon {
1231 LatLon::new((self.top + self.bottom) / 2., (self.left + self.right) / 2.).unwrap()
1232 }
1233
1234 pub fn center_point(&self) -> LatLon {
1236 self.centre_point()
1237 }
1238
1239 pub fn nw_corner(&self) -> LatLon {
1241 LatLon::new(self.top, self.left).unwrap()
1242 }
1243
1244 pub fn ne_corner(&self) -> LatLon {
1246 LatLon::new(self.top, self.right).unwrap()
1247 }
1248
1249 pub fn sw_corner(&self) -> LatLon {
1251 LatLon::new(self.bottom, self.left).unwrap()
1252 }
1253
1254 pub fn se_corner(&self) -> LatLon {
1256 LatLon::new(self.bottom, self.right).unwrap()
1257 }
1258}
1259
1260impl FromStr for BBox {
1261 type Err = &'static str;
1262
1263 fn from_str(string: &str) -> Result<Self, Self::Err> {
1266 lazy_static! {
1267 static ref SIMPLE_COPY_SPACE: Regex = Regex::new(r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$").unwrap();
1269 static ref SIMPLE_COPY_COMMA: Regex = Regex::new(r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$").unwrap();
1270 }
1271 let caps = SIMPLE_COPY_SPACE
1272 .captures(string)
1273 .or_else(|| SIMPLE_COPY_COMMA.captures(string));
1274 if caps.is_none() {
1275 return Err("regex not match");
1276 }
1277 let caps = caps.unwrap();
1278
1279 let minlat = caps.name("minlat");
1280 let maxlat = caps.name("maxlat");
1281 let minlon = caps.name("minlon");
1282 let maxlon = caps.name("maxlon");
1283
1284 if minlat.is_none() || maxlat.is_none() || minlon.is_none() || maxlon.is_none() {
1285 return Err("bad lat/lon");
1286 }
1287
1288 let minlat = minlat.unwrap().as_str().parse();
1289 let maxlat = maxlat.unwrap().as_str().parse();
1290 let minlon = minlon.unwrap().as_str().parse();
1291 let maxlon = maxlon.unwrap().as_str().parse();
1292
1293 if minlat.is_err() || maxlat.is_err() || minlon.is_err() || maxlon.is_err() {
1294 return Err("bad lat/lon");
1295 }
1296
1297 let minlat = minlat.unwrap();
1298 let maxlat = maxlat.unwrap();
1299 let minlon = minlon.unwrap();
1300 let maxlon = maxlon.unwrap();
1301
1302 BBox::new(maxlat, minlon, minlat, maxlon).ok_or("bad lat/lon")
1303 }
1304}
1305
1306pub struct BBoxTilesIterator<'a> {
1307 bbox: &'a BBox,
1308 tiles: Vec<Tile>,
1309 tile_index: usize,
1310}
1311
1312impl<'a> BBoxTilesIterator<'a> {
1313 pub fn new(bbox: &'a BBox) -> BBoxTilesIterator<'a> {
1314 BBoxTilesIterator {
1316 bbox: bbox,
1317 tiles: vec![Tile::new(0, 0, 0).unwrap()],
1318 tile_index: 0,
1319 }
1320 }
1321}
1322
1323impl<'a> Iterator for BBoxTilesIterator<'a> {
1324 type Item = Tile;
1325
1326 fn next(&mut self) -> Option<Tile> {
1327 if self.tile_index >= self.tiles.len() {
1328 let mut new_tiles: Vec<Tile> = Vec::with_capacity(self.tiles.len() * 4);
1330 for t in self.tiles.iter() {
1331 match t.subtiles() {
1332 None => {}
1333 Some(sub) => {
1334 if self.bbox.overlaps_bbox(&sub[0].bbox()) {
1335 new_tiles.push(sub[0]);
1336 }
1337 if self.bbox.overlaps_bbox(&sub[1].bbox()) {
1338 new_tiles.push(sub[1]);
1339 }
1340 if self.bbox.overlaps_bbox(&sub[2].bbox()) {
1341 new_tiles.push(sub[2]);
1342 }
1343 if self.bbox.overlaps_bbox(&sub[3].bbox()) {
1344 new_tiles.push(sub[3]);
1345 }
1346 }
1347 }
1348 }
1349
1350 new_tiles.shrink_to_fit();
1351 self.tiles = new_tiles;
1352 self.tile_index = 0;
1353 }
1354
1355 let tile = self.tiles[self.tile_index];
1356 self.tile_index += 1;
1357 Some(tile)
1358 }
1359}
1360
1361fn xy_to_tc(x: u32, y: u32) -> [String; 6] {
1363 [
1364 format!("{:03}", x / 1_000_000),
1365 format!("{:03}", (x / 1_000) % 1_000),
1366 format!("{:03}", x % 1_000),
1367 format!("{:03}", y / 1_000_000),
1368 format!("{:03}", (y / 1_000) % 1_000),
1369 format!("{:03}", y % 1_000),
1370 ]
1371}
1372
1373fn xy_to_mp(x: u32, y: u32) -> [String; 4] {
1375 [
1376 format!("{:04}", x / 10_000),
1377 format!("{:04}", x % 10_000),
1378 format!("{:04}", y / 10_000),
1379 format!("{:04}", y % 10_000),
1380 ]
1381}
1382
1383fn xy_to_ts(x: u32, y: u32) -> [String; 4] {
1385 [
1386 format!("{:03}", x / 1_000),
1387 format!("{:03}", x % 1_000),
1388 format!("{:03}", y / 1_000),
1389 format!("{:03}", y % 1_000),
1390 ]
1391}
1392
1393fn xy_to_mt(x: u32, y: u32) -> [String; 5] {
1395 let mut x = x;
1399 let mut y = y;
1400
1401 let e = (((x & 0x0f) << 4) | (y & 0x0f)) as u8;
1402 x >>= 4;
1403 y >>= 4;
1404
1405 let d = (((x & 0x0f) << 4) | (y & 0x0f)) as u8;
1406 x >>= 4;
1407 y >>= 4;
1408
1409 let c = (((x & 0b000_1111 as u32) << 4) | (y & 0b000_1111 as u32)) as u8;
1410 x >>= 4;
1411 y >>= 4;
1412
1413 let b = (((x & 0b000_1111 as u32) << 4) | (y & 0b000_1111 as u32)) as u8;
1414 x >>= 4;
1415 y >>= 4;
1416
1417 let a = (((x & 0b000_1111 as u32) << 4) | (y & 0b000_1111 as u32)) as u8;
1418 [
1422 format!("{}", a),
1423 format!("{}", b),
1424 format!("{}", c),
1425 format!("{}", d),
1426 format!("{}", e),
1427 ]
1428}
1429
1430fn num_tiles_in_zoom(zoom: u8) -> Option<usize> {
1432 if zoom == 0 {
1434 Some(1)
1436 } else if zoom <= 5 {
1437 Some(2u64.pow(2u32.pow(zoom as u32)) as usize)
1438 } else {
1439 None
1440 }
1441}
1442
1443pub fn xy_to_zorder(x: u32, y: u32) -> u64 {
1444 let mut res: u64 = 0;
1445 for i in 0..32 {
1446 let x_set: bool = (x >> i) & 1 == 1;
1447 let y_set: bool = (y >> i) & 1 == 1;
1448 if x_set {
1449 res |= 1 << (i * 2);
1450 }
1451 if y_set {
1452 res |= 1 << (i * 2) + 1;
1453 }
1454 }
1455
1456 res
1457}
1458
1459pub fn zorder_to_xy(zorder: u64) -> (u32, u32) {
1460 let mut x: u32 = 0;
1461 let mut y: u32 = 0;
1462
1463 for i in 0..32 {
1464 let x_bit_set = (zorder >> (i * 2)) & 1 == 1;
1465 let y_bit_set = (zorder >> ((i * 2) + 1)) & 1 == 1;
1466
1467 if x_bit_set {
1468 x |= 1 << i;
1469 }
1470 if y_bit_set {
1471 y |= 1 << i;
1472 }
1473 }
1474
1475 (x, y)
1476}