<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/env python
# -*- coding: utf-8 -*-
###############################################################################
# $Id$
#
# Project:  GDAL/OGR Test Suite
# Purpose:  WMTS driver test suite.
# Author:   Even Rouault, even dot rouault at spatialys.com
#
###############################################################################
# Copyright (c) 2015, Even Rouault &lt;even dot rouault at spatialys.com&gt;
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
###############################################################################

import sys
import shutil

sys.path.append( '../pymod' )

from osgeo import gdal

import gdaltest

###############################################################################
# Find WMTS driver

def wmts_1():

    gdaltest.wmts_drv = gdal.GetDriverByName('WMTS')

    if gdaltest.wmts_drv is not None and gdal.GetDriverByName('WMS') is None:
        print('Missing WMS driver')
        gdaltest.wmts_drv = None

    if gdaltest.wmts_drv is not None:

        gdal.SetConfigOption('CPL_CURL_ENABLE_VSIMEM', 'YES')
        gdal.SetConfigOption('GDAL_DEFAULT_WMS_CACHE_PATH', '/vsimem/cache')

        return 'success'
    else:
        return 'skip'

###############################################################################
# Error: no URL and invalid GDAL_WMTS service file documents

def wmts_2():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    gdal.PushErrorHandler()
    ds = gdal.Open('&lt;GDAL_WMTS&gt;')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    gdal.PushErrorHandler()
    ds = gdal.Open('&lt;GDAL_WMTSxxx/&gt;')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    gdal.PushErrorHandler()
    ds = gdal.Open('&lt;GDAL_WMTS&gt;&lt;/GDAL_WMTS&gt;')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: invalid URL

def wmts_3():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:https://non_existing')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: invalid URL

def wmts_4():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/non_existing')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: invalid XML in GetCapabilities response

def wmts_5():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/invalid_getcapabilities.xml', '&lt;invalid_xml')

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/invalid_getcapabilities.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: invalid content in GetCapabilities response

def wmts_6():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/invalid_getcapabilities.xml', '&lt;Capabilities/&gt;')

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/invalid_getcapabilities.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: no layers

def wmts_7():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/empty_getcapabilities.xml', '&lt;Capabilities&gt;&lt;Contents/&gt;&lt;/Capabilities&gt;')

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/empty_getcapabilities.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: missing TileMatrixSetLink and Style

def wmts_8():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/missing.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier/&gt;
        &lt;/Layer&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/missing.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: missing TileMatrixSet

def wmts_9():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/missing_tms.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier/&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet/&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style&gt;
                &lt;Identifier/&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png" template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpeg" resourceType="tile"/&gt;
        &lt;/Layer&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/missing_tms.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: Missing SupportedCRS

def wmts_10():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/missing_SupportedCRS.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier/&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet/&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style&gt;
                &lt;Identifier/&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png" template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpeg" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier/&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/missing_SupportedCRS.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: Cannot find TileMatrix in TileMatrixSet

def wmts_11():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/no_tilematrix.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier/&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet/&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style&gt;
                &lt;Identifier/&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png" template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpeg" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier/&gt;
            &lt;SupportedCRS&gt;urn:ogc:def:crs:EPSG:6.18:3:3857&lt;/SupportedCRS&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/no_tilematrix.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: Missing required element in TileMatrix element

def wmts_12():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/missing_required_element_in_tilematrix.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier/&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet/&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style&gt;
                &lt;Identifier/&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png" template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpeg" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier/&gt;
            &lt;SupportedCRS&gt;urn:ogc:def:crs:EPSG:6.18:3:3857&lt;/SupportedCRS&gt;
            &lt;TileMatrix/&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/missing_required_element_in_tilematrix.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Error: Missing ResourceURL

def wmts_12bis():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/wmts_12bis.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier/&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet/&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style&gt;
                &lt;Identifier/&gt;
            &lt;/Style&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier/&gt;
            &lt;SupportedCRS&gt;urn:ogc:def:crs:EPSG:6.18:3:3857&lt;/SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;Identifier&gt;0&lt;/Identifier&gt;
                &lt;ScaleDenominator&gt;559082264.029&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    gdal.PushErrorHandler()
    ds = gdal.Open('WMTS:/vsimem/wmts_12bis.xml')
    gdal.PopErrorHandler()
    if ds is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Minimal

