| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | |
|
| | 4 | | namespace Itinero.Network.Tiles; |
| | 5 | |
|
| | 6 | | internal static class TileStatic |
| | 7 | | { |
| | 8 | | private static void ValidateBox(this ((double longitude, double latitude, float? e) topLeft, |
| | 9 | | (double longitude, double latitude, float? e) bottomRight) box) |
| 38 | 10 | | { |
| 38 | 11 | | if (box.topLeft.latitude < box.bottomRight.latitude) |
| 0 | 12 | | { |
| 0 | 13 | | throw new ArgumentOutOfRangeException($"Top is lower than bottom."); |
| | 14 | | } |
| 38 | 15 | | } |
| | 16 | |
|
| | 17 | | public static (uint x, uint y) ToTile(int zoom, uint tileId) |
| 1934 | 18 | | { |
| 1934 | 19 | | var xMax = (ulong)(1 << zoom); |
| | 20 | |
|
| 1934 | 21 | | return ((uint)(tileId % xMax), (uint)(tileId / xMax)); |
| 1934 | 22 | | } |
| | 23 | |
|
| | 24 | | public static uint ToLocalId(uint x, uint y, int zoom) |
| 469 | 25 | | { |
| 469 | 26 | | var xMax = 1 << (int)zoom; |
| 469 | 27 | | return (uint)((y * xMax) + x); |
| 469 | 28 | | } |
| | 29 | |
|
| | 30 | | public static uint ToLocalId(double longitude, double latitude, int zoom) |
| 38 | 31 | | { |
| 38 | 32 | | var (x, y) = WorldToTile(longitude, latitude, zoom); |
| 38 | 33 | | return ToLocalId(x, y, zoom); |
| 38 | 34 | | } |
| | 35 | |
|
| | 36 | | public static (int x, int y) ToLocalTileCoordinates(int zoom, uint tileId, double longitude, double latitude, |
| | 37 | | int resolution) |
| 788 | 38 | | { |
| 788 | 39 | | var tile = ToTile(zoom, tileId); |
| | 40 | |
|
| 788 | 41 | | var n = Math.PI - (2.0 * Math.PI * tile.y / Math.Pow(2.0, zoom)); |
| 788 | 42 | | var left = (tile.x / Math.Pow(2.0, zoom) * 360.0) - 180.0; |
| 788 | 43 | | var top = 180.0 / Math.PI * Math.Atan(Math.Sinh(n)); |
| | 44 | |
|
| 788 | 45 | | n = Math.PI - (2.0 * Math.PI * (tile.y + 1) / Math.Pow(2.0, zoom)); |
| 788 | 46 | | var right = ((tile.x + 1) / Math.Pow(2.0, zoom) * 360.0) - 180.0; |
| 788 | 47 | | var bottom = 180.0 / Math.PI * Math.Atan(Math.Sinh(n)); |
| | 48 | |
|
| 788 | 49 | | var latStep = (top - bottom) / resolution; |
| 788 | 50 | | var lonStep = (right - left) / resolution; |
| | 51 | |
|
| 788 | 52 | | return ((int)((longitude - left) / lonStep), (int)((top - latitude) / latStep)); |
| 788 | 53 | | } |
| | 54 | |
|
| | 55 | | public static void FromLocalTileCoordinates(int zoom, uint tileId, int x, int y, int resolution, |
| | 56 | | out double longitude, out double latitude) |
| 1146 | 57 | | { |
| 1146 | 58 | | var tile = ToTile(zoom, tileId); |
| | 59 | |
|
| 1146 | 60 | | var n = Math.PI - (2.0 * Math.PI * tile.y / Math.Pow(2.0, zoom)); |
| 1146 | 61 | | var left = (tile.x / Math.Pow(2.0, zoom) * 360.0) - 180.0; |
| 1146 | 62 | | var top = 180.0 / Math.PI * Math.Atan(Math.Sinh(n)); |
| | 63 | |
|
| 1146 | 64 | | n = Math.PI - (2.0 * Math.PI * (tile.y + 1) / Math.Pow(2.0, zoom)); |
| 1146 | 65 | | var right = ((tile.x + 1) / Math.Pow(2.0, zoom) * 360.0) - 180.0; |
| 1146 | 66 | | var bottom = 180.0 / Math.PI * Math.Atan(Math.Sinh(n)); |
| | 67 | |
|
| 1146 | 68 | | var latStep = (top - bottom) / resolution; |
| 1146 | 69 | | var lonStep = (right - left) / resolution; |
| | 70 | |
|
| 1146 | 71 | | longitude = left + (lonStep * x); |
| 1146 | 72 | | latitude = top - (y * latStep); |
| 1146 | 73 | | } |
| | 74 | |
|
| | 75 | | private static int N(int zoom) |
| 527 | 76 | | { |
| 527 | 77 | | return (int)Math.Floor(Math.Pow(2, zoom)); // replace by bit shifting? |
| 527 | 78 | | } |
| | 79 | |
|
| | 80 | | public static (uint x, uint y) WorldToTile(double longitude, double latitude, int zoom) |
| 489 | 81 | | { |
| 489 | 82 | | var n = N(zoom); |
| 489 | 83 | | var rad = latitude / 180d * Math.PI; |
| | 84 | |
|
| 489 | 85 | | var x = (uint)((longitude + 180.0f) / 360.0f * n); |
| 489 | 86 | | var y = (uint)( |
| 489 | 87 | | (1.0f - (Math.Log(Math.Tan(rad) + (1.0f / Math.Cos(rad))) |
| 489 | 88 | | / Math.PI)) / 2f * n); |
| | 89 | |
|
| 489 | 90 | | return (x, y); |
| 489 | 91 | | } |
| | 92 | |
|
| | 93 | | public static IEnumerable<(uint x, uint y)> TileRange( |
| | 94 | | this ((double longitude, double latitude, float? e) topLeft, |
| | 95 | | (double longitude, double latitude, float? e) bottomRight) box, int zoom) |
| 38 | 96 | | { |
| 38 | 97 | | box.ValidateBox(); |
| | 98 | |
|
| 38 | 99 | | var n = N(zoom); |
| 38 | 100 | | var topLeft = WorldToTile(box.topLeft.longitude, box.topLeft.latitude, zoom); |
| 38 | 101 | | var bottomRight = WorldToTile(box.bottomRight.longitude, box.bottomRight.latitude, zoom); |
| | 102 | |
|
| 38 | 103 | | var x = topLeft.x; |
| 38 | 104 | | var y = topLeft.y; |
| 71 | 105 | | while (true) |
| 71 | 106 | | { |
| 71 | 107 | | yield return (x, y); |
| | 108 | |
|
| 50 | 109 | | if (y == bottomRight.y) |
| 26 | 110 | | { |
| | 111 | | // move on with x. |
| 26 | 112 | | if (x == bottomRight.x) |
| 17 | 113 | | { |
| | 114 | | // both x and y have reached the end. |
| 17 | 115 | | break; |
| | 116 | | } |
| | 117 | |
|
| | 118 | | // reset y. |
| 9 | 119 | | y = topLeft.y; |
| | 120 | |
|
| | 121 | | // move on with x. |
| 9 | 122 | | x++; |
| 9 | 123 | | if (x == n) |
| 0 | 124 | | { |
| 0 | 125 | | x = 0; |
| 0 | 126 | | } |
| | 127 | |
|
| 9 | 128 | | continue; |
| | 129 | | } |
| | 130 | |
|
| | 131 | | // move on with y. |
| 24 | 132 | | y++; |
| 24 | 133 | | } |
| 17 | 134 | | } |
| | 135 | | } |