A simple map viewer

coord.rs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. use std::f64::consts::{PI, FRAC_1_PI};
  2. use tile_source::TileSourceId;
  3. use cgmath::{Point3};
  4. /// A position in latitude, longitude.
  5. /// Values are in degrees and usually in these intervals:
  6. /// latitude: [-90.0, 90.0]
  7. /// longitude: [-180, 180.0]
  8. #[derive(Copy, Debug, PartialEq, Clone)]
  9. pub struct LatLon {
  10. pub lat: f64,
  11. pub lon: f64,
  12. }
  13. impl LatLon {
  14. pub fn new(lat: f64, lon: f64) -> Self {
  15. LatLon { lat, lon }
  16. }
  17. }
  18. //TODO Add more conversions from/to LatLonRad
  19. /// A position in latitude, longitude.
  20. /// Values are in radians and usually in these intervals:
  21. /// latitude: [-0.5 * π, 0.5 * π]
  22. /// longitude: [-π, π]
  23. #[derive(Copy, Debug, PartialEq, Clone)]
  24. pub struct LatLonRad {
  25. pub lat: f64,
  26. pub lon: f64,
  27. }
  28. impl LatLonRad {
  29. pub fn new(lat: f64, lon: f64) -> Self {
  30. LatLonRad { lat, lon }
  31. }
  32. pub fn to_sphere_xyz(&self, radius: f64) -> SphereXYZ {
  33. SphereXYZ {
  34. x: radius * self.lat.cos() * self.lon.cos(),
  35. y: radius * self.lat.sin(),
  36. z: radius * self.lat.cos() * self.lon.sin(),
  37. }
  38. }
  39. pub fn to_sphere_point3(&self, radius: f64) -> Point3<f32> {
  40. let p = self.to_sphere_xyz(radius);
  41. Point3::new(
  42. p.x as f32,
  43. p.y as f32,
  44. p.z as f32,
  45. )
  46. }
  47. }
  48. #[derive(Copy, Debug, PartialEq, Clone)]
  49. pub struct SphereXYZ {
  50. pub x: f64,
  51. pub y: f64,
  52. pub z: f64,
  53. }
  54. impl SphereXYZ {
  55. pub fn new(x: f64, y: f64, z: f64) -> Self {
  56. SphereXYZ { x, y, z }
  57. }
  58. }
  59. /// A position in map coordinates.
  60. /// Valid values for x and y lie in the interval [0.0, 1.0].
  61. #[derive(Copy, Debug, PartialEq, Clone)]
  62. pub struct MapCoord {
  63. pub x: f64,
  64. pub y: f64,
  65. }
  66. impl From<LatLon> for MapCoord
  67. {
  68. fn from(pos: LatLon) -> MapCoord {
  69. let x = pos.lon * (1.0 / 360.0) + 0.5;
  70. let pi_lat = pos.lat * (PI / 180.0);
  71. let y = f64::ln(f64::tan(pi_lat) + 1.0 / f64::cos(pi_lat)) * (-0.5 * FRAC_1_PI) + 0.5;
  72. MapCoord { x, y }
  73. }
  74. }
  75. impl MapCoord {
  76. pub fn new(x: f64, y: f64) -> MapCoord {
  77. MapCoord { x, y }
  78. }
  79. //TODO differ between normalized and not normalized tiles
  80. pub fn on_tile_at_zoom(&self, zoom: u32) -> TileCoord {
  81. let zoom_factor = f64::powi(2.0, zoom as i32);
  82. let x = (self.x * zoom_factor).floor() as i32;
  83. let y = (self.y * zoom_factor).floor() as i32;
  84. TileCoord { zoom, x, y }
  85. }
  86. pub fn normalize_x(&mut self) {
  87. // Wrap around in x-direction.
  88. // Do not wrap around in y-direction. The poles don't touch.
  89. self.x = (self.x.fract() + 1.0).fract();
  90. }
  91. pub fn normalize_xy(&mut self) {
  92. // Wrap around in x-direction.
  93. // Restrict y coordinates to interval [0.0, 1.0]
  94. self.x = (self.x.fract() + 1.0).fract();
  95. self.y = 0.0f64.max(1.0f64.min(self.y));
  96. }
  97. pub fn to_latlon_rad(&self) -> LatLonRad {
  98. LatLonRad {
  99. lat: (PI - self.y * (2.0 * PI)).sinh().atan(),
  100. lon: self.x * (2.0 * PI) - PI,
  101. }
  102. }
  103. }
  104. /// A position on the screen in pixels. Top-left corner is (0.0, 0.0).
  105. #[derive(Copy, Clone, Debug)]
  106. pub struct ScreenCoord {
  107. pub x: f64,
  108. pub y: f64,
  109. }
  110. impl ScreenCoord {
  111. pub fn new(x: f64, y: f64) -> Self {
  112. ScreenCoord { x, y }
  113. }
  114. pub fn snap_to_pixel(&mut self) {
  115. self.x = self.x.floor();
  116. self.y = self.y.floor();
  117. }
  118. pub fn is_inside(&self, rect: &ScreenRect) -> bool {
  119. self.x >= rect.x &&
  120. self.y >= rect.y &&
  121. self.x < rect.x + rect.width &&
  122. self.y < rect.y + rect.height
  123. }
  124. }
  125. /// A rectangle in screen coordinates.
  126. #[derive(Copy, Clone, Debug)]
  127. pub struct ScreenRect {
  128. pub x: f64,
  129. pub y: f64,
  130. pub width: f64,
  131. pub height: f64,
  132. }
  133. impl ScreenRect {
  134. pub fn subdivide(&self, sub_tile: &SubTileCoord) -> ScreenRect {
  135. let scale = 1.0 / f64::from(sub_tile.size);
  136. let w = self.width * scale;
  137. let h = self.height * scale;
  138. ScreenRect {
  139. x: self.x + f64::from(sub_tile.x) * w,
  140. y: self.y + f64::from(sub_tile.y) * h,
  141. width: w,
  142. height: h,
  143. }
  144. }
  145. }
  146. /// A rectangle in texture coordinates.
  147. /// Top-left corner is (0.0, 0.0).
  148. /// Bottom-right corner is (1.0, 1.0).
  149. #[derive(Copy, Clone, Debug)]
  150. pub struct TextureRect {
  151. pub x1: f64,
  152. pub y1: f64,
  153. pub x2: f64,
  154. pub y2: f64,
  155. }
  156. impl TextureRect {
  157. pub fn inset(self, margin_x: f64, margin_y: f64) -> TextureRect {
  158. TextureRect {
  159. x1: self.x1 + margin_x,
  160. y1: self.y1 + margin_y,
  161. x2: self.x2 - margin_x,
  162. y2: self.y2 - margin_y,
  163. }
  164. }
  165. pub fn subdivide(&self, sub_tile: &SubTileCoord) -> TextureRect {
  166. let scale = 1.0 / f64::from(sub_tile.size);
  167. let w = (self.x2 - self.x1) * scale;
  168. let h = (self.y2 - self.y1) * scale;
  169. TextureRect {
  170. x1: self.x1 + f64::from(sub_tile.x) * w,
  171. y1: self.y1 + f64::from(sub_tile.y) * h,
  172. x2: self.x1 + f64::from(sub_tile.x + 1) * w,
  173. y2: self.y1 + f64::from(sub_tile.y + 1) * h,
  174. }
  175. }
  176. }
  177. /// A subdivision of a tile. `x` and `y` are in the interval [0, `size` - 1].
  178. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
  179. pub struct SubTileCoord {
  180. pub size: u32,
  181. pub x: u32,
  182. pub y: u32,
  183. }
  184. impl SubTileCoord {
  185. pub fn subdivide(&self, other: &SubTileCoord) -> SubTileCoord {
  186. SubTileCoord {
  187. x: self.x * other.size + other.x,
  188. y: self.y * other.size + other.y,
  189. size: self.size * other.size,
  190. }
  191. }
  192. }
  193. /// A tile position in a tile pyramid.
  194. /// Each zoom level has 2<sup>zoom</sup> by 2<sup>zoom</sup> tiles.
  195. /// `x` and `y` are allowed to be negative or >= 2<sup>zoom</sup> but then they will not correspond to a tile
  196. /// and `is_on_planet` will return false.
  197. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
  198. pub struct TileCoord {
  199. pub zoom: u32,
  200. pub x: i32,
  201. pub y: i32,
  202. }
  203. impl TileCoord {
  204. pub fn new(zoom: u32, x: i32, y: i32) -> TileCoord {
  205. TileCoord {
  206. zoom,
  207. x: Self::normalize_coord(x, zoom),
  208. y,
  209. }
  210. }
  211. pub fn is_on_planet(&self) -> bool {
  212. let num_tiles = Self::get_zoom_level_tiles(self.zoom);
  213. self.y >= 0 && self.y < num_tiles &&
  214. self.x >= 0 && self.x < num_tiles
  215. }
  216. // Return the MapCoord of the top left corner of the current tile.
  217. pub fn map_coord_north_west(&self) -> MapCoord {
  218. let inv_zoom_factor = f64::powi(2.0, -(self.zoom as i32));
  219. MapCoord::new(f64::from(self.x) * inv_zoom_factor, f64::from(self.y) * inv_zoom_factor)
  220. }
  221. // Return the LatLonRad coordinate of the top left corner of the current tile.
  222. pub fn latlon_rad_north_west(&self) -> LatLonRad {
  223. let factor = f64::powi(2.0, -(self.zoom as i32)) * (2.0 * PI);
  224. LatLonRad::new(
  225. (PI - f64::from(self.y) * factor).sinh().atan(),
  226. f64::from(self.x) * factor - PI,
  227. )
  228. }
  229. // Return the LatLonRad coordinate of the bottom right corner of the current tile.
  230. pub fn latlon_rad_south_east(&self) -> LatLonRad {
  231. TileCoord { zoom: self.zoom, x: self.x + 1, y: self.y + 1 }.latlon_rad_north_west()
  232. }
  233. // Return the MapCoord of the center of the current tile.
  234. pub fn map_coord_center(&self) -> MapCoord {
  235. let inv_zoom_factor = f64::powi(2.0, -(self.zoom as i32));
  236. MapCoord::new(
  237. (f64::from(self.x) + 0.5) * inv_zoom_factor,
  238. (f64::from(self.y) + 0.5) * inv_zoom_factor,
  239. )
  240. }
  241. pub fn parent(&self, distance: u32) -> Option<(TileCoord, SubTileCoord)> {
  242. if distance > self.zoom {
  243. None
  244. } else {
  245. let scale = u32::pow(2, distance);
  246. Some((
  247. TileCoord {
  248. zoom: self.zoom - distance,
  249. x: self.x / scale as i32,
  250. y: self.y / scale as i32,
  251. },
  252. SubTileCoord {
  253. size: scale,
  254. x: (Self::normalize_coord(self.x, self.zoom) as u32) % scale,
  255. y: (Self::normalize_coord(self.y, self.zoom) as u32) % scale,
  256. },
  257. ))
  258. }
  259. }
  260. pub fn children(&self) -> [(TileCoord, SubTileCoord); 4] {
  261. [
  262. (
  263. TileCoord {
  264. zoom: self.zoom + 1,
  265. x: self.x * 2,
  266. y: self.y * 2,
  267. },
  268. SubTileCoord {
  269. size: 2,
  270. x: 0,
  271. y: 0,
  272. },
  273. ),
  274. (
  275. TileCoord {
  276. zoom: self.zoom + 1,
  277. x: self.x * 2 + 1,
  278. y: self.y * 2,
  279. },
  280. SubTileCoord {
  281. size: 2,
  282. x: 1,
  283. y: 0,
  284. },
  285. ),
  286. (
  287. TileCoord {
  288. zoom: self.zoom + 1,
  289. x: self.x * 2,
  290. y: self.y * 2 + 1,
  291. },
  292. SubTileCoord {
  293. size: 2,
  294. x: 0,
  295. y: 1,
  296. },
  297. ),
  298. (
  299. TileCoord {
  300. zoom: self.zoom + 1,
  301. x: self.x * 2 + 1,
  302. y: self.y * 2 + 1,
  303. },
  304. SubTileCoord {
  305. size: 2,
  306. x: 1,
  307. y: 1,
  308. },
  309. ),
  310. ]
  311. }
  312. #[inline]
  313. fn normalize_coord(coord: i32, zoom: u32) -> i32 {
  314. let max = Self::get_zoom_level_tiles(zoom);
  315. ((coord % max) + max) % max
  316. }
  317. #[inline]
  318. pub fn get_zoom_level_tiles(zoom: u32) -> i32 {
  319. //TODO throw error when zoom too big
  320. i32::pow(2, zoom)
  321. }
  322. pub fn to_quadkey(&self) -> Option<String> {
  323. if self.zoom == 0 || self.zoom > 30 || self.x < 0 || self.y < 0 {
  324. return None;
  325. }
  326. let mut quadkey = String::with_capacity(self.zoom as usize);
  327. let len = self.zoom;
  328. for i in (0..len).rev() {
  329. let mask: u32 = 1 << i;
  330. match ((self.x as u32 & mask) != 0, (self.y as u32 & mask) != 0) {
  331. (false, false) => quadkey.push('0'),
  332. (true, false) => quadkey.push('1'),
  333. (false, true) => quadkey.push('2'),
  334. (true, true) => quadkey.push('3'),
  335. }
  336. }
  337. Some(quadkey)
  338. }
  339. }
  340. //TODO include width and height of view rect to determine visibility
  341. #[derive(Copy, Clone, Debug, PartialEq)]
  342. pub struct View {
  343. pub source_id: TileSourceId,
  344. pub zoom: u32,
  345. pub center: MapCoord,
  346. }
  347. #[cfg(test)]
  348. mod tests {
  349. use coord::*;
  350. #[test]
  351. fn normalize_mapcoord() {
  352. {
  353. let a = MapCoord::new(0.0, 0.0);
  354. let mut b = a.clone();
  355. assert_eq!(a, b);
  356. b.normalize_x();
  357. assert_eq!(a, b);
  358. }
  359. {
  360. let mut a = MapCoord::new(1.0, 1.0);
  361. let b = MapCoord::new(0.0, 1.0);
  362. a.normalize_x();
  363. assert_eq!(a, b);
  364. }
  365. }
  366. #[test]
  367. fn quadkey() {
  368. assert_eq!(TileCoord::new(0, 0, 0).to_quadkey(), None);
  369. assert_eq!(TileCoord::new(1, 0, 0).to_quadkey(), Some("0".to_string()));
  370. assert_eq!(TileCoord::new(1, 1, 0).to_quadkey(), Some("1".to_string()));
  371. assert_eq!(TileCoord::new(1, 0, 1).to_quadkey(), Some("2".to_string()));
  372. assert_eq!(TileCoord::new(1, 1, 1).to_quadkey(), Some("3".to_string()));
  373. assert_eq!(TileCoord::new(3, 1, 0).to_quadkey(), Some("001".to_string()));
  374. assert_eq!(TileCoord::new(30, 0, 1).to_quadkey(), Some("000000000000000000000000000002".to_string()));
  375. }
  376. }