Skip to content

Commit

Permalink
Merge pull request #140 from developmentseed/feature/use-cellsize-for…
Browse files Browse the repository at this point in the history
…-resolution

use TileMatrix cellSize instead of resolution calculation
  • Loading branch information
vincentsarago authored Jan 8, 2024
2 parents 4b12d22 + 9a332c3 commit 6cfb280
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 44 deletions.
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@

## 5.1.0 (2023-12-21)

* Simplify bounds calculation by using `TileMatrix.cellSize` instead of `TileMatrix.scaleDenominator`
* remove `TileMatrixSet._resolution` private method

## 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)
Expand Down
2 changes: 1 addition & 1 deletion morecantile/data/WebMercatorQuad.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,4 @@
"matrixHeight": 16777216
}
]
}
}
43 changes: 16 additions & 27 deletions morecantile/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ def from_v1(cls, tms: Dict) -> "TileMatrixSet":
mpu = meters_per_unit(CRS.from_user_input(v2_tms["crs"]))
for i in range(len(v2_tms["tileMatrices"])):
v2_tms["tileMatrices"][i]["cellSize"] = (
v2_tms["tileMatrices"][i]["scaleDenominator"] * 0.00028 / mpu
v2_tms["tileMatrices"][i]["scaleDenominator"] * 0.28e-3 / mpu
)
v2_tms["tileMatrices"][i]["pointOfOrigin"] = v2_tms["tileMatrices"][i].pop(
"topLeftCorner"
Expand All @@ -553,6 +553,7 @@ def custom(
id: Optional[str] = None,
ordered_axes: Optional[List[str]] = None,
geographic_crs: CRS = WGS84_CRS,
screen_pixel_size: float = 0.28e-3,
**kwargs: Any,
):
"""
Expand Down Expand Up @@ -585,6 +586,8 @@ def custom(
Tile Matrix Set identifier
geographic_crs: pyproj.CRS
Geographic (lat,lon) coordinate reference system (default is EPSG:4326)
screen_pixel_size: float, optional
Rendering pixel size. 0.28 mm was the actual pixel size of a common display from 2005 and considered as standard by OGC.
kwargs: Any
Attributes to forward to the TileMatrixSet
Expand Down Expand Up @@ -621,7 +624,7 @@ def custom(
TileMatrix(
**{
"id": str(zoom),
"scaleDenominator": res * mpu / 0.00028,
"scaleDenominator": res * mpu / screen_pixel_size,
"cellSize": res,
"pointOfOrigin": [x_origin, y_origin],
"tileWidth": tile_width,
Expand Down Expand Up @@ -705,17 +708,6 @@ def matrix(self, zoom: int) -> TileMatrix:

return tile_matrix

def _resolution(self, matrix: TileMatrix) -> float:
"""
Tile resolution for a TileMatrix.
From note g in http://docs.opengeospatial.org/is/17-083r2/17-083r2.html#table_2:
The pixel size of the tile can be obtained from the scaleDenominator
by multiplying the later by 0.28 10-3 / metersPerUnit.
"""
return matrix.scaleDenominator * 0.28e-3 / meters_per_unit(self.crs._pyproj_crs)

def _matrix_origin(self, matrix: TileMatrix) -> Coords:
"""Return the Origin coordinates of the matrix."""
origin_x = (
Expand Down Expand Up @@ -760,7 +752,7 @@ def zoom_for_res(

# Freely adapted from https://github.com/OSGeo/gdal/blob/dc38aa64d779ecc45e3cd15b1817b83216cf96b8/gdal/frmts/gtiff/cogdriver.cpp#L272-L305
for zoom_level in range(min_z, max_z + 1):
matrix_res = self._resolution(self.matrix(zoom_level))
matrix_res = self.matrix(zoom_level).cellSize
if res > matrix_res or abs(res - matrix_res) / matrix_res <= 1e-8:
break

Expand All @@ -772,7 +764,7 @@ def zoom_for_res(
zoom_level = min(zoom_level, max_z)

elif zoom_level_strategy.lower() == "auto":
if (self._resolution(self.matrix(max(zoom_level - 1, min_z))) / res) < (
if (self.matrix(max(zoom_level - 1, min_z)).cellSize / res) < (
res / matrix_res
):
zoom_level = max(zoom_level - 1, min_z)
Expand Down Expand Up @@ -858,16 +850,15 @@ def _tile(
"""
matrix = self.matrix(zoom)
res = self._resolution(matrix)
origin_x, origin_y = self._matrix_origin(matrix)

xtile = (
math.floor((xcoord - origin_x) / float(res * matrix.tileWidth))
math.floor((xcoord - origin_x) / float(matrix.cellSize * matrix.tileWidth))
if not math.isinf(xcoord)
else 0
)
ytile = (
math.floor((origin_y - ycoord) / float(res * matrix.tileHeight))
math.floor((origin_y - ycoord) / float(matrix.cellSize * matrix.tileHeight))
if not math.isinf(ycoord)
else 0
)
Expand Down Expand Up @@ -940,7 +931,6 @@ def _ul(self, *tile: Tile) -> Coords:
t = _parse_tile_arg(*tile)

matrix = self.matrix(t.z)
res = self._resolution(matrix)
origin_x, origin_y = self._matrix_origin(matrix)

cf = (
Expand All @@ -949,8 +939,8 @@ def _ul(self, *tile: Tile) -> Coords:
else 1
)
return Coords(
origin_x + math.floor(t.x / cf) * res * cf * matrix.tileWidth,
origin_y - t.y * res * matrix.tileHeight,
origin_x + math.floor(t.x / cf) * matrix.cellSize * cf * matrix.tileWidth,
origin_y - t.y * matrix.cellSize * matrix.tileHeight,
)

def _lr(self, *tile: Tile) -> Coords:
Expand All @@ -969,7 +959,6 @@ def _lr(self, *tile: Tile) -> Coords:
t = _parse_tile_arg(*tile)

matrix = self.matrix(t.z)
res = self._resolution(matrix)
origin_x, origin_y = self._matrix_origin(matrix)

cf = (
Expand All @@ -978,8 +967,9 @@ def _lr(self, *tile: Tile) -> Coords:
else 1
)
return Coords(
origin_x + (math.floor(t.x / cf) + 1) * res * cf * matrix.tileWidth,
origin_y - (t.y + 1) * res * matrix.tileHeight,
origin_x
+ (math.floor(t.x / cf) + 1) * matrix.cellSize * cf * matrix.tileWidth,
origin_y - (t.y + 1) * matrix.cellSize * matrix.tileHeight,
)

def xy_bounds(self, *tile: Tile) -> BoundingBox:
Expand Down Expand Up @@ -1466,8 +1456,7 @@ def parent(self, *tile: Tile, zoom: int = None):
target_zoom = t.z - 1 if zoom is None else zoom

# buffer value to apply on bbox
res = self._resolution(self.matrix(t.z)) / 10.0

res = self.matrix(t.z).cellSize / 10.0
bbox = self.xy_bounds(t)
ul_tile = self._tile(bbox.left + res, bbox.top - res, target_zoom)
lr_tile = self._tile(bbox.right - res, bbox.bottom + res, target_zoom)
Expand Down Expand Up @@ -1513,7 +1502,7 @@ def children(self, *tile: Tile, zoom: int = None):
target_zoom = t.z + 1 if zoom is None else zoom

# buffer value to apply on bbox
res = self._resolution(self.matrix(t.z)) / 10.0
res = self.matrix(t.z).cellSize / 10.0

bbox = self.xy_bounds(t)
ul_tile = self._tile(bbox.left + res, bbox.top - res, target_zoom)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_mercantile_conform.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
@pytest.mark.parametrize("zoom", range(0, 20))
def test_get_tile(zoom: int):
"""Make sure mercantile and morecantile returns the same thing."""
tile = mercantile.tile(0, 0, zoom=zoom)
morecantile_tile = tms.tile(0, 0, zoom=zoom)
tile = mercantile.tile(-10, 10, zoom=zoom)
morecantile_tile = tms.tile(-10, 10, zoom=zoom)
assert tile == morecantile_tile


Expand Down
28 changes: 14 additions & 14 deletions tests/test_morecantile.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def test_bounds(args):
tms = morecantile.tms.get("WebMercatorQuad")
bbox = tms.bounds(*args)
for a, b in zip(expected, bbox):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0
assert bbox.left == bbox[0]
assert bbox.bottom == bbox[1]
assert bbox.right == bbox[2]
Expand All @@ -123,7 +123,7 @@ def test_xy_bounds(args):
tms = morecantile.tms.get("WebMercatorQuad")
bounds = tms.xy_bounds(*args)
for a, b in zip(expected, bounds):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0


def test_ul_tile():
Expand All @@ -136,7 +136,7 @@ def test_ul_tile():
xy = tms.ul(486, 332, 10)
expected = (-9.140625, 53.33087298301705)
for a, b in zip(expected, xy):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0


def test_projul_tile():
Expand All @@ -149,7 +149,7 @@ def test_projul_tile():
xy = tms._ul(486, 332, 10)
expected = (-1017529.7205322663, 7044436.526761846)
for a, b in zip(expected, xy):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0


def test_projtile():
Expand Down Expand Up @@ -201,7 +201,7 @@ def test_ul(args):
expected = (-9.140625, 53.33087298301705)
lnglat = tms.ul(*args)
for a, b in zip(expected, lnglat):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0
assert lnglat[0] == lnglat.x
assert lnglat[1] == lnglat.y

Expand All @@ -215,7 +215,7 @@ def test_bbox(args):
expected = (-9.140625, 53.12040528310657, -8.7890625, 53.33087298301705)
bbox = tms.bounds(*args)
for a, b in zip(expected, bbox):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0
assert bbox.left == bbox[0]
assert bbox.bottom == bbox[1]
assert bbox.right == bbox[2]
Expand All @@ -229,7 +229,7 @@ def test_xy_tile():
xy = tms.xy(*ul)
expected = (-1017529.7205322663, 7044436.526761846)
for a, b in zip(expected, xy):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0


def test_xy_null_island():
Expand All @@ -238,7 +238,7 @@ def test_xy_null_island():
xy = tms.xy(0.0, 0.0)
expected = (0.0, 0.0)
for a, b in zip(expected, xy):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0


@pytest.mark.xfail
Expand Down Expand Up @@ -320,7 +320,7 @@ def test_lnglat_xy_roundtrip():
lnglat = (-105.0844, 40.5853)
roundtrip = tms.lnglat(*tms.xy(*lnglat))
for a, b in zip(roundtrip, lnglat):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0


@pytest.mark.parametrize(
Expand All @@ -338,7 +338,7 @@ def test_xy_bounds_mercantile(args):
bounds = tms.xy_bounds(*args)

for a, b in zip(expected, bounds):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0


def test_tile_not_truncated():
Expand Down Expand Up @@ -462,25 +462,25 @@ def test_extend_zoom():
with pytest.warns(UserWarning):
more = tms.xy_bounds(1000, 1000, 25)
for a, b in zip(more, merc):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0

merc = mercantile.xy_bounds(2000, 2000, 26)
with pytest.warns(UserWarning):
more = tms.xy_bounds(2000, 2000, 26)
for a, b in zip(more, merc):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0

merc = mercantile.xy_bounds(2000, 2000, 27)
with pytest.warns(UserWarning):
more = tms.xy_bounds(2000, 2000, 27)
for a, b in zip(more, merc):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0

merc = mercantile.xy_bounds(2000, 2000, 30)
with pytest.warns(UserWarning):
more = tms.xy_bounds(2000, 2000, 30)
for a, b in zip(more, merc):
assert round(a - b, 7) == 0
assert round(a - b, 6) == 0


def test_is_power_of_two():
Expand Down

0 comments on commit 6cfb280

Please sign in to comment.