def wmts_13():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/minimal.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier/&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet/&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style&gt;
                &lt;Identifier/&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png" template="/vsimem/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier/&gt;
            &lt;SupportedCRS&gt;urn:ogc:def:crs:EPSG:6.18:3:3857&lt;/SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;Identifier&gt;0&lt;/Identifier&gt;
                &lt;ScaleDenominator&gt;559082264.029&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/minimal.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 256:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterYSize != 256:
        gdaltest.post_reason('fail')
        return 'fail'
    got_gt = ds.GetGeoTransform()
    expected_gt = (-20037508.342799999, 156543.03392811998, 0.0, 20037508.342799999, 0.0, -156543.03392811998)
    for i in range(6):
        if abs(got_gt[i] - expected_gt[i]) &gt; 1e-8:
            gdaltest.post_reason('fail')
            print(got_gt)
            return 'fail'
    if ds.GetProjectionRef().find('3857') &lt; 0:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterCount != 4:
        gdaltest.post_reason('fail')
        return 'fail'
    for i in range(4):
        if ds.GetRasterBand(i+1).GetColorInterpretation() != gdal.GCI_RedBand + i:
            gdaltest.post_reason('fail')
            return 'fail'
    if ds.GetRasterBand(1).GetOverviewCount() != 0:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.GetRasterBand(1).GetOverview(0) is not None:
        gdaltest.post_reason('fail')
        return 'fail'
    gdal.PushErrorHandler()
    cs = ds.GetRasterBand(1).Checksum()
    gdal.PopErrorHandler()
    if cs != 0:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.GetSubDatasets() != []:
        gdaltest.post_reason('fail')
        print(ds.GetSubDatasets())
        return 'fail'
    if ds.GetRasterBand(1).GetMetadataItem('Pixel_0_0', 'LocationInfo') is not None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.GetRasterBand(1).GetMetadataItem('foo') is not None:
        gdaltest.post_reason('fail')
        return 'fail'

    for connection_str in [ 'WMTS:/vsimem/minimal.xml,layer=',
                            'WMTS:/vsimem/minimal.xml,style=',
                            'WMTS:/vsimem/minimal.xml,tilematrixset=',
                            'WMTS:/vsimem/minimal.xml,tilematrix=',
                            'WMTS:/vsimem/minimal.xml,zoom_level=',
                            'WMTS:/vsimem/minimal.xml,layer=,style=,tilematrixset=' ]:
        ds = gdal.Open(connection_str)
        if ds is None:
            gdaltest.post_reason('fail')
            print(connection_str)
            return 'fail'
        ds = None

    for connection_str in [ 'WMTS:/vsimem/minimal.xml,layer=foo',
                            'WMTS:/vsimem/minimal.xml,style=bar',
                            'WMTS:/vsimem/minimal.xml,tilematrixset=baz',
                            'WMTS:/vsimem/minimal.xml,tilematrix=baw',
                            'WMTS:/vsimem/minimal.xml,zoom_level=30' ]:
        gdal.PushErrorHandler()
        ds = gdal.Open(connection_str)
        gdal.PopErrorHandler()
        if ds is not None:
            gdaltest.post_reason('fail')
            print(connection_str)
            return 'fail'
        ds = None

    ds = gdal.Open('WMTS:/vsimem/minimal.xml')
    tmp_ds = gdal.GetDriverByName('MEM').Create('',256,256,4)
    for i in range(4):
        tmp_ds.GetRasterBand(i+1).Fill((i+1)*255/4)
    tmp_ds = gdal.GetDriverByName('PNG').CreateCopy('/vsimem/0/0/0.png', tmp_ds)
    for i in range(4):
        cs = ds.GetRasterBand(i+1).Checksum()
        if cs != tmp_ds.GetRasterBand(i+1).Checksum():
            gdaltest.post_reason('fail')
            return 'fail'

    ref_data = tmp_ds.ReadRaster(0,0,256,256)
    got_data = ds.ReadRaster(0,0,ds.RasterXSize,ds.RasterYSize,256,256)
    if ref_data != got_data:
        gdaltest.post_reason('fail')
        return 'fail'

    ref_data = tmp_ds.GetRasterBand(1).ReadRaster(0,0,256,256)
    got_data = ds.GetRasterBand(1).ReadRaster(0,0,ds.RasterXSize,ds.RasterYSize,256,256)
    if ref_data != got_data:
        gdaltest.post_reason('fail')
        return 'fail'

    ds = None
    wmts_CleanCache()

    return 'success'

###############################################################################
# Nominal RESTful

