-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTileSystem.py
166 lines (139 loc) · 5.24 KB
/
TileSystem.py
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
"""
Adaptation en Python d'après fichier microsoft initialement écrit en C# par Joe Schwartz
30/05/2020
David Bomard
"""
from math import cos, pi, sin, log, atan, exp
EARTH_RADIUS = 6378137
MIN_LATITUDE = -85.05112878
MAX_LATITUDE = 85.05112878
MIN_LONGITUDE = -180
MAX_LONGITUDE = 180
def Clip(n, min_value, max_value):
"""
clips a number to the specified minimum and maximum values
:param n: the value to clip
:param min_value: Minimum allowable value
:param max_value: Maximum allowable value
:return: The clipped value
"""
return min(max(n, min_value), max_value)
def MapSize(level_of_detail):
"""
Determines the map width and height (in pixels) at a specified level of detail.
:param level_of_detail:Level of detail, from 1 (lowest detail) to 23 (highest detail)
:return: The map width and height in pixels
"""
return 256 << level_of_detail
def GroundResolution(latitude, level_of_detail):
"""
Determines the ground resolution (in meters per pixel) at a specified
latitude and level of detail
:param latitude: Latitude (in degrees) at which to measure the
ground resolution
:param level_of_detail: Level of detail, from 1 (lowest detail)
to 23 (highest detail)
:return: The ground resolution, in meters per pixel
"""
latitude = Clip(latitude, MIN_LATITUDE, MAX_LATITUDE)
return cos(latitude * pi / 180) * 2 * pi * EARTH_RADIUS / MapSize(level_of_detail)
def LatLongToPixelXY(latitude, longitude, level_of_detail):
"""
Converts a point from latitude/longitude WGS-84 coordinates (in degrees)
into pixel XY coordinates at a specified level of detail
:param latitude: Latitude of the point, in degrees
:param longitude: Longitude of the point, in degrees
:param level_of_detail:Level of detail, from 1 (lowest detail)
to 23 (highest detail)
:return: a tuple (the X coordinate in pixels, the Y coordinate in pixels)
"""
latitude = Clip(latitude, MIN_LATITUDE, MAX_LATITUDE)
longitude = Clip(longitude, MIN_LONGITUDE, MAX_LONGITUDE)
x = (longitude + 180) / 360
sin_latitude = sin(latitude * pi / 180)
y = 0.5 - log((1 + sin_latitude) / (1 - sin_latitude)) / (4 * pi)
map_size = MapSize(level_of_detail)
pixel_x = int(Clip(x * map_size + 0.5, 0, map_size - 1))
pixel_y = int(Clip(y * map_size + 0.5, 0, map_size - 1))
return pixel_x, pixel_y
def PixelXYToLatLong(pixel_x, pixel_y, level_of_detail):
"""
Converts a pixel from pixel XY coordinates at a specified level of detail
into latitude/longitude WGS-84 coordinates (in degrees
:param pixel_x: X coordinate of the point, in pixels
:param pixel_y: Y coordinates of the point, in pixels
:param level_of_detail: Level of detail, from 1 (lowest detail)
to 23 (highest detail)
:return: a tuple (latitude in degrees, longitude in degrees)
"""
map_size = MapSize(level_of_detail)
x = (Clip(pixel_x, 0, map_size - 1) / map_size) - 0.5
y = 0.5 - (Clip(pixel_y, 0, map_size - 1) / map_size)
latitude = 90 - 360 * atan(exp(-y * 2 * pi)) / pi
longitude = 360 * x
return latitude, longitude
def PixelXYToTileXY(pixel_x, pixel_y):
"""
Converts pixel XY coordinates into tile XY coordinates of the tile containing
the specified pixel
:param pixel_x: Pixel X coordinate
:param pixel_y: Pixel X coordinate
:return: tuple (tile x coordinate, tile y coordinate
"""
tile_x = int(pixel_x / 256)
tile_y = int(pixel_y / 256)
return tile_x, tile_y
def TileXYToPixelXY(tile_x, tile_y):
"""
Converts tile XY coordinates into pixel XY coordinates of the upper-left pixel
of the specified tile
:param tile_x: Tile X coordinate
:param tile_y: Tile Y coordinate
:return: tuple (pixel x coordinate, pixel y coordinate)
"""
pixel_x = tile_x * 256
pixel_y = tile_y * 256
return pixel_x, pixel_y
def TileXYToQuadKey(tile_x, tile_y, level_of_detail):
"""
Converts tile XY coordinates into a QuadKey at a specified level of detail
:param tile_x: Tile X coordinate
:param tile_y: Tile Y coordinate
:param level_of_detail: Level of detail, from 1 (lowest detail)
to 23 (highest detail)
:return: A string containing the QuadKey
"""
quadkey = ''
for i in range(level_of_detail, 0, -1):
digit = 0
mask = 1 << (i - 1)
if (tile_x & mask) != 0:
digit += 1
if (tile_y & mask) != 0:
digit += 2
quadkey = quadkey + str(digit)
return quadkey
def QuadKeyToTileXY(quad_key):
"""
Converts a QuadKey into tile XY coordinates
:param quad_key: QuadKey of the tile
:return: tuple (tile X coordinate, tile Y coordinate, level of detail)
"""
tile_x = 0
tile_y = 0
level_of_detail = len(quad_key)
for i in range(level_of_detail, 0, -1):
mask = 1 << (i - 1)
switcher = quad_key[level_of_detail - i]
if switcher == '0':
break
elif switcher == '1':
tile_x |= mask
elif switcher == '2':
tile_y |= mask
elif switcher == '3':
tile_x |= mask
tile_y |= mask
else:
raise ValueError('Invalid Quadkey digit sequence')
return tile_x, tile_y, level_of_detail