Skip to content

Commit 642797c

Browse files
update wmts (#1012)
1 parent 67f6b64 commit 642797c

File tree

8 files changed

+121
-92
lines changed

8 files changed

+121
-92
lines changed

CHANGES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@
7777
7878
* `/bounds` endpoints now return a `crs: str` attribute in the response
7979
80+
* update `wmts.xml` template to support multiple layers
81+
82+
* re-order endpoints parameters
83+
84+
* avoid `lat/lon` overflow in `map` viewer
85+
8086
### titiler.mosaic
8187
8288
* Rename `reader` attribute to `backend` in `MosaicTilerFactory` **breaking change**
@@ -87,6 +93,8 @@
8793
8894
* Update `cogeo-mosaic` dependency to `>=8.0,<9.0`
8995
96+
* re-order endpoints parameters
97+
9098
### titiler.extensions
9199
92100
* Encode URL for cog_viewer and stac_viewer (author @guillemc23, https://github.com/developmentseed/titiler/pull/961)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def test_wmts(rio, app):
6464
"http://testserver/cog/WebMercatorQuad/WMTSCapabilities.xml?url=https"
6565
in response.content.decode()
6666
)
67-
assert "<ows:Identifier>Dataset</ows:Identifier>" in response.content.decode()
67+
assert "<ows:Identifier>default</ows:Identifier>" in response.content.decode()
6868
assert (
6969
"http://testserver/cog/tiles/WebMercatorQuad/{TileMatrix}/{TileCol}/{TileRow}@1x.png?url=https"
7070
in response.content.decode()

src/titiler/core/titiler/core/dependencies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ def CRSParams(
606606
crs: Annotated[
607607
Optional[str],
608608
Query(
609-
description="Coordinate Reference System`.",
609+
description="Coordinate Reference System.",
610610
),
611611
] = None,
612612
) -> Optional[CRS]:

src/titiler/core/titiler/core/factory.py

Lines changed: 51 additions & 46 deletions
Large diffs are not rendered by default.

src/titiler/core/titiler/core/templates/map.html

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,33 @@
8686

8787
let bounds = [...data.bounds]
8888

89+
var left = bounds[0],
90+
bottom = bounds[1],
91+
right = bounds[2],
92+
top = bounds[3];
93+
94+
if (left < right) {
95+
left = Math.max(left, {{ tms.bbox.left }})
96+
right = Math.min(right, {{ tms.bbox.right }})
97+
}
98+
bottom = Math.max(bottom, {{ tms.bbox.bottom }})
99+
top = Math.min(top, {{ tms.bbox.top }})
100+
console.log(left, bottom, right, top)
101+
89102
let geo;
90103
// handle files that span accross dateline
91-
if (bounds[0] > bounds[2]) {
104+
if (left > right) {
92105
geo = {
93106
"type": "FeatureCollection",
94107
"features": [
95-
bboxPolygon([-180, bounds[1], bounds[2], bounds[3]]),
96-
bboxPolygon([bounds[0], bounds[1], 180, bounds[3]]),
108+
bboxPolygon([-180, bottom, right, top]),
109+
bboxPolygon([left, bottom, 180, top]),
97110
]
98111
}
99112
} else {
100113
geo = {
101114
"type": "FeatureCollection",
102-
"features": [bboxPolygon(bounds)]
115+
"features": [bboxPolygon([left, bottom, right, top])]
103116
}
104117
}
105118
console.log(geo)
@@ -110,13 +123,9 @@
110123
map.fitBounds(aoi.getBounds());
111124

112125
// Bounds crossing dateline
113-
if (bounds[0] > bounds[2]) {
114-
bounds[0] = bounds[0] - 360
126+
if (left > right) {
127+
left = right - 360
115128
}
116-
var left = bounds[0],
117-
bottom = bounds[1],
118-
right = bounds[2],
119-
top = bounds[3];
120129

121130
L.tileLayer(
122131
data.tiles[0], {

src/titiler/core/titiler/core/templates/wmts.xml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,27 @@
3333
</ows:Operation>
3434
</ows:OperationsMetadata>
3535
<Contents>
36+
{% for layer in layers %}
3637
<Layer>
37-
<ows:Title>{{ title }}</ows:Title>
38-
<ows:Identifier>{{ layer_name }}</ows:Identifier>
39-
<ows:Abstract>{{ title }}</ows:Abstract>
38+
<ows:Title>{{ layer.title }}</ows:Title>
39+
<ows:Identifier>{{ layer.name }}</ows:Identifier>
40+
<ows:Abstract>{{ layer.name }}</ows:Abstract>
4041
<ows:{{ bbox_crs_type }} crs="{{ bbox_crs_uri }}">
41-
<ows:LowerCorner>{{ bounds[0] }} {{ bounds[1] }}</ows:LowerCorner>
42-
<ows:UpperCorner>{{ bounds[2] }} {{ bounds[3] }}</ows:UpperCorner>
42+
<ows:LowerCorner>{{ layer.bounds[0] }} {{ layer.bounds[1] }}</ows:LowerCorner>
43+
<ows:UpperCorner>{{ layer.bounds[2] }} {{ layer.bounds[3] }}</ows:UpperCorner>
4344
</ows:{{ bbox_crs_type }}>
4445
<Style isDefault="true">
4546
<ows:Identifier>default</ows:Identifier>
4647
</Style>
4748
<Format>{{ media_type }}</Format>
4849
<TileMatrixSetLink>
49-
<TileMatrixSet>{{ tms.id }}</TileMatrixSet>
50+
<TileMatrixSet>{{ tileMatrixSetId }}</TileMatrixSet>
5051
</TileMatrixSetLink>
51-
<ResourceURL format="{{ media_type }}" resourceType="tile" template="{{ tiles_endpoint | escape }}" />
52+
<ResourceURL format="{{ media_type }}" resourceType="tile" template="{{ layer.tiles_url }}?{{ layer.query_string | escape }}" />
5253
</Layer>
54+
{% endfor %}
5355
<TileMatrixSet>
54-
<ows:Identifier>{{ tms.id }}</ows:Identifier>
56+
<ows:Identifier>{{ tileMatrixSetId }}</ows:Identifier>
5557
<ows:SupportedCRS>{{ supported_crs }}</ows:SupportedCRS>
5658
{% for item in tileMatrix %}
5759
{{ item | safe }}

src/titiler/extensions/titiler/extensions/wms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,13 +284,13 @@ def register(self, factory: TilerFactory): # noqa: C901
284284
def wms( # noqa: C901
285285
request: Request,
286286
# vendor (titiler) parameters
287+
reader_params=Depends(factory.reader_dependency),
287288
layer_params=Depends(factory.layer_dependency),
288289
dataset_params=Depends(factory.dataset_dependency),
289290
post_process=Depends(factory.process_dependency),
290291
rescale=Depends(RescalingParams),
291292
color_formula=Depends(ColorFormulaParams),
292293
colormap=Depends(factory.colormap_dependency),
293-
reader_params=Depends(factory.reader_dependency),
294294
env=Depends(factory.environment_dependency),
295295
):
296296
"""Return a WMS query for a single COG.

src/titiler/mosaic/titiler/mosaic/factory.py

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
)
4646
from titiler.core.factory import DEFAULT_TEMPLATES, BaseFactory, img_endpoint_params
4747
from titiler.core.models.mapbox import TileJSON
48-
from titiler.core.resources.enums import ImageType, MediaType, OptionalHeader
48+
from titiler.core.resources.enums import ImageType, OptionalHeader
4949
from titiler.core.resources.responses import GeoJSONResponse, JSONResponse, XMLResponse
5050
from titiler.core.utils import render_image
5151
from titiler.mosaic.models.responses import Point
@@ -316,6 +316,8 @@ def tile(
316316
"Default will be automatically defined if the output image needs a mask (png) or not (jpeg).",
317317
] = None,
318318
src_path=Depends(self.path_dependency),
319+
backend_params=Depends(self.backend_dependency),
320+
reader_params=Depends(self.reader_dependency),
319321
layer_params=Depends(self.layer_dependency),
320322
dataset_params=Depends(self.dataset_dependency),
321323
pixel_selection=Depends(self.pixel_selection_dependency),
@@ -325,8 +327,6 @@ def tile(
325327
color_formula=Depends(self.color_formula_dependency),
326328
colormap=Depends(self.colormap_dependency),
327329
render_params=Depends(self.render_dependency),
328-
backend_params=Depends(self.backend_dependency),
329-
reader_params=Depends(self.reader_dependency),
330330
env=Depends(self.environment_dependency),
331331
):
332332
"""Create map tile from a COG."""
@@ -410,7 +410,6 @@ def tilejson(
410410
description="Identifier selecting one of the TileMatrixSetId supported."
411411
),
412412
],
413-
src_path=Depends(self.path_dependency),
414413
tile_format: Annotated[
415414
Optional[ImageType],
416415
Query(
@@ -431,6 +430,9 @@ def tilejson(
431430
Optional[int],
432431
Query(description="Overwrite default maxzoom."),
433432
] = None,
433+
src_path=Depends(self.path_dependency),
434+
backend_params=Depends(self.backend_dependency),
435+
reader_params=Depends(self.reader_dependency),
434436
layer_params=Depends(self.layer_dependency),
435437
dataset_params=Depends(self.dataset_dependency),
436438
pixel_selection=Depends(self.pixel_selection_dependency),
@@ -440,8 +442,6 @@ def tilejson(
440442
color_formula=Depends(self.color_formula_dependency),
441443
colormap=Depends(self.colormap_dependency),
442444
render_params=Depends(self.render_dependency),
443-
backend_params=Depends(self.backend_dependency),
444-
reader_params=Depends(self.reader_dependency),
445445
env=Depends(self.environment_dependency),
446446
):
447447
"""Return TileJSON document for a COG."""
@@ -504,7 +504,6 @@ def map_viewer(
504504
description="Identifier selecting one of the TileMatrixSetId supported."
505505
),
506506
],
507-
src_path=Depends(self.path_dependency),
508507
tile_format: Annotated[
509508
Optional[ImageType],
510509
Query(
@@ -525,6 +524,9 @@ def map_viewer(
525524
Optional[int],
526525
Query(description="Overwrite default maxzoom."),
527526
] = None,
527+
src_path=Depends(self.path_dependency),
528+
backend_params=Depends(self.backend_dependency),
529+
reader_params=Depends(self.reader_dependency),
528530
layer_params=Depends(self.layer_dependency),
529531
dataset_params=Depends(self.dataset_dependency),
530532
pixel_selection=Depends(self.pixel_selection_dependency),
@@ -534,8 +536,6 @@ def map_viewer(
534536
color_formula=Depends(self.color_formula_dependency),
535537
colormap=Depends(self.colormap_dependency),
536538
render_params=Depends(self.render_dependency),
537-
backend_params=Depends(self.backend_dependency),
538-
reader_params=Depends(self.reader_dependency),
539539
env=Depends(self.environment_dependency),
540540
):
541541
"""Return TileJSON document for a dataset."""
@@ -571,7 +571,6 @@ def wmts(
571571
description="Identifier selecting one of the TileMatrixSetId supported."
572572
),
573573
],
574-
src_path=Depends(self.path_dependency),
575574
tile_format: Annotated[
576575
ImageType,
577576
Query(description="Output image type. Default is png."),
@@ -596,6 +595,9 @@ def wmts(
596595
description="Use EPSG code, not opengis.net, for the ows:SupportedCRS in the TileMatrixSet (set to True to enable ArcMap compatability)"
597596
),
598597
] = False,
598+
src_path=Depends(self.path_dependency),
599+
backend_params=Depends(self.backend_dependency),
600+
reader_params=Depends(self.reader_dependency),
599601
layer_params=Depends(self.layer_dependency),
600602
dataset_params=Depends(self.dataset_dependency),
601603
pixel_selection=Depends(self.pixel_selection_dependency),
@@ -605,8 +607,6 @@ def wmts(
605607
color_formula=Depends(self.color_formula_dependency),
606608
colormap=Depends(self.colormap_dependency),
607609
render_params=Depends(self.render_dependency),
608-
backend_params=Depends(self.backend_dependency),
609-
reader_params=Depends(self.reader_dependency),
610610
env=Depends(self.environment_dependency),
611611
):
612612
"""OGC WMTS endpoint."""
@@ -635,8 +635,6 @@ def wmts(
635635
for (key, value) in request.query_params._list
636636
if key.lower() not in qs_key_to_remove
637637
]
638-
if qs:
639-
tiles_url += f"?{urlencode(qs)}"
640638

641639
tms = self.supported_tms.get(tileMatrixSetId)
642640
with rasterio.Env(**env):
@@ -671,6 +669,18 @@ def wmts(
671669
else:
672670
supported_crs = tms.crs.srs
673671

672+
layers = [
673+
{
674+
"title": src_path
675+
if isinstance(src_path, str)
676+
else "TiTiler Mosaic",
677+
"name": "default",
678+
"tiles_url": tiles_url,
679+
"query_string": urlencode(qs, doseq=True) if qs else None,
680+
"bounds": bounds,
681+
},
682+
]
683+
674684
bbox_crs_type = "WGS84BoundingBox"
675685
bbox_crs_uri = "urn:ogc:def:crs:OGC:2:84"
676686
if tms.rasterio_geographic_crs != WGS84_CRS:
@@ -681,20 +691,15 @@ def wmts(
681691
request,
682692
name="wmts.xml",
683693
context={
684-
"tiles_endpoint": tiles_url,
685-
"bounds": bounds,
694+
"tileMatrixSetId": tms.id,
686695
"tileMatrix": tileMatrix,
687-
"tms": tms,
688696
"supported_crs": supported_crs,
689697
"bbox_crs_type": bbox_crs_type,
690698
"bbox_crs_uri": bbox_crs_uri,
691-
"title": src_path
692-
if isinstance(src_path, str)
693-
else "TiTiler Mosaic",
694-
"layer_name": "Mosaic",
699+
"layers": layers,
695700
"media_type": tile_format.mediatype,
696701
},
697-
media_type=MediaType.xml.value,
702+
media_type="application/xml",
698703
)
699704

700705
############################################################################
@@ -714,11 +719,11 @@ def point(
714719
lon: Annotated[float, Path(description="Longitude")],
715720
lat: Annotated[float, Path(description="Latitude")],
716721
src_path=Depends(self.path_dependency),
722+
backend_params=Depends(self.backend_dependency),
723+
reader_params=Depends(self.reader_dependency),
717724
coord_crs=Depends(CoordCRSParams),
718725
layer_params=Depends(self.layer_dependency),
719726
dataset_params=Depends(self.dataset_dependency),
720-
backend_params=Depends(self.backend_dependency),
721-
reader_params=Depends(self.reader_dependency),
722727
env=Depends(self.environment_dependency),
723728
):
724729
"""Get Point value for a Mosaic."""
@@ -768,9 +773,9 @@ def assets_for_bbox(
768773
maxx: Annotated[float, Path(description="Bounding box max X")],
769774
maxy: Annotated[float, Path(description="Bounding box max Y")],
770775
src_path=Depends(self.path_dependency),
771-
coord_crs=Depends(CoordCRSParams),
772776
backend_params=Depends(self.backend_dependency),
773777
reader_params=Depends(self.reader_dependency),
778+
coord_crs=Depends(CoordCRSParams),
774779
env=Depends(self.environment_dependency),
775780
):
776781
"""Return a list of assets which overlap a bounding box"""

0 commit comments

Comments
 (0)