def wmts_14():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/nominal.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;Abstract&gt;My abstract&lt;/Abstract&gt;
            &lt;ows:WGS84BoundingBox&gt;
                &lt;ows:LowerCorner&gt;-180 -85.0511287798065&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;180 85.0511287798065&lt;/ows:UpperCorner&gt;
            &lt;/ows:WGS84BoundingBox&gt;
            &lt;Dimension&gt;
                &lt;ows:Identifier&gt;time&lt;/ows:Identifier&gt;
                &lt;UOM&gt;ISO8601&lt;/UOM&gt;
                &lt;Default&gt;2011-10-04&lt;/Default&gt;
                &lt;Current&gt;false&lt;/Current&gt;
                &lt;Value&gt;2002-06-01/2011-10-04/P1D&lt;/Value&gt;
           &lt;/Dimension&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;another_tms&lt;/TileMatrixSet&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;style=auto&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;Style&gt;
                &lt;Identifier&gt;another_style&lt;/Identifier&gt;
                &lt;Title&gt;Another style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png"
    template="/vsimem/{time}/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
            &lt;ResourceURL format="text/plain"
    template="/vsimem/{time}/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}/{J}/{I}.txt" resourceType="FeatureInfo"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;SupportedCRS&gt;urn:ogc:def:crs:EPSG:6.18:3:3857&lt;/SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;Identifier&gt;tm_0&lt;/Identifier&gt;
                &lt;ScaleDenominator&gt;559082264.029&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;tm_18&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;2132.72958385&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;262144&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;262144&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;24&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;33.3238997477&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;16777216&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;16777216&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;another_tms&lt;/Identifier&gt;
            &lt;ows:Identifier&gt;GoogleCRS84Quad&lt;/ows:Identifier&gt;
            &lt;ows:SupportedCRS&gt;urn:ogc:def:crs:EPSG::4326&lt;/ows:SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:0&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;5.590822640287178E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
    &lt;ServiceMetadataURL xlink:href="/vsimem/nominal.xml"/&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/nominal.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.GetSubDatasets() != [('WMTS:/vsimem/nominal.xml,layer=lyr1,tilematrixset=tms,style="style=auto"',
                                'Layer My layer1, tile matrix set tms, style "Default style"'),
                                ('WMTS:/vsimem/nominal.xml,layer=lyr1,tilematrixset=tms,style=another_style',
                                'Layer My layer1, tile matrix set tms, style "Another style"'),
                                ('WMTS:/vsimem/nominal.xml,layer=lyr1,tilematrixset=another_tms,style="style=auto"',
                                'Layer My layer1, tile matrix set another_tms, style "Default style"'),
                                ('WMTS:/vsimem/nominal.xml,layer=lyr1,tilematrixset=another_tms,style=another_style',
                                'Layer My layer1, tile matrix set another_tms, style "Another style"')]:
        gdaltest.post_reason('fail')
        print(ds.GetSubDatasets())
        return 'fail'
    if ds.RasterXSize != 67108864:
        gdaltest.post_reason('fail')
        return 'fail'
    gdal.PushErrorHandler()
    res = ds.GetRasterBand(1).GetMetadataItem('Pixel_1_2', 'LocationInfo')
    gdal.PopErrorHandler()
    if res != '':
        gdaltest.post_reason('fail')
        print(res)
        return 'fail'
    if ds.GetMetadata() != {'ABSTRACT': 'My abstract', 'TITLE': 'My layer1'}:
        gdaltest.post_reason('fail')
        print(ds.GetMetadata())
        return 'fail'

    gdal.PushErrorHandler()
    gdaltest.wmts_drv.CreateCopy('/vsimem/gdal_nominal.xml', gdal.GetDriverByName('MEM').Create('',1,1))
    gdal.PopErrorHandler()

    gdaltest.wmts_drv.CreateCopy('/vsimem/gdal_nominal.xml', ds)
    ds = None

    f = gdal.VSIFOpenL('/vsimem/gdal_nominal.xml', 'rb')
    data = gdal.VSIFReadL(1, 10000, f).decode('ascii')
    gdal.VSIFCloseL(f)
    if data != """&lt;GDAL_WMTS&gt;
  &lt;GetCapabilitiesUrl&gt;/vsimem/nominal.xml&lt;/GetCapabilitiesUrl&gt;
  &lt;Layer&gt;lyr1&lt;/Layer&gt;
  &lt;Style&gt;style=auto&lt;/Style&gt;
  &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
  &lt;DataWindow&gt;
    &lt;UpperLeftX&gt;-20037508.3428&lt;/UpperLeftX&gt;
    &lt;UpperLeftY&gt;20037508.3428&lt;/UpperLeftY&gt;
    &lt;LowerRightX&gt;20037508.34278254&lt;/LowerRightX&gt;
    &lt;LowerRightY&gt;-20037508.34278254&lt;/LowerRightY&gt;
  &lt;/DataWindow&gt;
  &lt;BandsCount&gt;4&lt;/BandsCount&gt;
  &lt;Cache /&gt;
  &lt;UnsafeSSL&gt;true&lt;/UnsafeSSL&gt;
  &lt;ZeroBlockHttpCodes&gt;204,404&lt;/ZeroBlockHttpCodes&gt;
  &lt;ZeroBlockOnServerException&gt;true&lt;/ZeroBlockOnServerException&gt;
&lt;/GDAL_WMTS&gt;
""":
        gdaltest.post_reason('fail')
        print(data)
        return 'fail'

    ds = gdal.Open('/vsimem/gdal_nominal.xml')
    gdal.FileFromMemBuffer('/vsimem/2011-10-04/style=auto/tms/tm_18/0/0/2/1.txt', 'foo')
    res = ds.GetRasterBand(1).GetMetadataItem('Pixel_1_2', 'LocationInfo')
    if res != '&lt;LocationInfo&gt;foo&lt;/LocationInfo&gt;':
        gdaltest.post_reason('fail')
        print(res)
        return 'fail'
    res = ds.GetRasterBand(1).GetMetadataItem('Pixel_1_2', 'LocationInfo')
    if res != '&lt;LocationInfo&gt;foo&lt;/LocationInfo&gt;':
        gdaltest.post_reason('fail')
        print(res)
        return 'fail'

    ds = gdal.Open('&lt;GDAL_WMTS&gt;&lt;GetCapabilitiesUrl&gt;/vsimem/nominal.xml&lt;/GetCapabilitiesUrl&gt;&lt;/GDAL_WMTS&gt;')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'

    ds = gdal.Open('WMTS:/vsimem/gdal_nominal.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'

    for open_options in [ ['URL=/vsimem/nominal.xml'],
                          ['URL=/vsimem/nominal.xml', 'STYLE=style=auto', 'TILEMATRIXSET=tms'] ]:
        ds = gdal.OpenEx('WMTS:', open_options = open_options)
        if ds is None:
            gdaltest.post_reason('fail')
            return 'fail'

    for open_options in [ ['URL=/vsimem/nominal.xml', 'STYLE=x', 'TILEMATRIXSET=y'],
                          ['URL=/vsimem/nominal.xml', 'STYLE=style=auto', 'TILEMATRIX=30'],
                          ['URL=/vsimem/nominal.xml', 'STYLE=style=auto', 'ZOOM_LEVEL=30'] ]:
        gdal.PushErrorHandler()
        ds = gdal.OpenEx('WMTS:', open_options = open_options)
        gdal.PopErrorHandler()
        if ds is not None:
            gdaltest.post_reason('fail')
            return 'fail'

    ds = gdal.Open('WMTS:/vsimem/nominal.xml')
    gdal.FileFromMemBuffer('/vsimem/2011-10-04/style=auto/tms/tm_18/0/0/2/1.txt', '&lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;xml_content/&gt;')
    res = ds.GetRasterBand(1).GetMetadataItem('Pixel_1_2', 'LocationInfo')
    if res != """&lt;LocationInfo&gt;&lt;xml_content /&gt;
&lt;/LocationInfo&gt;""":
        gdaltest.post_reason('fail')
        print(res)
        return 'fail'

    ds = gdal.Open('WMTS:/vsimem/gdal_nominal.xml,tilematrix=tm_0')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'

    ds = gdal.OpenEx('WMTS:/vsimem/gdal_nominal.xml', open_options = ['tilematrix=tm_0'])
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'

    ds = gdal.Open('WMTS:/vsimem/gdal_nominal.xml,zoom_level=0')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'

    ds = gdal.OpenEx('WMTS:/vsimem/gdal_nominal.xml', open_options = ['zoom_level=0'])
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'

    gdal.FileFromMemBuffer('/vsimem/gdal_nominal.xml', """&lt;GDAL_WMTS&gt;
  &lt;GetCapabilitiesUrl&gt;/vsimem/nominal.xml&lt;/GetCapabilitiesUrl&gt;
  &lt;Layer&gt;lyr1&lt;/Layer&gt;
  &lt;Style&gt;style=auto&lt;/Style&gt;
  &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
  &lt;TileMatrix&gt;tm_0&lt;/TileMatrix&gt;
  &lt;DataWindow&gt;
    &lt;UpperLeftX&gt;-20037508.3428&lt;/UpperLeftX&gt;
    &lt;UpperLeftY&gt;20037508.3428&lt;/UpperLeftY&gt;
    &lt;LowerRightX&gt;20037508.34278254&lt;/LowerRightX&gt;
    &lt;LowerRightY&gt;-20037508.34278254&lt;/LowerRightY&gt;
  &lt;/DataWindow&gt;
  &lt;BandsCount&gt;4&lt;/BandsCount&gt;
  &lt;Cache /&gt;
  &lt;UnsafeSSL&gt;true&lt;/UnsafeSSL&gt;
  &lt;ZeroBlockHttpCodes&gt;204,404&lt;/ZeroBlockHttpCodes&gt;
  &lt;ZeroBlockOnServerException&gt;true&lt;/ZeroBlockOnServerException&gt;
&lt;/GDAL_WMTS&gt;""")
    ds = gdal.Open('WMTS:/vsimem/gdal_nominal.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'

    gdal.FileFromMemBuffer('/vsimem/gdal_nominal.xml', """&lt;GDAL_WMTS&gt;
  &lt;GetCapabilitiesUrl&gt;/vsimem/nominal.xml&lt;/GetCapabilitiesUrl&gt;
  &lt;Layer&gt;lyr1&lt;/Layer&gt;
  &lt;Style&gt;style=auto&lt;/Style&gt;
  &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
  &lt;ZoomLevel&gt;0&lt;/ZoomLevel&gt;
  &lt;DataWindow&gt;
    &lt;UpperLeftX&gt;-20037508.3428&lt;/UpperLeftX&gt;
    &lt;UpperLeftY&gt;20037508.3428&lt;/UpperLeftY&gt;
    &lt;LowerRightX&gt;20037508.34278254&lt;/LowerRightX&gt;
    &lt;LowerRightY&gt;-20037508.34278254&lt;/LowerRightY&gt;
  &lt;/DataWindow&gt;
  &lt;BandsCount&gt;4&lt;/BandsCount&gt;
  &lt;Cache /&gt;
  &lt;UnsafeSSL&gt;true&lt;/UnsafeSSL&gt;
  &lt;ZeroBlockHttpCodes&gt;204,404&lt;/ZeroBlockHttpCodes&gt;
  &lt;ZeroBlockOnServerException&gt;true&lt;/ZeroBlockOnServerException&gt;
&lt;/GDAL_WMTS&gt;""")
    ds = gdal.Open('WMTS:/vsimem/gdal_nominal.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'

    return 'success'

###############################################################################
# Nominal KVP

def wmts_15():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/nominal_kvp.xml?service=WMTS&amp;request=GetCapabilities', """&lt;Capabilities xmlns="http://www.opengis.net/wmts/1.0"&gt;
    &lt;ows:OperationsMetadata&gt;
    &lt;ows:Operation name="GetCapabilities"&gt;
      &lt;ows:DCP&gt;
        &lt;ows:HTTP&gt;
          &lt;ows:Get xlink:href="/vsimem/nominal_kvp.xml?"&gt;
            &lt;ows:Constraint name="GetEncoding"&gt;
              &lt;ows:AllowedValues&gt;
                &lt;ows:Value&gt;KVP&lt;/ows:Value&gt;
              &lt;/ows:AllowedValues&gt;
            &lt;/ows:Constraint&gt;
          &lt;/ows:Get&gt;
        &lt;/ows:HTTP&gt;
      &lt;/ows:DCP&gt;
    &lt;/ows:Operation&gt;
    &lt;ows:Operation name="GetTile"&gt;
      &lt;ows:DCP&gt;
        &lt;ows:HTTP&gt;
          &lt;ows:Get xlink:href="/vsimem/nominal_kvp.xml?"&gt;
          &lt;/ows:Get&gt;
        &lt;/ows:HTTP&gt;
      &lt;/ows:DCP&gt;
    &lt;/ows:Operation&gt;
    &lt;ows:Operation name="GetFeatureInfo"&gt;
      &lt;ows:DCP&gt;
        &lt;ows:HTTP&gt;
          &lt;ows:Get xlink:href="/vsimem/nominal_kvp.xml?"&gt;
          &lt;/ows:Get&gt;
        &lt;/ows:HTTP&gt;
      &lt;/ows:DCP&gt;
    &lt;/ows:Operation&gt;
  &lt;/ows:OperationsMetadata&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;ows:BoundingBox crs="urn:ogc:def:crs:EPSG:6.18:3:3857"&gt;
                &lt;ows:LowerCorner&gt;-20037508.3428 -20037508.3428&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;20037508.3428 20037508.3428&lt;/ows:UpperCorner&gt;
            &lt;/ows:BoundingBox&gt;
            &lt;Dimension&gt;
                &lt;ows:Identifier&gt;time&lt;/ows:Identifier&gt;
                &lt;UOM&gt;ISO8601&lt;/UOM&gt;
                &lt;Default&gt;2011-10-04&lt;/Default&gt;
                &lt;Current&gt;false&lt;/Current&gt;
                &lt;Value&gt;2002-06-01/2011-10-04/P1D&lt;/Value&gt;
           &lt;/Dimension&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;default_style&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;Format&gt;image/jpeg&lt;/Format&gt;
            &lt;Format&gt;image/png&lt;/Format&gt;
            &lt;InfoFormat&gt;text/plain&lt;/InfoFormat&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;ows:BoundingBox crs="urn:ogc:def:crs:EPSG:6.18:3:3857"&gt;
                &lt;ows:LowerCorner&gt;-20037508.3428 -20037508.3428&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;20037508.3428 20037508.3428&lt;/ows:UpperCorner&gt;
            &lt;/ows:BoundingBox&gt;
            &lt;SupportedCRS&gt;urn:ogc:def:crs:EPSG:6.18:3:3857&lt;/SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;Identifier&gt;0&lt;/Identifier&gt;
                &lt;ScaleDenominator&gt;559082264.029&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;18&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;2132.72958385&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;262144&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;262144&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;24&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;33.3238997477&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;16777216&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;16777216&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('/vsimem/nominal_kvp.xml?service=WMTS&amp;request=GetCapabilities')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 67108864:
        gdaltest.post_reason('fail')
        return 'fail'
    gdal.PushErrorHandler()
    res = ds.GetRasterBand(1).GetMetadataItem('Pixel_1_2', 'LocationInfo')
    gdal.PopErrorHandler()
    if res != '':
        gdaltest.post_reason('fail')
        print(res)
        return 'fail'

    gdaltest.wmts_drv.CreateCopy('/vsimem/gdal_nominal_kvp.xml', ds)
    ds = None

    ds = gdal.Open('/vsimem/gdal_nominal_kvp.xml')
    gdal.FileFromMemBuffer('/vsimem/nominal_kvp.xml?service=WMTS&amp;request=GetFeatureInfo&amp;version=1.0.0&amp;layer=lyr1&amp;style=default_style&amp;InfoFormat=text/plain&amp;TileMatrixSet=tms&amp;TileMatrix=18&amp;TileRow=0&amp;TileCol=0&amp;J=2&amp;I=1&amp;time=2011-10-04', 'bar')
    res = ds.GetRasterBand(1).GetMetadataItem('Pixel_1_2', 'LocationInfo')
    if res != '&lt;LocationInfo&gt;bar&lt;/LocationInfo&gt;':
        gdaltest.post_reason('fail')
        print(res)
        return 'fail'

    ds = gdal.Open('WMTS:/vsimem/gdal_nominal_kvp.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    tmp_ds = gdal.GetDriverByName('MEM').Create('',256,256,4)
    for i in range(4):
        tmp_ds.GetRasterBand(i+1).Fill((i+1)*255/4)
    tmp_ds = gdal.GetDriverByName('PNG').CreateCopy('/vsimem/nominal_kvp.xml?service=WMTS&amp;request=GetTile&amp;version=1.0.0&amp;layer=lyr1&amp;style=default_style&amp;format=image/png&amp;TileMatrixSet=tms&amp;TileMatrix=0&amp;TileRow=0&amp;TileCol=0&amp;time=2011-10-04', tmp_ds)
    for i in range(4):
        cs = ds.GetRasterBand(i+1).GetOverview(0).Checksum()
        if cs != tmp_ds.GetRasterBand(i+1).Checksum():
            gdaltest.post_reason('fail')
            return 'fail'

    ref_data = tmp_ds.ReadRaster(0,0,256,256)
    got_data = ds.ReadRaster(0,0,ds.RasterXSize,ds.RasterYSize,256,256)
    if ref_data != got_data:
        gdaltest.post_reason('fail')
        return 'fail'

    ref_data = tmp_ds.GetRasterBand(1).ReadRaster(0,0,256,256)
    got_data = ds.GetRasterBand(1).ReadRaster(0,0,ds.RasterXSize,ds.RasterYSize,256,256)
    if ref_data != got_data:
        gdaltest.post_reason('fail')
        return 'fail'

    ds = None
    wmts_CleanCache()

    return 'success'

###############################################################################
# AOI from layer WGS84BoundingBox

def wmts_16():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/wmts_16.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;ows:WGS84BoundingBox&gt;
                &lt;ows:LowerCorner&gt;-90 0&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;90 90&lt;/ows:UpperCorner&gt;
            &lt;/ows:WGS84BoundingBox&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;default_style&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png"
    template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;ows:Identifier&gt;GoogleCRS84Quad&lt;/ows:Identifier&gt;
            &lt;ows:SupportedCRS&gt;urn:ogc:def:crs:EPSG::4326&lt;/ows:SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:0&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;5.590822640287178E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:1&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;2.795411320143589E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;2&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:2&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;1.397705660071794E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;4&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;2&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
    &lt;ServiceMetadataURL xlink:href="/vsimem/wmts_16.xml"/&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/wmts_16.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 512:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'
    if ds.RasterYSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterYSize)
        return 'fail'
    got_gt = ds.GetGeoTransform()
    expected_gt = (-90, 0.3515625, 0.0, 90.0, 0.0, -0.3515625)
    for i in range(6):
        if abs(got_gt[i] - expected_gt[i]) &gt; 1e-8:
            gdaltest.post_reason('fail')
            print(got_gt)
            return 'fail'
    if ds.GetProjectionRef().find('4326') &lt; 0 or ds.GetProjectionRef().find('AXIS') &gt;= 0:
        gdaltest.post_reason('fail')
        print(ds.GetProjectionRef())
        return 'fail'

    return 'success'

###############################################################################
# AOI from layer BoundingBox

def wmts_17():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/wmts_17.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;ows:BoundingBox crs="urn:ogc:def:crs:EPSG::4326"&gt;
                &lt;ows:LowerCorner&gt;0 -90&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;90 90&lt;/ows:UpperCorner&gt;
            &lt;/ows:BoundingBox&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;default_style&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png"
    template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;ows:Identifier&gt;GoogleCRS84Quad&lt;/ows:Identifier&gt;
            &lt;ows:SupportedCRS&gt;urn:ogc:def:crs:EPSG::4326&lt;/ows:SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:0&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;5.590822640287178E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:1&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;2.795411320143589E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;2&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:2&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;1.397705660071794E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;4&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;2&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
    &lt;ServiceMetadataURL xlink:href="/vsimem/wmts_17.xml"/&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/wmts_17.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 512:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'
    if ds.RasterYSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterYSize)
        return 'fail'
    got_gt = ds.GetGeoTransform()
    expected_gt = (-90, 0.3515625, 0.0, 90.0, 0.0, -0.3515625)
    for i in range(6):
        if abs(got_gt[i] - expected_gt[i]) &gt; 1e-8:
            gdaltest.post_reason('fail')
            print(got_gt)
            return 'fail'
    if ds.GetProjectionRef().find('4326') &lt; 0 or ds.GetProjectionRef().find('AXIS') &gt;= 0:
        gdaltest.post_reason('fail')
        print(ds.GetProjectionRef())
        return 'fail'

    return 'success'

###############################################################################
# AOI from TileMatrixSet BoundingBox

def wmts_18():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/wmts_18.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;default_style&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png"
    template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;ows:Identifier&gt;GoogleCRS84Quad&lt;/ows:Identifier&gt;
            &lt;ows:SupportedCRS&gt;urn:ogc:def:crs:EPSG::4326&lt;/ows:SupportedCRS&gt;
            &lt;ows:BoundingBox crs="urn:ogc:def:crs:EPSG::4326"&gt;
                &lt;ows:LowerCorner&gt;0 -90&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;90 90&lt;/ows:UpperCorner&gt;
            &lt;/ows:BoundingBox&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:0&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;5.590822640287178E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:1&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;2.795411320143589E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;2&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:2&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;1.397705660071794E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;4&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;2&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
    &lt;ServiceMetadataURL xlink:href="/vsimem/wmts_18.xml"/&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/wmts_18.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 512:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'
    if ds.RasterYSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterYSize)
        return 'fail'
    got_gt = ds.GetGeoTransform()
    expected_gt = (-90, 0.3515625, 0.0, 90.0, 0.0, -0.3515625)
    for i in range(6):
        if abs(got_gt[i] - expected_gt[i]) &gt; 1e-8:
            gdaltest.post_reason('fail')
            print(got_gt)
            return 'fail'
    if ds.GetProjectionRef().find('4326') &lt; 0 or ds.GetProjectionRef().find('AXIS') &gt;= 0:
        gdaltest.post_reason('fail')
        print(ds.GetProjectionRef())
        return 'fail'

    return 'success'

###############################################################################
# AOI from TileMatrixSetLimits

def wmts_19():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/wmts_19.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
                &lt;TileMatrixSetLimits&gt;
                    &lt;TileMatrixLimits&gt;
                        &lt;TileMatrix&gt;GoogleCRS84Quad:2&lt;/TileMatrix&gt;
                        &lt;MinTileRow&gt;0&lt;/MinTileRow&gt;
                        &lt;MaxTileRow&gt;0&lt;/MaxTileRow&gt;
                        &lt;MinTileCol&gt;1&lt;/MinTileCol&gt;
                        &lt;MaxTileCol&gt;2&lt;/MaxTileCol&gt;
                    &lt;/TileMatrixLimits&gt;
                &lt;/TileMatrixSetLimits&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;default_style&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png"
    template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;ows:Identifier&gt;GoogleCRS84Quad&lt;/ows:Identifier&gt;
            &lt;ows:SupportedCRS&gt;urn:ogc:def:crs:EPSG::4326&lt;/ows:SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:0&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;5.590822640287178E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:1&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;2.795411320143589E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;2&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:2&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;1.397705660071794E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;4&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;2&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
    &lt;ServiceMetadataURL xlink:href="/vsimem/wmts_19.xml"/&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/wmts_19.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 512:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'
    if ds.RasterYSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterYSize)
        return 'fail'
    got_gt = ds.GetGeoTransform()
    expected_gt = (-90, 0.3515625, 0.0, 90.0, 0.0, -0.3515625)
    for i in range(6):
        if abs(got_gt[i] - expected_gt[i]) &gt; 1e-8:
            gdaltest.post_reason('fail')
            print(got_gt)
            return 'fail'
    if ds.GetProjectionRef().find('4326') &lt; 0 or ds.GetProjectionRef().find('AXIS') &gt;= 0:
        gdaltest.post_reason('fail')
        print(ds.GetProjectionRef())
        return 'fail'

    return 'success'

