Commit 2483c2d8 authored by Jgiannelos's avatar Jgiannelos
Browse files

Fix parent/children tile traversal

* When input tile zoom is in maxzoom we only care about traversing parents until root
* When input tile zoom is between 0 and maxzoom we traverse both parents (up to the root) and children up to maxzoom
* When input tile zoom is 0 we traverse only children up to maxzoom
* If a tile is already discovered stop recursion
parent e3e84d40
......@@ -17,8 +17,9 @@ class TestTile(TestCase):
class TestTileSet(TestCase):
@mock.patch("tileset.TileSet.add")
def test_tileset_read(self, mock_add):
@mock.patch("tileset.TileSet.addChildren")
@mock.patch("tileset.TileSet.addParent")
def test_tileset_read(self, mock_addParent, mock_addChildren):
test_input = ["15/5/5", "15/12/12"]
test_input_file = "\n".join(test_input)
......@@ -27,8 +28,27 @@ class TestTileSet(TestCase):
tileset = TileSet("/path/to/tilelist", 15)
tileset.read()
call_list = [mock.call(Tile(15, 5, 5)), mock.call(Tile(15, 12, 12))]
mock_add.assert_has_calls(call_list)
parent_list = [mock.call(Tile(14, 2, 2)), mock.call(Tile(14, 6, 6))]
mock_addParent.assert_has_calls(parent_list)
children_list = [
mock.call(
[
Tile(z=16, x=10, y=10),
Tile(z=16, x=11, y=10),
Tile(z=16, x=10, y=11),
Tile(z=16, x=11, y=11),
]
),
mock.call(
[
Tile(z=16, x=24, y=24),
Tile(z=16, x=25, y=24),
Tile(z=16, x=24, y=25),
Tile(z=16, x=25, y=25),
]
),
]
mock_addChildren.assert_has_calls(children_list)
def test_parse_entry(self):
entry = "4/15/100"
......@@ -40,7 +60,7 @@ class TestTileSet(TestCase):
parsed = TileSet.parse(entry)
self.assertEqual(parsed, Tile(4, 15, 100))
def test_tileset_add_single(self):
def test_tileset_add_single_minzoom(self):
test_input = ["0/0/0"]
test_input_file = "\n".join(test_input)
......@@ -50,7 +70,7 @@ class TestTileSet(TestCase):
tileset.read()
self.assertEqual(len(tileset.tileset), 5)
def test_tileset_add_multiple(self):
def test_tileset_add_multiple_minzoom(self):
test_input = ["0/0/0", "0/1/1"]
test_input_file = "\n".join(test_input)
......@@ -60,7 +80,7 @@ class TestTileSet(TestCase):
tileset.read()
self.assertEqual(len(tileset.tileset), 10)
def test_tileset_add_multiple_overlapping(self):
def test_tileset_add_multiple_overlapping_minzoom(self):
test_input = ["0/0/0", "1/0/0"]
test_input_file = "\n".join(test_input)
......@@ -69,3 +89,63 @@ class TestTileSet(TestCase):
tileset = TileSet("/path/to/tilelist", 1)
tileset.read()
self.assertEqual(len(tileset.tileset), 5)
def test_tileset_add_single_z_between_maxzoom(self):
test_input = ["2/0/0"]
test_input_file = "\n".join(test_input)
mock_open = mock.mock_open(read_data=test_input_file)
with mock.patch("builtins.open", mock_open):
tileset = TileSet("/path/to/tilelist", 3)
tileset.read()
self.assertEqual(len(tileset.tileset), 7)
def test_tileset_add_multiple_z_between_maxzoom(self):
test_input = ["2/0/0", "2/10/10"]
test_input_file = "\n".join(test_input)
mock_open = mock.mock_open(read_data=test_input_file)
with mock.patch("builtins.open", mock_open):
tileset = TileSet("/path/to/tilelist", 3)
tileset.read()
self.assertEqual(len(tileset.tileset), 14)
def test_tileset_add_multiple_overlapping_z_between_maxzoom(self):
test_input = ["2/0/0", "3/0/0"]
test_input_file = "\n".join(test_input)
mock_open = mock.mock_open(read_data=test_input_file)
with mock.patch("builtins.open", mock_open):
tileset = TileSet("/path/to/tilelist", 3)
tileset.read()
self.assertEqual(len(tileset.tileset), 7)
def test_tileset_add_single_maxzoom(self):
test_input = ["3/0/0"]
test_input_file = "\n".join(test_input)
mock_open = mock.mock_open(read_data=test_input_file)
with mock.patch("builtins.open", mock_open):
tileset = TileSet("/path/to/tilelist", 3)
tileset.read()
self.assertEqual(len(tileset.tileset), 4)
def test_tileset_add_multiple_maxzoom(self):
test_input = ["3/0/0", "3/10/10"]
test_input_file = "\n".join(test_input)
mock_open = mock.mock_open(read_data=test_input_file)
with mock.patch("builtins.open", mock_open):
tileset = TileSet("/path/to/tilelist", 3)
tileset.read()
self.assertEqual(len(tileset.tileset), 8)
def test_tileset_add_multiple_overlapping_maxzoom(self):
test_input = ["3/0/0", "3/1/1"]
test_input_file = "\n".join(test_input)
mock_open = mock.mock_open(read_data=test_input_file)
with mock.patch("builtins.open", mock_open):
tileset = TileSet("/path/to/tilelist", 3)
tileset.read()
self.assertEqual(len(tileset.tileset), 5)
from math import floor
from dataclasses import dataclass
......@@ -7,6 +8,7 @@ class Tile:
x: int
y: int
@property
def children(self):
"""Calculate the children tiles of a tile"""
return [
......@@ -16,6 +18,11 @@ class Tile:
Tile(self.z + 1, self.x * 2 + 1, self.y * 2 + 1),
]
@property
def parent(self):
"""Calculate the parent tile of a tile"""
return Tile(self.z - 1, floor(self.x / 2), floor(self.y / 2))
class TileSet:
def __init__(self, tilelistPath, maxzoom):
......@@ -30,19 +37,28 @@ class TileSet:
(z, x, y) = map(int, entry.split("/"))
return Tile(z, x, y)
def add(self, tile):
"""Add tile in tileset"""
if tile.z > self.maxzoom:
def addParent(self, tile):
"""Add parent tile in the tileset"""
if tile.z < 0 or tile in self.tileset:
return
self.tileset.add(tile)
self.addParent(tile.parent)
for child in tile.children():
self.add(child)
def addChildren(self, tiles):
"""Add children tiles in the tileset"""
for tile in tiles:
if tile.z > self.maxzoom or tile in self.tileset:
return
self.tileset.add(tile)
self.addChildren(tile.children)
def read(self):
"""Read tilelist as input"""
with open(self.tilelistPath, "r") as f:
for line in f.readlines():
tile = self.parse(line)
self.add(tile)
self.addParent(tile.parent)
self.addChildren(tile.children)
self.tileset.add(tile)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment