From 9cd9de22aaf4d51aca5dd93cb6a42ec86c3c88fa Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Fri, 1 Dec 2023 14:07:51 +0100 Subject: [PATCH] fix git history --- .bumpversion.cfg | 2 +- CHANGES.md | 6 +- morecantile/__init__.py | 2 +- morecantile/models.py | 161 +++++++++++++++++++++++-------- morecantile/scripts/cli.py | 12 ++- tests/test_tms_variable_width.py | 85 ++++++++++++++-- 6 files changed, 218 insertions(+), 50 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 3de4e01..45d82cc 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 5.0.0 +current_version = 5.0.1 commit = True tag = True tag_name = {new_version} diff --git a/CHANGES.md b/CHANGES.md index 39bf694..ed60858 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,12 @@ -## 5.0.1 (2023-12-01) +## 5.0.2 (2023-12-01) * Remove *alias* tiles in `.parent()`, `.children()`, `.neighbors()` and `.tiles()` methods for Variable Matrix Width TileMatrixSets (https://github.com/developmentseed/morecantile/pull/136) +## 5.0.1 (2023-12-01) [DELETED] + +* ~~Remove *alias* tiles in `.parent()`, `.children()`, `.neighbors()` and `.tiles()` methods for Variable Matrix Width TileMatrixSets (https://github.com/developmentseed/morecantile/pull/136)~~ + ## 5.0.0 (2023-07-24) * update pydantic requirement to `~=2.0` diff --git a/morecantile/__init__.py b/morecantile/__init__.py index d69a583..8ba52f5 100644 --- a/morecantile/__init__.py +++ b/morecantile/__init__.py @@ -8,7 +8,7 @@ """ -__version__ = "5.0.0" +__version__ = "5.0.1" from .commons import BoundingBox, Coords, Tile # noqa from .defaults import TileMatrixSets, tms # noqa diff --git a/morecantile/models.py b/morecantile/models.py index 5274772..2f40d72 100644 --- a/morecantile/models.py +++ b/morecantile/models.py @@ -835,7 +835,13 @@ def truncate_lnglat(self, lng: float, lat: float) -> Tuple[float, float]: return lng, lat - def _tile(self, xcoord: float, ycoord: float, zoom: int) -> Tile: + def _tile( + self, + xcoord: float, + ycoord: float, + zoom: int, + ignore_coalescence: bool = True, + ) -> Tile: """ Get the tile containing a Point (in TMS CRS). @@ -867,21 +873,37 @@ def _tile(self, xcoord: float, ycoord: float, zoom: int) -> Tile: ) # avoid out-of-range tiles - if xtile < 0: - xtile = 0 - if ytile < 0: ytile = 0 + if ytile >= matrix.matrixHeight: + ytile = matrix.matrixHeight - 1 + + if xtile < 0: + xtile = 0 + if xtile >= matrix.matrixWidth: xtile = matrix.matrixWidth - 1 - if ytile >= matrix.matrixHeight: - ytile = matrix.matrixHeight - 1 + if not ignore_coalescence: + cf = ( + matrix.get_coalesce_factor(ytile) + if matrix.variableMatrixWidths is not None + else 1 + ) + if cf != 1 and xtile % cf: + xtile -= xtile % cf return Tile(x=xtile, y=ytile, z=zoom) - def tile(self, lng: float, lat: float, zoom: int, truncate=False) -> Tile: + def tile( + self, + lng: float, + lat: float, + zoom: int, + truncate=False, + ignore_coalescence: bool = False, + ) -> Tile: """ Get the tile for a given geographic longitude and latitude pair. @@ -900,7 +922,7 @@ def tile(self, lng: float, lat: float, zoom: int, truncate=False) -> Tile: """ x, y = self.xy(lng, lat, truncate=truncate) - return self._tile(x, y, zoom) + return self._tile(x, y, zoom, ignore_coalescence=ignore_coalescence) def _ul(self, *tile: Tile) -> Coords: """ @@ -921,10 +943,11 @@ def _ul(self, *tile: Tile) -> Coords: res = self._resolution(matrix) origin_x, origin_y = self._matrix_origin(matrix) - cf = 1 - if matrix.variableMatrixWidths is not None: - cf = matrix.get_coalesce_factor(t.y) - + cf = ( + matrix.get_coalesce_factor(t.y) + if matrix.variableMatrixWidths is not None + else 1 + ) return Coords( origin_x + math.floor(t.x / cf) * res * cf * matrix.tileWidth, origin_y - t.y * res * matrix.tileHeight, @@ -949,10 +972,11 @@ def _lr(self, *tile: Tile) -> Coords: res = self._resolution(matrix) origin_x, origin_y = self._matrix_origin(matrix) - cf = 1 - if matrix.variableMatrixWidths is not None: - cf = matrix.get_coalesce_factor(t.y) - + cf = ( + matrix.get_coalesce_factor(t.y) + if matrix.variableMatrixWidths is not None + else 1 + ) return Coords( origin_x + (math.floor(t.x / cf) + 1) * res * cf * matrix.tileWidth, origin_y - (t.y + 1) * res * matrix.tileHeight, @@ -1068,7 +1092,7 @@ def intersect_tms(self, bbox: BoundingBox) -> bool: and (bbox[1] < tms_bounds[3]) ) - def tiles( + def tiles( # noqa: C901 self, west: float, south: float, @@ -1127,17 +1151,34 @@ def tiles( for z in zooms: nw_tile = self.tile( - w + LL_EPSILON, n - LL_EPSILON, z + w + LL_EPSILON, + n - LL_EPSILON, + z, + ignore_coalescence=True, ) # Not in mercantile - se_tile = self.tile(e - LL_EPSILON, s + LL_EPSILON, z) + se_tile = self.tile( + e - LL_EPSILON, + s + LL_EPSILON, + z, + ignore_coalescence=True, + ) minx = min(nw_tile.x, se_tile.x) maxx = max(nw_tile.x, se_tile.x) miny = min(nw_tile.y, se_tile.y) maxy = max(nw_tile.y, se_tile.y) - for i in range(minx, maxx + 1): - for j in range(miny, maxy + 1): + matrix = self.matrix(z) + for j in range(miny, maxy + 1): + cf = ( + matrix.get_coalesce_factor(j) + if matrix.variableMatrixWidths is not None + else 1 + ) + for i in range(minx, maxx + 1): + if cf != 1 and i % cf: + continue + yield Tile(i, j, z) def feature( @@ -1326,9 +1367,9 @@ def is_valid(self, *tile: Tile) -> bool: if t.z < self.minzoom: return False - extrema = self.minmax(t.z) - validx = extrema["x"]["min"] <= t.x <= extrema["x"]["max"] - validy = extrema["y"]["min"] <= t.y <= extrema["y"]["max"] + matrix = self.matrix(t.z) + validx = 0 <= t.x <= matrix.matrixWidth - 1 + validy = 0 <= t.y <= matrix.matrixHeight - 1 return validx and validy @@ -1352,22 +1393,48 @@ def neighbors(self, *tile: Tile) -> List[Tile]: """ t = _parse_tile_arg(*tile) - extrema = self.minmax(t.z) + matrix = self.matrix(t.z) + x = t.x + y = t.y - tiles = [] - for i in [-1, 0, 1]: - for j in [-1, 0, 1]: - if i == 0 and j == 0: - continue - elif t.x + i < extrema["x"]["min"] or t.y + j < extrema["y"]["min"]: - continue + tiles = set() + + miny = max(0, y - 1) + maxy = min(y + 1, matrix.matrixHeight - 1) + + cf = ( + matrix.get_coalesce_factor(y) + if matrix.variableMatrixWidths is not None + else 1 + ) - elif t.x + i > extrema["x"]["max"] or t.y + j > extrema["y"]["max"]: + if cf != 1: + if x % cf: + x -= x % cf + minx = max(0, x - (x % cf) - 1) + maxx = min(x + (x % cf) + cf, matrix.matrixWidth - 1) + + else: + minx = max(0, x - 1) + maxx = min(x + 1, matrix.matrixWidth - 1) + + for ytile in range(miny, maxy + 1): + cf = ( + matrix.get_coalesce_factor(ytile) + if matrix.variableMatrixWidths is not None + else 1 + ) + for xtile in range(minx, maxx + 1): + nx = xtile + if cf != 1 and nx % cf: + nx = nx - nx % cf + + if nx == x and ytile == y: continue - tiles.append(Tile(x=t.x + i, y=t.y + j, z=t.z)) + tiles.add(Tile(x=nx, y=ytile, z=t.z)) - return tiles + return sorted(tiles) def parent(self, *tile: Tile, zoom: int = None): """Get the parent of a tile @@ -1406,8 +1473,16 @@ def parent(self, *tile: Tile, zoom: int = None): lr_tile = self._tile(bbox.right - res, bbox.bottom + res, target_zoom) tiles = [] - for i in range(ul_tile.x, lr_tile.x + 1): - for j in range(ul_tile.y, lr_tile.y + 1): + matrix = self.matrix(target_zoom) + for j in range(ul_tile.y, lr_tile.y + 1): + cf = ( + matrix.get_coalesce_factor(j) + if matrix.variableMatrixWidths is not None + else 1 + ) + for i in range(ul_tile.x, lr_tile.x + 1): + if cf != 1 and i % cf: + continue tiles.append(Tile(i, j, target_zoom)) return tiles @@ -1445,8 +1520,16 @@ def children(self, *tile: Tile, zoom: int = None): lr_tile = self._tile(bbox.right - res, bbox.bottom + res, target_zoom) tiles = [] - for i in range(ul_tile.x, lr_tile.x + 1): - for j in range(ul_tile.y, lr_tile.y + 1): + matrix = self.matrix(target_zoom) + for j in range(ul_tile.y, lr_tile.y + 1): + cf = ( + matrix.get_coalesce_factor(j) + if matrix.variableMatrixWidths is not None + else 1 + ) + for i in range(ul_tile.x, lr_tile.x + 1): + if cf != 1 and i % cf: + continue tiles.append(Tile(i, j, target_zoom)) return tiles diff --git a/morecantile/scripts/cli.py b/morecantile/scripts/cli.py index f2944f3..939a09a 100644 --- a/morecantile/scripts/cli.py +++ b/morecantile/scripts/cli.py @@ -534,8 +534,16 @@ def tms_to_geojson( # noqa: C901 col_xs = [] col_ys = [] - for x in range(0, matrix.matrixWidth): - for y in range(0, matrix.matrixHeight): + for y in range(0, matrix.matrixHeight): + cf = ( + matrix.get_coalesce_factor(y) + if matrix.variableMatrixWidths is not None + else 1 + ) + for x in range(0, matrix.matrixWidth): + if cf != 1 and x % cf: + continue + feature = tms.feature( (x, y, level), projected=projected, diff --git a/tests/test_tms_variable_width.py b/tests/test_tms_variable_width.py index 3e4d2ee..ad7b739 100644 --- a/tests/test_tms_variable_width.py +++ b/tests/test_tms_variable_width.py @@ -104,11 +104,84 @@ def test_gnosisg(): tiles = gnosisg_tms.tiles(-180, -90, 180, 90, [0]) assert len(list(tiles)) == 8 - tiles = gnosisg_tms.tiles(-180, -90, 180, 90, [1]) - assert len(list(tiles)) == 32 + ############################# + # CHECK WE DON'T HAVE ALIASES + tiles = list(gnosisg_tms.tiles(-180, -90, 180, 90, [1])) + assert len(tiles) == 24 + assert Tile(1, 0, 1) not in tiles + # make sure the aliased tiles are not added assert len(gnosisg_tms.parent(Tile(0, 0, 1))) == 1 - assert len(gnosisg_tms.parent(Tile(0, 0, 2))) == 2 - assert len(gnosisg_tms.parent(Tile(0, 0, 3))) == 4 - - assert len(gnosisg_tms.children(Tile(0, 0, 0), zoom=1)) == 4 + assert len(gnosisg_tms.parent(Tile(0, 0, 2))) == 1 + assert len(gnosisg_tms.parent(Tile(0, 0, 3))) == 1 + assert len(gnosisg_tms.children(Tile(0, 0, 0), zoom=1)) == 3 + assert len(gnosisg_tms.children(Tile(0, 0, 0), zoom=2)) == 11 + assert len(gnosisg_tms.children(Tile(0, 1, 1), zoom=2)) == 4 + + # test neighbors + tiles = gnosisg_tms.neighbors(Tile(0, 0, 1)) + assert tiles == [ + Tile(x=0, y=1, z=1), + Tile(x=1, y=1, z=1), + Tile(x=2, y=0, z=1), + Tile(x=2, y=1, z=1), + ] + + tiles = gnosisg_tms.neighbors(Tile(2, 0, 1)) + assert tiles == [ + Tile(x=0, y=0, z=1), + Tile(x=1, y=1, z=1), + Tile(x=2, y=1, z=1), + Tile(x=3, y=1, z=1), + Tile(x=4, y=0, z=1), + Tile(x=4, y=1, z=1), + ] + + tiles = gnosisg_tms.neighbors(Tile(6, 0, 1)) + assert tiles == [ + Tile(x=4, y=0, z=1), + Tile(x=5, y=1, z=1), + Tile(x=6, y=1, z=1), + Tile(x=7, y=1, z=1), + ] + + tiles = gnosisg_tms.neighbors(Tile(0, 1, 1)) + assert tiles == [ + Tile(x=0, y=0, z=1), + Tile(x=0, y=2, z=1), + Tile(x=1, y=1, z=1), + Tile(x=1, y=2, z=1), + ] + + tiles = gnosisg_tms.neighbors(Tile(3, 1, 1)) + assert tiles == [ + Tile(x=2, y=0, z=1), + Tile(x=2, y=1, z=1), + Tile(x=2, y=2, z=1), + Tile(x=3, y=2, z=1), + Tile(x=4, y=0, z=1), + Tile(x=4, y=1, z=1), + Tile(x=4, y=2, z=1), + ] + + tiles = gnosisg_tms.neighbors(Tile(0, 3, 1)) + assert tiles == [ + Tile(x=0, y=2, z=1), + Tile(x=1, y=2, z=1), + Tile(x=2, y=2, z=1), + Tile(x=2, y=3, z=1), + ] + + # assert alias tile have the same neighbors + assert gnosisg_tms.neighbors(Tile(0, 0, 1)) == gnosisg_tms.neighbors(Tile(1, 0, 1)) + + assert gnosisg_tms.tile(-180, 90, 2) == Tile(0, 0, 2) + assert gnosisg_tms.tile(-150, 90, 2) == Tile(0, 0, 2) + assert gnosisg_tms.tile(-80, 90, 2) == Tile(4, 0, 2) + assert gnosisg_tms.tile(-180, -90, 2) == Tile(0, 7, 2) + assert gnosisg_tms.tile(-150, -90, 2) == Tile(0, 7, 2) + assert gnosisg_tms.tile(-80, -90, 2) == Tile(4, 7, 2) + + # Ignore coalescence and return alias + assert gnosisg_tms.tile(-150, 90, 2, ignore_coalescence=True) == Tile(1, 0, 2) + assert gnosisg_tms.tile(150, -90, 2, ignore_coalescence=True) == Tile(14, 7, 2)