###############################################################################
# AOI from layer BoundingBox but restricted with TileMatrixSetLimits

def wmts_20():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/wmts_20.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;ows:BoundingBox crs="urn:ogc:def:crs:EPSG::4326"&gt;
                &lt;ows:LowerCorner&gt;-90 -180&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;90 180&lt;/ows:UpperCorner&gt;
            &lt;/ows:BoundingBox&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
                &lt;TileMatrixSetLimits&gt;
                    &lt;TileMatrixLimits&gt;
                        &lt;TileMatrix&gt;GoogleCRS84Quad:2&lt;/TileMatrix&gt;
                        &lt;MinTileRow&gt;0&lt;/MinTileRow&gt;
                        &lt;MaxTileRow&gt;0&lt;/MaxTileRow&gt;
                        &lt;MinTileCol&gt;1&lt;/MinTileCol&gt;
                        &lt;MaxTileCol&gt;2&lt;/MaxTileCol&gt;
                    &lt;/TileMatrixLimits&gt;
                &lt;/TileMatrixSetLimits&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;default_style&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png"
    template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;ows:Identifier&gt;GoogleCRS84Quad&lt;/ows:Identifier&gt;
            &lt;ows:SupportedCRS&gt;urn:ogc:def:crs:EPSG::4326&lt;/ows:SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:0&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;5.590822640287178E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:1&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;2.795411320143589E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;2&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:2&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;1.397705660071794E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;4&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;2&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
    &lt;ServiceMetadataURL xlink:href="/vsimem/wmts_20.xml"/&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/wmts_20.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 512:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'
    if ds.RasterYSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterYSize)
        return 'fail'
    got_gt = ds.GetGeoTransform()
    expected_gt = (-90, 0.3515625, 0.0, 90.0, 0.0, -0.3515625)
    for i in range(6):
        if abs(got_gt[i] - expected_gt[i]) &gt; 1e-8:
            gdaltest.post_reason('fail')
            print(got_gt)
            return 'fail'
    if ds.GetProjectionRef().find('4326') &lt; 0 or ds.GetProjectionRef().find('AXIS') &gt;= 0:
        gdaltest.post_reason('fail')
        print(ds.GetProjectionRef())
        return 'fail'

    return 'success'

