2

I've got a map in OpenLayers with 12 possible zoom levels given by a resolution array as defined in view:

var map = new Map({ 
//...
view: new View({                                               
      resolutions: [8, 4, 2, 1, 0.5, 0.25, 0.125, 0.0625, 0.03125, 0.015625, 0.0078125, 0.00390625],
      zoom: 2,
      constrainResolution: true
    }),
});

The maximum zoom seems to be given implicitly by the resolutions array and is set to 12.

In my use case, users should be able to select detail or overview layers for which I want to constrain the zoom temporarily:

  map.getView().setMaxZoom(7);

I would expect the zoom now to stop at zoom level 7 (i. e. restrict the zoom to resolutions of [8, 4, 2, 1, 0.5, 0.25, 0.125, 0.0625]).

Unfortunately, it doesn't work for me as intended. Visually, zoom will stop at zoom level 7 but internally, on zoom interactions, zoom is being incremented until the original maximum zoom 12 is reached. Not only is this notable by calling map.getView().getZoom() in these stages, but also by user interaction. When “invisibly” having zoomed out to zoom level 12 (the original max zoom), users have to undo 5 zoom interactions (e. g. mouse wheel) before again reaching the state where zooming out results in a visible change.

Is there any way to achieve the behavior I am aiming for?

3
  • You can force integer zoom levels using view.setConstrainResolution(true); and remove the constraint with view.setConstrainResolution(false);. openlayers.org/en/latest/apidoc/…
    – Mike
    Jan 18 at 10:51
  • I'm using OL 6.5. However, for my actual use case I'm using a modified tileGrid with variable tile sizes to enable this kind of functionality: gis.stackexchange.com/questions/344604/…. Could this be tampering with setting minZoom and maxZoom? Jan 18 at 11:12
  • View zoom level is not necessarily the same as tilegrid zoom level
    – Mike
    Jan 18 at 11:22
4

Maybe @Mike ha some trick upon his sleeve, but I couldn't get around this problem some normal way. I have tile layer with custom tile grid. Without constrainResolution: true option in the view definition, view honors maxZoom option. As soon constrainResolution: true option is added to view definition, view disregards maxZoom option.

Below is my actual code, where maxZoom: 4 option is simply ignored:

proj4.defs('EPSG:3912',
           '+proj=tmerc +lat_0=0 +lon_0=15 +k=0.9999 +x_0=500000 +y_0=-5000000 +ellps=bessel +towgs84=682,-203,480,0,0,0,0 +units=m +no_defs');                   
ol.proj.proj4.register(proj4);

var layerResol = [658, 500, 130, 125, 42, 41, 21,  12,  4, 2, 1];          
var layerExtent = [374371.84, 30513.32, 624119.18, 195517.48];

var tileGrid = new ol.tilegrid.TileGrid({
      extent: layerExtent,
      resolutions: layerResol
    });
    
var layer = new ol.layer.Tile({
  source: new ol.source.TileWMS({
    url: 'http://prostor4.gov.si:80/ows2-m-pub/wms',
    attributions: '&copy <a href="http://www.gu.gov.si/">GURS</a>',
    params: {
      'LAYERS': '',
      'FORMAT': 'image/png',
      'VERSION': '1.3.0'
    },
    tileGrid: tileGrid,
    projection: 'EPSG:3912'
  })
});

var urlFunc = layer.getSource().getTileUrlFunction().bind(layer.getSource());
layer.getSource().setTileUrlFunction(function (tileCoord, pixelRatio, projection) {
    var products = ["DPK1000", "DPK750", "DPK750", "DPK750", "DPK500", "DPK250", 'DPK250', 'DTK50', 'DTK50', "DTK25", "DTK25", "SVR", "SV" ];
    var z = tileCoord[0];
    this.updateParams({ 'LAYERS':'SI.GURS.DK:' + products[z]});
    return urlFunc(tileCoord, pixelRatio, projection);
});

var view = new ol.View({
    projection: 'EPSG:3912',
    center: ol.extent.getCenter(layerExtent),
    resolutions: layerResol,
    constrainResolution: true,
    maxZoom: 4,
    zoom: 1
  });

var osmLayer = new ol.layer.Tile({
  source: new ol.source.OSM()
});

var map = new ol.Map({
  target: 'map',
  layers: [osmLayer, layer]
});

map.setView(view);

The only solution I could find to this was to create second view with reduced grid resolutions and apply it to map with simple function when needed:

var layerResol2 = [658, 500, 130, 125];          
var view2 = new ol.View({
    projection: 'EPSG:3912',
    resolutions: layerResol2,
    constrainResolution: true
  });

function setReducedZoomView(setIt) {
  var center = map.getView().getCenter();
  var zoom = map.getView().getZoom();
  if (setIt) {
    view2.setZoom(zoom);
    view2.setCenter(center);
    map.setView(view2);
    }
  else {
    view.setZoom(zoom);
    view.setCenter(center);
    map.setView(view);
  }
}
2
  • Yes, the documentation for the resolutions option says "Resolutions to determine the resolution constraint. If set the maxResolution, minResolution, minZoom, maxZoom, and zoomFactor options are ignored." But that only applies when constrainResolution is true. You can switch between constrained resolutions and maxZoom by calling setConstrainResolution(), but your method allows both constraints together.
    – Mike
    Jan 18 at 19:38
  • Thank you for your reply and also for providing an actual minimal code example! I ended up using this solution, which makes it very easy to customize the start zoom of the new view before it is applied. For anyone using this, if you're watching the viewZoom (e. g. by using map.getView().on('change:resolution', watchFunction)), the event has to be reset when the view is changed. This can be done easily with map.on('change:view', updateFunction). Jan 20 at 11:24
2

It is possible to dynamically update the content of the views's resolutions array, for example

var source = new OSM();
var resolutions = source.getTileGrid().getResolutions().slice(0, 8);
var liveResolutions = resolutions.slice();

var map = new Map({
  layers: [
    new TileLayer({
      source: source
    })
  ],
  target: "map",
  view: new View({
    center: [0, 0],
    resolutions: liveResolutions,
    constrainResolution: true,
    zoom: 2,
    multiWorld: true
  })
});

document.getElementById("toggle").onchange = function () {
  if (liveResolutions.length > 5) {
    liveResolutions.splice(5, 3);
  } else {
    liveResolutions.splice(
      5,
      0,
      resolutions[5],
      resolutions[6],
      resolutions[7]
    );
  }
  map.getView().setResolution(map.getView().getResolution());
};

https://codesandbox.io/s/simple-forked-nnlx1?file=/main.js

1
  • Thanks for this solution! I thought it's impossible to set view resolutions dynamically, since there is no setResolutions method for the view.
    – TomazicM
    Jan 19 at 9:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.