diff --git a/README.md b/README.md index f3ad4dc..830e1b5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Converts a lat,lon to pixel space to tile space to a quadkey import quadkey qk = quadkey.from_geo((-105, 40), 17) - print qk.key # => 02310101232121212 + print(qk.key) # => 02310101232121212 assert qk.level is 17 tile = qk.to_tile() # => [(x, y), z] @@ -24,7 +24,7 @@ Not a lot of documentation here, but the implementation has quite a bit, so look Install ------- -The package on pypi is quadtweet, so the recommended installation is with pip +The package on PyPI is quadkey, so the recommended installation is with pip pip install quadkey @@ -38,16 +38,19 @@ There are many straightforward methods, so I'll only go into detail of the uniqu * is_ancestor() * is_descendent() * area() +* side() +* nearby() * to_geo() * to_tile() +* to_pixel() -####difference(to) +#### difference(to) Gets the quadkeys between self and to forming a rectangle, inclusive. qk.difference(to) -> [qk,,,,,to] -####unwind() +#### unwind() Gets a list of all ancestors in descending order by level, inclusive. diff --git a/quadkey/__init__.py b/quadkey/__init__.py index 6e9e3da..e43ec23 100644 --- a/quadkey/__init__.py +++ b/quadkey/__init__.py @@ -1,5 +1,5 @@ -from util import precondition -from tile_system import TileSystem, valid_key +from .util import precondition +from .tile_system import TileSystem, valid_key LAT_STR = 'lat' LON_STR = 'lon' @@ -27,7 +27,7 @@ def nearby(self): perms = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] tiles = set( - map(lambda perm: (abs(tile[0] + perm[0]), abs(tile[1] + perm[1])), perms)) + [(abs(tile[0] + perm[0]), abs(tile[1] + perm[1])) for perm in perms]) return [TileSystem.tile_to_quadkey(tile, level) for tile in tiles] def is_ancestor(self, node): @@ -48,44 +48,50 @@ def is_descendent(self, node): """ return node.is_ancestor(self) + def side(self): + return 256 * TileSystem.ground_resolution(0, self.level) + def area(self): - size = TileSystem.map_size(self.level) - LAT = 0 - res = TileSystem.ground_resolution(LAT, self.level) - side = (size / 2) * res + side = self.side() return side * side - def xdifference(self, to): + @staticmethod + def xdifference(first, second): """ Generator Gives the difference of quadkeys between self and to Generator in case done on a low level Only works with quadkeys of same level """ - x,y = 0,1 - assert self.level == to.level - self_tile = list(self.to_tile()[0]) - to_tile = list(to.to_tile()[0]) - if self_tile[x] >= to_tile[x] and self_tile[y] <= self_tile[y]: - ne_tile, sw_tile = self_tile, to_tile - else: - sw_tile, ne_tile = self_tile, to_tile - cur = ne_tile[:] - while cur[x] >= sw_tile[x]: - while cur[y] <= sw_tile[y]: - yield from_tile(tuple(cur), self.level) - cur[y] += 1 + x, y = 0, 1 + assert first.level == second.level + self_tile = list(first.to_tile()[0]) + to_tile = list(second.to_tile()[0]) + se, sw, ne, nw = None, None, None, None + if self_tile[x] >= to_tile[x] and self_tile[y] <= to_tile[y]: + ne, sw = self_tile, to_tile + elif self_tile[x] <= to_tile[x] and self_tile[y] >= to_tile[y]: + sw, ne = self_tile, to_tile + elif self_tile[x] <= to_tile[x] and self_tile[y] <= to_tile[y]: + nw, se = self_tile, to_tile + elif self_tile[x] >= to_tile[x] and self_tile[y] >= to_tile[y]: + se, nw = self_tile, to_tile + cur = ne[:] if ne else se[:] + while cur[x] >= (sw[x] if sw else nw[x]): + while (sw and cur[y] <= sw[y]) or (nw and cur[y] >= nw[y]): + yield from_tile(tuple(cur), first.level) + cur[y] += 1 if sw else -1 cur[x] -= 1 - cur[y] = ne_tile[y] + cur[y] = ne[y] if ne else se[y] def difference(self, to): """ Non generator version of xdifference """ - return [qk for qk in self.xdifference(to)] + return [qk for qk in self.xdifference(self, to)] def unwind(self): """ Get a list of all ancestors in descending order of level, including a new instance of self """ - return [ QuadKey(self.key[:l+1]) for l in reversed(range(len(self.key))) ] + return [ QuadKey(self.key[:l+1]) for l in reversed(list(range(len(self.key)))) ] def to_tile(self): return TileSystem.quadkey_to_tile(self.key) @@ -98,11 +104,14 @@ def to_geo(self, centered=False): return TileSystem.pixel_to_geo(pixel, lvl) def __eq__(self, other): - return self.key == other.key + return isinstance(other, QuadKey) and self.key == other.key def __ne__(self, other): return not self.__eq__(other) + def __lt__(self, other): + return self.key.__lt__(other.key) + def __str__(self): return self.key diff --git a/quadkey/tile_system.py b/quadkey/tile_system.py index 918b66a..b3c4a8e 100644 --- a/quadkey/tile_system.py +++ b/quadkey/tile_system.py @@ -1,4 +1,4 @@ -from util import precondition +from .util import precondition from math import sin, cos, atan, exp, log, pi @@ -84,7 +84,7 @@ def pixel_to_geo(pixel, level): @staticmethod def pixel_to_tile(pixel): """Transform pixel to tile coordinates""" - return pixel[0] / 256, pixel[1] / 256 + return int(pixel[0] / 256), int(pixel[1] / 256) @staticmethod def tile_to_pixel(tile, centered=False): @@ -99,10 +99,10 @@ def tile_to_pixel(tile, centered=False): @precondition(lambda tile, lvl: valid_level(lvl)) def tile_to_quadkey(tile, level): """Transform tile coordinates to a quadkey""" - tile_x = tile[0] - tile_y = tile[1] + tile_x = int(tile[0]) + tile_y = int(tile[1]) quadkey = "" - for i in xrange(level): + for i in range(level): bit = level - i digit = ord('0') mask = 1 << (bit - 1) # if (bit - 1) > 0 else 1 >> (bit - 1) @@ -118,7 +118,7 @@ def quadkey_to_tile(quadkey): """Transform quadkey to tile coordinates""" tile_x, tile_y = (0, 0) level = len(quadkey) - for i in xrange(level): + for i in range(level): bit = level - i mask = 1 << (bit - 1) if quadkey[level - bit] == '1': diff --git a/tests/quadkey_tests.py b/tests/quadkey_tests.py index 1aab9ee..cbb2c06 100644 --- a/tests/quadkey_tests.py +++ b/tests/quadkey_tests.py @@ -28,7 +28,7 @@ def testChildren(self): qk = quadkey.from_str('0') self.assertEqual( [c.key for c in qk.children()], ['00', '01', '02', '03']) - qk = quadkey.from_str(''.join(['0' for x in xrange(23)])) + qk = quadkey.from_str(''.join(['0' for x in range(23)])) self.assertEqual(qk.children(), []) def testAncestry(self): @@ -61,5 +61,22 @@ def testDifference(self): self.assertEqual(diff, set([qk.key for qk in _to.difference(_from)])) self.assertEqual(diff, set([qk.key for qk in _from.difference(_to)])) + def testDifference2(self): + qk1 = quadkey.QuadKey('033') + qk2 = quadkey.QuadKey('003') + diff = [qk.key for qk in qk1.difference(qk2)] + self.assertEqual(set(diff), set(['033', '031', '013', '032', '030', '012', '023', '021', '003'])) + def testDifference3(self): + qk1 = quadkey.QuadKey('021') + qk2 = quadkey.QuadKey('011') + diff = [qk.key for qk in qk1.difference(qk2)] + self.assertEqual(set(diff), set(['011', '013', '031', '010', '012', '030', '001', '003', '021'])) + def testSide(self): + qk = quadkey.QuadKey(''.join(['0'] * 10)) + self.assertEqual(int(qk.side()), 39135) + + def testArea(self): + qk = quadkey.QuadKey(''.join(['0'] * 10)) + self.assertEqual(int(qk.area()), 1531607591) \ No newline at end of file diff --git a/tests/tile_system.py b/tests/tile_system.py index 62305e4..a88c7fe 100644 --- a/tests/tile_system.py +++ b/tests/tile_system.py @@ -26,7 +26,7 @@ def testMapScale(self): geo = (40., -105.) level = 7 dpi = 96 - scale = 3540913.0290224836 + scale = 3540913.029022482 self.assertEqual(scale, TileSystem.map_scale(geo[0], level, dpi)) def testGeoToPixel(self):