###############################################################################
# Test ExtendBeyondDateLine

def wmts_21():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/wmts_21.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;ows:BoundingBox crs="urn:ogc:def:crs:EPSG::4326"&gt;
                &lt;ows:LowerCorner&gt;-90 -180&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;0 180&lt;/ows:UpperCorner&gt;
            &lt;/ows:BoundingBox&gt;
            &lt;!-- completely made-up case and not really representative... --&gt;
            &lt;ows:BoundingBox crs="urn:ogc:def:crs:OGC:2:84"&gt;
                &lt;ows:LowerCorner&gt;90 -90&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;-90 0&lt;/ows:UpperCorner&gt;
            &lt;/ows:BoundingBox&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;default_style&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;ResourceURL format="image/png"
    template="/vsimem/wmts_21/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;ows:Identifier&gt;GoogleCRS84Quad&lt;/ows:Identifier&gt;
            &lt;ows:SupportedCRS&gt;urn:ogc:def:crs:EPSG::4326&lt;/ows:SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:0&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;5.590822640287178E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:1&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;2.795411320143589E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;2&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;GoogleCRS84Quad:2&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;1.397705660071794E8&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;90.0 -180.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;4&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;2&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
    &lt;ServiceMetadataURL xlink:href="/vsimem/wmts_21.xml"/&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/wmts_21.xml,extendbeyonddateline=yes')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 512:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'
    if ds.RasterYSize != 256:
        gdaltest.post_reason('fail')
        print(ds.RasterYSize)
        return 'fail'
    got_gt = ds.GetGeoTransform()
    expected_gt = (90, 0.3515625, 0.0, 0.0, 0.0, -0.3515625)
    for i in range(6):
        if abs(got_gt[i] - expected_gt[i]) &gt; 1e-8:
            gdaltest.post_reason('fail')
            print(got_gt)
            return 'fail'
    if ds.GetProjectionRef().find('4326') &lt; 0 or ds.GetProjectionRef().find('AXIS') &gt;= 0:
        gdaltest.post_reason('fail')
        print(ds.GetProjectionRef())
        return 'fail'

    tmp_ds = gdal.GetDriverByName('MEM').Create('',256,256,4)
    for i in range(4):
        tmp_ds.GetRasterBand(i+1).Fill(64)
    tmp3_ds = gdal.GetDriverByName('PNG').CreateCopy('/vsimem/wmts_21/default_style/tms/GoogleCRS84Quad:2/1/3.png', tmp_ds)

    tmp_ds = gdal.GetDriverByName('MEM').Create('',256,256,4)
    for i in range(4):
        tmp_ds.GetRasterBand(i+1).Fill(128)
    tmp0_ds = gdal.GetDriverByName('PNG').CreateCopy('/vsimem/wmts_21/default_style/tms/GoogleCRS84Quad:2/1/0.png', tmp_ds)

    if ds.GetRasterBand(1).ReadRaster(0,0,256,256) != tmp3_ds.GetRasterBand(1).ReadRaster(0,0,256,256):
        gdaltest.post_reason('fail')
        return 'fail'

    if ds.GetRasterBand(1).ReadRaster(256,0,256,256) != tmp0_ds.GetRasterBand(1).ReadRaster(0,0,256,256):
        gdaltest.post_reason('fail')
        return 'fail'

    return 'success'

