forked from asciipip/TopOSM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tileexpire.py
133 lines (112 loc) · 4.41 KB
/
tileexpire.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
#!/usr/bin/python
"""
OSMTileExpire, a class for managing tile expirations in a memory efficient
way.
"""
__author__ = "Phil! Gold <[email protected]>"
__copyright__ = "waived; see license"
__license__ = "CC0: http://creativecommons.org/publicdomain/zero/1.0/"
# Mapping from hilbert subcurve to child subscripts in order of processing.
hilbert_order = {
0: [0, 2, 3, 1],
1: [3, 2, 0, 1],
2: [3, 1, 0, 2],
3: [0, 1, 3, 2]
}
# For each hilbert subcurve, gives the subcurve of the next level down for each
# child. Order of next subcurve number matches child subscripts, so
# [ul, ur, ll, lr].
hilbert_next = {
0: [3, 1, 0, 0],
1: [1, 0, 1, 2],
2: [2, 2, 3, 1],
3: [0, 3, 2, 3]
}
class OSMTileExpire:
"""
Manages tile expirations.
To instantiate, simply create with no arguments:
expiredTiles = OSMTileExpire()
Use the expire() method to mark individual tiles as expired:
expiredTiles.expire(15, 16384, 16384)
Marking a given tile as expired will also mark every tile below it and
every tile above it as expired, too. Thus, the above code will also
expire 14/8192/8192, 16/32768/32768, 16/32769/32768, 16/32768/32769,
and 16/32769/32769, among others.
To list all expired tiles at a given zoom level, use the expiredAt()
method:
for t in expiredTiles.expiredAt(13):
# Do something here.
"""
def __init__(self, myz=0, myx=0, myy=0):
self.z = myz
self.x = myx
self.y = myy
self.children = [None, None, None, None]
self.full = False
def expire(self, targetz, targetx, targety):
"""Mark the supplied tile as expired."""
if targetz < self.z:
return False
elif self.full:
return True
elif targetz == self.z:
self.markFull()
return True
xoff = (targetx >> (targetz - self.z - 1)) - (self.x << 1)
yoff = (targety >> (targetz - self.z - 1)) - (self.y << 1)
if xoff > 1 or 0 > xoff:
raise Exception(
'z:{0}, x:{1} out of bounds for tile z:{2}, x:{3}, y{4}'.format(
targetz, targetx, self.z, self.x, self.y))
if yoff > 1 or 0 > yoff:
raise Exception(
'z:{0}, y:{1} out of bounds for tile z:{2}, x:{3}, y{4}'.format(
targetz, targety, self.z, self.x, self.y))
i = 2*yoff + xoff
if not self.children[i]:
self.children[i] = OSMTileExpire(
self.z + 1, self.x * 2 + xoff, self.y * 2 + yoff)
child_full = self.children[i].expire(targetz, targetx, targety)
if child_full and self.checkFull():
self.markFull()
return True
return False
def checkFull(self):
return self.full or (all(self.children) and all([c.full for c in self.children]))
def markFull(self):
self.full = True
self.children = [None, None, None, None]
def countExpiredAt(self, targetz):
if targetz < self.z:
raise Exception('Cannot count expired tiles at zoom {0}, which is lower than my zoom {1}'.format(targetz, self.z))
if targetz == self.z:
return 1
if self.full:
return 4 ** (targetz - self.z)
return sum([c.countExpiredAt(targetz) for c in self.children if c])
def expiredAt(self, targetz):
"""Yield (as a generator) all the expired tiles at the given zoom."""
return self._expiredAt(targetz, 0)
def _expiredAt(self, targetz, hilbert_curve):
if targetz < self.z:
raise Exception('zoom {0} is lower than this zoom, {1}'.format(targetz, self.z))
elif targetz == self.z:
yield (self.x, self.y)
elif self.full:
for t in enumeratePoints(self.x, self.y, targetz - self.z, hilbert_curve):
yield t
else:
for hi in hilbert_order[hilbert_curve]:
if self.children[hi]:
for t in self.children[hi]._expiredAt(targetz, hilbert_next[hilbert_curve][hi]):
yield t
def enumeratePoints(x, y, n, hilbert_curve):
if n == 0:
yield (x, y)
else:
for hi in hilbert_order[hilbert_curve]:
x1 = 2 * x + hi % 2
y1 = 2 * y + hi / 2
for t in enumeratePoints(x1, y1, n - 1, hilbert_next[hilbert_curve][hi]):
yield t