[GIS] Zoom levels for WMS and WMTS layers are different

mapproxyopenlayers-2wmswmts

I use MapProxy to 'convert' a WMTS Layer to a WMS Layer. I need to do that because my client software can handle WMS only! I have a configuration which is working but there is a problem with the zoom-levels or the resolution. I have set up MapProxy with my configuration. I open then the demo site at localhost:8080/demo. Here I can view an OpenLayers map for the original WMTS Layer and the result of my conversion (MapProxy does a nice job here).

When I view the OpenLayers map of the original WMTS layer I can easily read all the labels for cities and streets. When I look at the map of the WMS layer it's hard to read these labels. The map doesn't appear sharp and the font size is too small. Furthermore it's obvious that the zoom levels for the OpenLayers WMS and -WMTS maps are different. It seems that the tiles for the WMS layer have shrunk.

Has someone a explanation for that behaviour? Is that a problem of MapProxy or a OpenLayers problem? Or is it just a constraint of the WMS specification?

Some additional informations:
The WMTS capabilities of the layer I try convert is available here: http://www.basemap.at/wmts/1.0.0/WMTSCapabilities.xml

A screenshot which shows the problem. On the left the MapProxy demo page for my WMS layer (labels are quite small and also the map extract is larger than in the original WMTS layer). On the right the demo page for the original WMTS layer (labels are large and sharp):
http://img42.com/VlzDo

The MapProxy configuration:

services:
  demo:
  wms:
    md:
    title: basemap.at WMS
  wmts:
    md:
    title: basemap.at WMTS

sources:
  basemap_wmts:
  type: tile
  url: http://maps.wien.gv.at/basemap/geolandbasemap/normal/google3857/%(z)s/%(y)s/%(x)s.jpeg
  transparent: false
  grid: webmercator
  coverage:
    bbox: [977650, 5838030, 1913530, 6281290]
    srs: 'EPSG:3857'

grids:
  webmercator:
  base: GLOBAL_WEBMERCATOR

caches:
  basemap_cache:
  format: image/jpeg
  request_format: image/jpeg    
  grids: [webmercator]
  sources: [basemap_wmts]

layers:
  - name: basemap_layer
  title: Simple Basemaplayer
  sources: [basemap_cache]

OpenLayers for the WMS-layer map:

<script src="static/OpenLayers.js"></script>
<script type="text/javascript">
var map;
function init(){
    OpenLayers.Util.onImageLoadErrorColor = "transparent";

    map = new OpenLayers.Map('map', {
      maxResolution: 3655.78125,
      maxExtent: new OpenLayers.Bounds(977650, 5838030, 1913530, 6281290),
      projection: new OpenLayers.Projection("EPSG:3857")
    });
    var layer = new OpenLayers.Layer.WMS( "WMS basemap_layer",
        "../service?",
        {layers: "basemap_layer", format: "image/jpeg", srs:"EPSG:3857",
         exceptions: "application/vnd.ogc.se_inimage"},
        {singleTile: true, ratio: 1, isBaseLayer: true} );

    map.addLayer(layer);
    map.zoomToMaxExtent();
}
</script>

OpenLayers configuration for the WMTS-layer map:

<script src="static/OpenLayers.js"></script>
<script type="text/javascript">
var map;
function init(){
    var mapOptions = {
    projection: new OpenLayers.Projection('EPSG:3857'),
    resolutions: [156543.033928, 78271.516964, 39135.758482, 19567.879241, 9783.9396205, 4891.96981025, 2445.98490513, 1222.99245256, 611.496226281, 305.748113141, 152.87405657, 76.4370282852, 38.2185141426, 19.1092570713, 9.55462853565, 4.77731426782, 2.38865713391, 1.19432856696, 0.597164283478, 0.298582141739],
    units: 'm',
    maxExtent: new OpenLayers.Bounds(-20037508.3428, -20037508.3428, 20037508.3428, 20037508.3428)
};

map = new OpenLayers.Map('map', mapOptions);

var layer = new OpenLayers.Layer.WMTS({
 name: "WMTS basemap_layer",
 url: '../wmts/basemap_layer/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.jpeg',
 layer: 'basemap_layer',
 matrixSet: 'webmercator',
 format: 'image/jpeg',
 isBaseLayer: true,
 style: 'default',
 requestEncoding: 'REST'
});

map.addLayer(layer)
map.zoomToExtent(new OpenLayers.Bounds(977650.00, 5838030.00, 1913530.00, 6281290.00));
}
</script>

Best Answer

This is an inherent problem in taking the discrete tiles and zoom levels of WMTS and squashing, merging, and clipping them to handle arbitrary WMS requests. You'll get the same problem with for instance FullWMS mode in GeoWebCache or WMTS cascading in GeoServer which both do this sort of thing.

If you can force the client to use the exact zoom/scale levels that line up with those of the WMTS tile matrix set and no others, then in principle, the labels should be the expected size and there shouldn't be any resampling artifacts but in practice this could be fairly brittle.