###############################################################################
# Test when WGS84BoundingBox is a densified reprojection of the tile matrix bbox

def wmts_22():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.FileFromMemBuffer('/vsimem/wmts_22.xml', """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;ows:WGS84BoundingBox&gt;
                &lt;ows:LowerCorner&gt;-6.38153862706 55.6179644952&lt;/ows:LowerCorner&gt;
                &lt;ows:UpperCorner&gt;60.3815386271 75.5825702342&lt;/ows:UpperCorner&gt;
            &lt;/ows:WGS84BoundingBox&gt;
            &lt;Identifier&gt;lyr1&lt;/Identifier&gt;
            &lt;Title&gt;My layer1&lt;/Title&gt;
            &lt;Style isDefault="true"&gt;
                &lt;Identifier&gt;default_style&lt;/Identifier&gt;
                &lt;Title&gt;Default style&lt;/Title&gt;
            &lt;/Style&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet&gt;tms&lt;/TileMatrixSet&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;ResourceURL format="image/png"
    template="/vsimem/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier&gt;tms&lt;/Identifier&gt;
            &lt;ows:Identifier&gt;tms&lt;/ows:Identifier&gt;
            &lt;ows:SupportedCRS&gt;urn:ogc:def:crs:EPSG::3067&lt;/ows:SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;ows:Identifier&gt;13&lt;/ows:Identifier&gt;
                &lt;ScaleDenominator&gt;3571.42857143&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-548576.0 8388608.0&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;256&lt;/TileWidth&gt;
                &lt;TileHeight&gt;256&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;8192&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;8192&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
    &lt;ServiceMetadataURL xlink:href="/vsimem/wmts_22.xml"/&gt;
&lt;/Capabilities&gt;""")

    ds = gdal.Open('WMTS:/vsimem/wmts_22.xml')
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'
    if ds.RasterXSize != 2097152:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'
    if ds.RasterYSize != 2097152:
        gdaltest.post_reason('fail')
        print(ds.RasterYSize)
        return 'fail'
    got_gt = ds.GetGeoTransform()
    expected_gt = (-548576.0, 1.0000000000004, 0.0, 8388608.0, 0.0, -1.0000000000004)
    for i in range(6):
        if abs(got_gt[i] - expected_gt[i]) &gt; 1e-8:
            gdaltest.post_reason('fail')
            print(got_gt)
            return 'fail'
    if ds.GetProjectionRef().find('3067') &lt; 0:
        gdaltest.post_reason('fail')
        print(ds.GetProjectionRef())
        return 'fail'

    return 'success'
