Skip to content

Commit 4ecb0cf

Browse files
update tilejson spec (#1241)
* update tilejson spec * allow extra fields * fix tests
1 parent 1584af5 commit 4ecb0cf

File tree

5 files changed

+34
-12
lines changed

5 files changed

+34
-12
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### titiler.core
6+
7+
* update `TileJSON` spec from 2.2.0 to 3.0.0
8+
59
## 0.24.1 (2025-10-10)
610

711
* add `grayscale` and `bitonal` algorithms

src/titiler/application/tests/routes/test_cog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def test_tilejson(rio, app):
260260
)
261261
assert response.status_code == 200
262262
body = response.json()
263-
assert body["tilejson"] == "2.2.0"
263+
assert body["tilejson"] == "3.0.0"
264264
assert body["version"] == "1.0.0"
265265
assert body["scheme"] == "xyz"
266266
assert len(body["tiles"]) == 1

src/titiler/application/tests/routes/test_stac.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def test_tilejson(httpx, rio, app):
118118
)
119119
assert response.status_code == 200
120120
body = response.json()
121-
assert body["tilejson"] == "2.2.0"
121+
assert body["tilejson"] == "3.0.0"
122122
assert body["version"] == "1.0.0"
123123
assert body["scheme"] == "xyz"
124124
assert len(body["tiles"]) == 1

src/titiler/core/tests/test_models.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
def test_tilejson_model():
1010
"""Make sure TileJSON model validates input and return default."""
1111
tj = TileJSON(tiles=["https://something.xyz/{x}/{y}/{z}"])
12-
assert tj.center == (0.0, 0.0, 0)
13-
assert tj.bounds == [-180, -90, 180, 90]
12+
assert list(map(round, tj.center)) == [0.0, 0.0, 0]
13+
assert tj.bounds == [-180, -85.0511287798066, 180, 85.0511287798066]
1414
assert tj.minzoom == 0
1515
assert tj.maxzoom == 30
1616
assert tj.scheme == "xyz"
@@ -19,8 +19,12 @@ def test_tilejson_model():
1919
tiles=["https://something.xyz/{x}/{y}/{z}"], center=(10, 10, 4), scheme="tms"
2020
)
2121
assert tj.center == (10.0, 10.0, 4)
22-
assert tj.bounds == [-180, -90, 180, 90]
22+
assert tj.bounds == [-180, -85.0511287798066, 180, 85.0511287798066]
2323
assert tj.scheme == "tms"
2424

2525
with pytest.raises(ValidationError):
2626
TileJSON(tiles=["https://something.xyz/{x}/{y}/{z}"], scheme="abc")
27+
28+
# Check extra fields are allowed
29+
tj = TileJSON(tiles=["https://something.xyz/{x}/{y}/{z}"], dtype="uint8")
30+
assert tj.dtype == "uint8"

src/titiler/core/titiler/core/models/mapbox.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
11
"""Common response models."""
22

3-
from typing import List, Literal, Optional, Tuple
3+
from typing import Annotated, Dict, List, Literal, Optional, Tuple
44

55
from pydantic import BaseModel, Field, model_validator
66

77

8-
class TileJSON(BaseModel):
8+
class LayerJSON(BaseModel):
9+
"""
10+
https://github.com/mapbox/tilejson-spec/tree/master/3.0.0#33-vector_layers
11+
"""
12+
13+
id: str
14+
fields: Annotated[Dict, Field(default_factory=dict)]
15+
description: Optional[str] = None
16+
minzoom: Optional[int] = None
17+
maxzoom: Optional[int] = None
18+
19+
20+
class TileJSON(BaseModel, extra="allow"):
921
"""
1022
TileJSON model.
1123
12-
Based on https://github.com/mapbox/tilejson-spec/tree/master/2.2.0
24+
Based on https://github.com/mapbox/tilejson-spec/tree/master/3.0.0
1325
1426
"""
1527

16-
tilejson: str = "2.2.0"
28+
tilejson: str = "3.0.0"
1729
name: Optional[str] = None
1830
description: Optional[str] = None
1931
version: str = "1.0.0"
@@ -22,11 +34,13 @@ class TileJSON(BaseModel):
2234
legend: Optional[str] = None
2335
scheme: Literal["xyz", "tms"] = "xyz"
2436
tiles: List[str]
37+
vector_layers: Optional[List[LayerJSON]] = None
2538
grids: Optional[List[str]] = None
2639
data: Optional[List[str]] = None
27-
minzoom: int = Field(0, ge=0, le=30)
28-
maxzoom: int = Field(30, ge=0, le=30)
29-
bounds: List[float] = [-180, -90, 180, 90]
40+
minzoom: int = Field(0)
41+
maxzoom: int = Field(30)
42+
fillzoom: Optional[int] = None
43+
bounds: List[float] = [-180, -85.0511287798066, 180, 85.0511287798066]
3044
center: Optional[Tuple[float, float, int]] = None
3145

3246
@model_validator(mode="after")

0 commit comments

Comments
 (0)