###############################################################################
#

def wmts_23( imagetype, expected_cs ):

    if gdaltest.wmts_drv is None:
        return 'skip'

    inputXml = '/vsimem/' + imagetype +'.xml'
    serviceUrl =  '/vsimem/wmts_23/' + imagetype
    gdal.FileFromMemBuffer( inputXml, """&lt;Capabilities&gt;
    &lt;Contents&gt;
        &lt;Layer&gt;
            &lt;Identifier/&gt;
            &lt;TileMatrixSetLink&gt;
                &lt;TileMatrixSet/&gt;
            &lt;/TileMatrixSetLink&gt;
            &lt;Style&gt;
                &lt;Identifier/&gt;
            &lt;/Style&gt;
            &lt;ResourceURL format="image/png" template=" """ + serviceUrl + """/{TileMatrix}/{TileRow}/{TileCol}.png" resourceType="tile"/&gt;
        &lt;/Layer&gt;
        &lt;TileMatrixSet&gt;
            &lt;Identifier/&gt;
            &lt;SupportedCRS&gt;urn:ogc:def:crs:EPSG:6.18:3:3857&lt;/SupportedCRS&gt;
            &lt;TileMatrix&gt;
                &lt;Identifier&gt;0&lt;/Identifier&gt;
                &lt;ScaleDenominator&gt;559082264.029&lt;/ScaleDenominator&gt;
                &lt;TopLeftCorner&gt;-20037508.3428 20037508.3428&lt;/TopLeftCorner&gt;
                &lt;TileWidth&gt;128&lt;/TileWidth&gt;
                &lt;TileHeight&gt;128&lt;/TileHeight&gt;
                &lt;MatrixWidth&gt;1&lt;/MatrixWidth&gt;
                &lt;MatrixHeight&gt;1&lt;/MatrixHeight&gt;
            &lt;/TileMatrix&gt;
        &lt;/TileMatrixSet&gt;
    &lt;/Contents&gt;
&lt;/Capabilities&gt;""")

    tmp_ds = gdal.Open( 'data/wms/' + imagetype + '.png' )
    if tmp_ds is None:
        gdaltest.post_reason('fail - cannot open tmp_ds')
        return 'fail'

    tile0_ds = gdal.GetDriverByName('PNG').CreateCopy(serviceUrl + '/0/0/0.png', tmp_ds )
    if tile0_ds is None:
        gdaltest.post_reason('fail - cannot create tile0')
        return 'fail'

    ds = gdal.Open('WMTS:' + inputXml )
    if ds is None:
        gdaltest.post_reason('fail')
        return 'fail'

    if ds.RasterXSize != 128:
        gdaltest.post_reason('fail')
        print(ds.RasterXSize)
        return 'fail'
    if ds.RasterYSize != 128:
        gdaltest.post_reason('fail')
        print(ds.RasterYSize)
        return 'fail'

    for i in range(4):
        cs = ds.GetRasterBand( i + 1 ).Checksum()
        if cs != expected_cs[i]:
            gdaltest.post_reason('fail')
            print( cs )
            return 'fail'

    return 'success'

def wmts_23_gray():
    return wmts_23( 'gray', [ 60137, 60137, 60137, 4428 ] )

def wmts_23_grayalpha():
    return wmts_23( 'gray+alpha', [ 39910, 39910, 39910, 63180 ] )

def wmts_23_pal():
    return wmts_23( 'pal', [ 62950, 59100, 63864, 453 ] )

def wmts_23_rgb():
    return wmts_23( 'rgb', [ 1020, 3665, 6180, 4428 ] )

def wmts_23_rgba():
    return wmts_23( 'rgba', [ 65530, 51449, 1361, 59291 ] )

###############################################################################
#

def wmts_CleanCache():
    hexstr = '012346789abcdef'
    for i in range(len(hexstr)):
        for j in range(len(hexstr)):
            lst = gdal.ReadDir('/vsimem/cache/%s/%s' % (i, j))
            if lst is not None:
                for f in lst:
                    gdal.Unlink('/vsimem/cache/%s/%s/%s' % (i, j, f))

###############################################################################
#

def wmts_cleanup():

    if gdaltest.wmts_drv is None:
        return 'skip'

    gdal.SetConfigOption('CPL_CURL_ENABLE_VSIMEM', None)
    gdal.SetConfigOption('GDAL_DEFAULT_WMS_CACHE_PATH', None)

    wmts_CleanCache()

    lst = gdal.ReadDir('/vsimem/')
    if lst:
        for f in lst:
            gdal.Unlink('/vsimem/' + f)

    try:
        shutil.rmtree('tmp/wmts_cache')
    except:
        pass

    return 'success'


gdaltest_list = [
    wmts_1,
    wmts_2,
    wmts_3,
    wmts_4,
    wmts_5,
    wmts_6,
    wmts_7,
    wmts_8,
    wmts_9,
    wmts_10,
    wmts_11,
    wmts_12,
    wmts_12bis,
    wmts_13,
    wmts_14,
    wmts_15,
    wmts_16,
    wmts_17,
    wmts_18,
    wmts_19,
    wmts_20,
    wmts_21,
    wmts_22,
    wmts_23_gray,
    wmts_23_grayalpha,
    wmts_23_pal,
    wmts_23_rgb,
    wmts_23_rgba,
    wmts_cleanup ]

if __name__ == '__main__':

    gdaltest.setup_run( 'wmts' )

    gdaltest.run_tests( gdaltest_list )

    gdaltest.summarize()

</pre></body></html>