Support zoom limits for layers

This commit is contained in:
Tim Schaub
2019-08-06 13:27:32 -04:00
parent c767091faf
commit 65ad4932f4
11 changed files with 424 additions and 86 deletions

View File

@@ -0,0 +1,13 @@
---
layout: example.html
title: Layer Zoom Limits
shortdesc: Using minZoom and maxZoom to control layer visibility.
docs: >
Layers support `minZoom` and `maxZoom` options for controlling visibility based on the view's zoom level.
If min or max zoom are set, the layer will only be visible at zoom levels greater than the `minZoom` and
less than or equal to the `maxZoom`. After construction, the layer's `setMinZoom` and `setMaxZoom` can
be used to set limits. This example shows an OSM layer at zoom levels 14 and lower and a USGS layer at
zoom levels higher than 14.
tags: "minZoom, maxZoom, layer"
---
<div id="map" class="map"></div>

View File

@@ -0,0 +1,33 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js';
import OSM from '../src/ol/source/OSM.js';
import XYZ from '../src/ol/source/XYZ.js';
import {transformExtent, fromLonLat} from '../src/ol/proj.js';
const mapExtent = [-112.261791, 35.983744, -112.113981, 36.132062];
const map = new Map({
target: 'map',
layers: [
new TileLayer({
maxZoom: 14, // visible at zoom levels 14 and below
source: new OSM()
}),
new TileLayer({
minZoom: 14, // visible at zoom levels above 14
source: new XYZ({
attributions: 'Tiles © USGS, rendered with ' +
'<a href="http://www.maptiler.com/">MapTiler</a>',
url: 'https://tileserver.maptiler.com/grandcanyon/{z}/{x}/{y}.png'
})
})
],
view: new View({
center: fromLonLat([-112.18688965, 36.057944835]),
zoom: 15,
maxZoom: 18,
extent: transformExtent(mapExtent, 'EPSG:4326', 'EPSG:3857'),
constrainOnlyCenter: true
})
});

View File

@@ -7,7 +7,7 @@ import {CLASS_CONTROL, CLASS_UNSELECTABLE, CLASS_COLLAPSED} from '../css.js';
import {removeChildren, replaceNode} from '../dom.js'; import {removeChildren, replaceNode} from '../dom.js';
import {listen} from '../events.js'; import {listen} from '../events.js';
import EventType from '../events/EventType.js'; import EventType from '../events/EventType.js';
import {visibleAtResolution} from '../layer/Layer.js'; import {inView} from '../layer/Layer.js';
/** /**
@@ -170,10 +170,9 @@ class Attribution extends Control {
const visibleAttributions = []; const visibleAttributions = [];
const layerStatesArray = frameState.layerStatesArray; const layerStatesArray = frameState.layerStatesArray;
const resolution = frameState.viewState.resolution;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i]; const layerState = layerStatesArray[i];
if (!visibleAtResolution(layerState, resolution)) { if (!inView(layerState, frameState.viewState)) {
continue; continue;
} }

View File

@@ -23,6 +23,10 @@ import {assign} from '../obj.js';
* visible. * visible.
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
* be visible. * be visible.
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
* visible.
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
* be visible.
*/ */
@@ -57,6 +61,10 @@ class BaseLayer extends BaseObject {
options.maxResolution !== undefined ? options.maxResolution : Infinity; options.maxResolution !== undefined ? options.maxResolution : Infinity;
properties[LayerProperty.MIN_RESOLUTION] = properties[LayerProperty.MIN_RESOLUTION] =
options.minResolution !== undefined ? options.minResolution : 0; options.minResolution !== undefined ? options.minResolution : 0;
properties[LayerProperty.MIN_ZOOM] =
options.minZoom !== undefined ? options.minZoom : -Infinity;
properties[LayerProperty.MAX_ZOOM] =
options.maxZoom !== undefined ? options.maxZoom : Infinity;
/** /**
* @type {string} * @type {string}
@@ -103,6 +111,8 @@ class BaseLayer extends BaseObject {
state.zIndex = this.getZIndex() || (state.managed === false ? Infinity : 0); state.zIndex = this.getZIndex() || (state.managed === false ? Infinity : 0);
state.maxResolution = this.getMaxResolution(); state.maxResolution = this.getMaxResolution();
state.minResolution = Math.max(this.getMinResolution(), 0); state.minResolution = Math.max(this.getMinResolution(), 0);
state.minZoom = this.getMinZoom();
state.maxZoom = this.getMaxZoom();
this.state_ = state; this.state_ = state;
return state; return state;
@@ -161,6 +171,26 @@ class BaseLayer extends BaseObject {
return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION)); return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION));
} }
/**
* Return the minimum zoom level of the layer.
* @return {number} The minimum zoom level of the layer.
* @observable
* @api
*/
getMinZoom() {
return /** @type {number} */ (this.get(LayerProperty.MIN_ZOOM));
}
/**
* Return the maximum zoom level of the layer.
* @return {number} The maximum zoom level of the layer.
* @observable
* @api
*/
getMaxZoom() {
return /** @type {number} */ (this.get(LayerProperty.MAX_ZOOM));
}
/** /**
* Return the opacity of the layer (between 0 and 1). * Return the opacity of the layer (between 0 and 1).
* @return {number} The opacity of the layer. * @return {number} The opacity of the layer.
@@ -231,6 +261,30 @@ class BaseLayer extends BaseObject {
this.set(LayerProperty.MIN_RESOLUTION, minResolution); this.set(LayerProperty.MIN_RESOLUTION, minResolution);
} }
/**
* Set the maximum zoom (exclusive) at which the layer is visible.
* Note that the zoom levels for layer visibility are based on the
* view zoom level, which may be different from a tile source zoom level.
* @param {number} maxZoom The maximum zoom of the layer.
* @observable
* @api
*/
setMaxZoom(maxZoom) {
this.set(LayerProperty.MAX_ZOOM, maxZoom);
}
/**
* Set the minimum zoom (inclusive) at which the layer is visible.
* Note that the zoom levels for layer visibility are based on the
* view zoom level, which may be different from a tile source zoom level.
* @param {number} minZoom The minimum zoom of the layer.
* @observable
* @api
*/
setMinZoom(minZoom) {
this.set(LayerProperty.MIN_ZOOM, minZoom);
}
/** /**
* Set the opacity of the layer, allowed values range from 0 to 1. * Set the opacity of the layer, allowed values range from 0 to 1.
* @param {number} opacity The opacity of the layer. * @param {number} opacity The opacity of the layer.

View File

@@ -29,6 +29,10 @@ import SourceState from '../source/State.js';
* visible. * visible.
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
* be visible. * be visible.
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
* visible.
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
* be visible.
* @property {Array<import("./Base.js").default>|import("../Collection.js").default<import("./Base.js").default>} [layers] Child layers. * @property {Array<import("./Base.js").default>|import("../Collection.js").default<import("./Base.js").default>} [layers] Child layers.
*/ */
@@ -215,6 +219,10 @@ class LayerGroup extends BaseLayer {
layerState.maxResolution, ownLayerState.maxResolution); layerState.maxResolution, ownLayerState.maxResolution);
layerState.minResolution = Math.max( layerState.minResolution = Math.max(
layerState.minResolution, ownLayerState.minResolution); layerState.minResolution, ownLayerState.minResolution);
layerState.minZoom = Math.max(
layerState.minZoom, ownLayerState.minZoom);
layerState.maxZoom = Math.min(
layerState.maxZoom, ownLayerState.maxZoom);
if (ownLayerState.extent !== undefined) { if (ownLayerState.extent !== undefined) {
if (layerState.extent !== undefined) { if (layerState.extent !== undefined) {
layerState.extent = getIntersection(layerState.extent, ownLayerState.extent); layerState.extent = getIntersection(layerState.extent, ownLayerState.extent);

View File

@@ -50,6 +50,8 @@ import SourceState from '../source/State.js';
* @property {number} zIndex * @property {number} zIndex
* @property {number} maxResolution * @property {number} maxResolution
* @property {number} minResolution * @property {number} minResolution
* @property {number} minZoom
* @property {number} maxZoom
*/ */
/** /**
@@ -277,17 +279,22 @@ class Layer extends BaseLayer {
/** /**
* Return `true` if the layer is visible, and if the passed resolution is * Return `true` if the layer is visible and if the provided view state
* between the layer's minResolution and maxResolution. The comparison is * has resolution and zoom levels that are in range of the layer's min/max.
* inclusive for `minResolution` and exclusive for `maxResolution`.
* @param {State} layerState Layer state. * @param {State} layerState Layer state.
* @param {number} resolution Resolution. * @param {import("../View.js").State} viewState View state.
* @return {boolean} The layer is visible at the given resolution. * @return {boolean} The layer is visible at the given view state.
*/ */
export function visibleAtResolution(layerState, resolution) { export function inView(layerState, viewState) {
return layerState.visible && resolution >= layerState.minResolution && if (!layerState.visible) {
resolution < layerState.maxResolution; return false;
}
const resolution = viewState.resolution;
if (resolution < layerState.minResolution || resolution >= layerState.maxResolution) {
return false;
}
const zoom = viewState.zoom;
return zoom > layerState.minZoom && zoom <= layerState.maxZoom;
} }
export default Layer; export default Layer;

View File

@@ -12,5 +12,7 @@ export default {
Z_INDEX: 'zIndex', Z_INDEX: 'zIndex',
MAX_RESOLUTION: 'maxResolution', MAX_RESOLUTION: 'maxResolution',
MIN_RESOLUTION: 'minResolution', MIN_RESOLUTION: 'minResolution',
MAX_ZOOM: 'maxZoom',
MIN_ZOOM: 'minZoom',
SOURCE: 'source' SOURCE: 'source'
}; };

View File

@@ -2,7 +2,7 @@
* @module ol/renderer/Composite * @module ol/renderer/Composite
*/ */
import {CLASS_UNSELECTABLE} from '../css.js'; import {CLASS_UNSELECTABLE} from '../css.js';
import {visibleAtResolution} from '../layer/Layer.js'; import {inView} from '../layer/Layer.js';
import RenderEvent from '../render/Event.js'; import RenderEvent from '../render/Event.js';
import RenderEventType from '../render/EventType.js'; import RenderEventType from '../render/EventType.js';
import MapRenderer from './Map.js'; import MapRenderer from './Map.js';
@@ -95,7 +95,7 @@ class CompositeMapRenderer extends MapRenderer {
const layerStatesArray = frameState.layerStatesArray.sort(function(a, b) { const layerStatesArray = frameState.layerStatesArray.sort(function(a, b) {
return a.zIndex - b.zIndex; return a.zIndex - b.zIndex;
}); });
const viewResolution = frameState.viewState.resolution; const viewState = frameState.viewState;
this.children_.length = 0; this.children_.length = 0;
let hasOverlay = false; let hasOverlay = false;
@@ -104,7 +104,7 @@ class CompositeMapRenderer extends MapRenderer {
const layerState = layerStatesArray[i]; const layerState = layerStatesArray[i];
hasOverlay = hasOverlay || layerState.hasOverlay; hasOverlay = hasOverlay || layerState.hasOverlay;
frameState.layerIndex = i; frameState.layerIndex = i;
if (!visibleAtResolution(layerState, viewResolution) || if (!inView(layerState, viewState) ||
(layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) { (layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) {
continue; continue;
} }
@@ -142,7 +142,6 @@ class CompositeMapRenderer extends MapRenderer {
*/ */
forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, layerFilter) { forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, layerFilter) {
const viewState = frameState.viewState; const viewState = frameState.viewState;
const viewResolution = viewState.resolution;
const layerStates = frameState.layerStatesArray; const layerStates = frameState.layerStatesArray;
const numLayers = layerStates.length; const numLayers = layerStates.length;
@@ -150,7 +149,7 @@ class CompositeMapRenderer extends MapRenderer {
for (let i = numLayers - 1; i >= 0; --i) { for (let i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i]; const layerState = layerStates[i];
const layer = layerState.layer; const layer = layerState.layer;
if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter(layer)) { if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter(layer)) {
const layerRenderer = layer.getRenderer(); const layerRenderer = layer.getRenderer();
const data = layerRenderer.getDataAtPixel(pixel, frameState, hitTolerance); const data = layerRenderer.getDataAtPixel(pixel, frameState, hitTolerance);
if (data) { if (data) {

View File

@@ -5,7 +5,7 @@ import {abstract, getUid} from '../util.js';
import Disposable from '../Disposable.js'; import Disposable from '../Disposable.js';
import {getWidth} from '../extent.js'; import {getWidth} from '../extent.js';
import {TRUE} from '../functions.js'; import {TRUE} from '../functions.js';
import {visibleAtResolution} from '../layer/Layer.js'; import {inView} from '../layer/Layer.js';
import {shared as iconImageCache} from '../style/IconImageCache.js'; import {shared as iconImageCache} from '../style/IconImageCache.js';
import {compose as composeTransform, makeInverse} from '../transform.js'; import {compose as composeTransform, makeInverse} from '../transform.js';
import {renderDeclutterItems} from '../render.js'; import {renderDeclutterItems} from '../render.js';
@@ -87,7 +87,6 @@ class MapRenderer extends Disposable {
) { ) {
let result; let result;
const viewState = frameState.viewState; const viewState = frameState.viewState;
const viewResolution = viewState.resolution;
/** /**
* @param {boolean} managed Managed layer. * @param {boolean} managed Managed layer.
@@ -126,7 +125,7 @@ class MapRenderer extends Disposable {
for (i = numLayers - 1; i >= 0; --i) { for (i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i]; const layerState = layerStates[i];
const layer = /** @type {import("../layer/Layer.js").default} */ (layerState.layer); const layer = /** @type {import("../layer/Layer.js").default} */ (layerState.layer);
if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter.call(thisArg2, layer)) {
const layerRenderer = layer.getRenderer(); const layerRenderer = layer.getRenderer();
const source = layer.getSource(); const source = layer.getSource();
if (layerRenderer && source) { if (layerRenderer && source) {

View File

@@ -44,7 +44,9 @@ describe('ol.layer.Group', function() {
extent: undefined, extent: undefined,
zIndex: 0, zIndex: 0,
maxResolution: Infinity, maxResolution: Infinity,
minResolution: 0 minResolution: 0,
minZoom: -Infinity,
maxZoom: Infinity
}); });
}); });
@@ -147,13 +149,17 @@ describe('ol.layer.Group', function() {
visible: false, visible: false,
zIndex: 10, zIndex: 10,
maxResolution: 500, maxResolution: 500,
minResolution: 0.25 minResolution: 0.25,
minZoom: 1,
maxZoom: 10
}); });
expect(layerGroup.getOpacity()).to.be(0.5); expect(layerGroup.getOpacity()).to.be(0.5);
expect(layerGroup.getVisible()).to.be(false); expect(layerGroup.getVisible()).to.be(false);
expect(layerGroup.getMaxResolution()).to.be(500); expect(layerGroup.getMaxResolution()).to.be(500);
expect(layerGroup.getMinResolution()).to.be(0.25); expect(layerGroup.getMinResolution()).to.be(0.25);
expect(layerGroup.getMinZoom()).to.be(1);
expect(layerGroup.getMaxZoom()).to.be(10);
expect(layerGroup.getLayerState()).to.eql({ expect(layerGroup.getLayerState()).to.eql({
layer: layerGroup, layer: layerGroup,
opacity: 0.5, opacity: 0.5,
@@ -164,7 +170,9 @@ describe('ol.layer.Group', function() {
extent: undefined, extent: undefined,
zIndex: 10, zIndex: 10,
maxResolution: 500, maxResolution: 500,
minResolution: 0.25 minResolution: 0.25,
minZoom: 1,
maxZoom: 10
}); });
expect(layerGroup.getLayers()).to.be.a(Collection); expect(layerGroup.getLayers()).to.be.a(Collection);
expect(layerGroup.getLayers().getLength()).to.be(1); expect(layerGroup.getLayers().getLength()).to.be(1);
@@ -206,7 +214,9 @@ describe('ol.layer.Group', function() {
extent: groupExtent, extent: groupExtent,
zIndex: 0, zIndex: 0,
maxResolution: 500, maxResolution: 500,
minResolution: 0.25 minResolution: 0.25,
minZoom: -Infinity,
maxZoom: Infinity
}); });
expect(layerGroup.getLayers()).to.be.a(Collection); expect(layerGroup.getLayers()).to.be.a(Collection);
expect(layerGroup.getLayers().getLength()).to.be(1); expect(layerGroup.getLayers().getLength()).to.be(1);
@@ -237,6 +247,8 @@ describe('ol.layer.Group', function() {
layerGroup.setExtent(groupExtent); layerGroup.setExtent(groupExtent);
layerGroup.setMaxResolution(500); layerGroup.setMaxResolution(500);
layerGroup.setMinResolution(0.25); layerGroup.setMinResolution(0.25);
layerGroup.setMinZoom(5);
layerGroup.setMaxZoom(10);
expect(layerGroup.getLayerState()).to.eql({ expect(layerGroup.getLayerState()).to.eql({
layer: layerGroup, layer: layerGroup,
opacity: 0.3, opacity: 0.3,
@@ -247,7 +259,9 @@ describe('ol.layer.Group', function() {
extent: groupExtent, extent: groupExtent,
zIndex: 10, zIndex: 10,
maxResolution: 500, maxResolution: 500,
minResolution: 0.25 minResolution: 0.25,
minZoom: 5,
maxZoom: 10
}); });
}); });
@@ -264,7 +278,9 @@ describe('ol.layer.Group', function() {
extent: undefined, extent: undefined,
zIndex: 0, zIndex: 0,
maxResolution: Infinity, maxResolution: Infinity,
minResolution: 0 minResolution: 0,
minZoom: -Infinity,
maxZoom: Infinity
}); });
layerGroup.setOpacity(3); layerGroup.setOpacity(3);
@@ -279,7 +295,9 @@ describe('ol.layer.Group', function() {
extent: undefined, extent: undefined,
zIndex: 0, zIndex: 0,
maxResolution: Infinity, maxResolution: Infinity,
minResolution: 0 minResolution: 0,
minZoom: -Infinity,
maxZoom: Infinity
}); });
}); });
@@ -452,12 +470,58 @@ describe('ol.layer.Group', function() {
extent: undefined, extent: undefined,
zIndex: 0, zIndex: 0,
maxResolution: 150, maxResolution: 150,
minResolution: 0.25 minResolution: 0.25,
minZoom: -Infinity,
maxZoom: Infinity
}); });
layerGroup.dispose(); layerGroup.dispose();
}); });
it('returns max minZoom', function() {
const group = new LayerGroup({
minZoom: 5,
layers: [
new Layer({
source: new Source({
projection: 'EPSG:4326'
})
}),
new Layer({
source: new Source({
projection: 'EPSG:4326'
}),
minZoom: 10
})
]
});
expect(group.getLayerStatesArray()[0].minZoom).to.be(5);
expect(group.getLayerStatesArray()[1].minZoom).to.be(10);
});
it('returns min maxZoom of layers', function() {
const group = new LayerGroup({
maxZoom: 5,
layers: [
new Layer({
source: new Source({
projection: 'EPSG:4326'
})
}),
new Layer({
source: new Source({
projection: 'EPSG:4326'
}),
maxZoom: 2
})
]
});
expect(group.getLayerStatesArray()[0].maxZoom).to.be(5);
expect(group.getLayerStatesArray()[1].maxZoom).to.be(2);
});
}); });
}); });

View File

@@ -1,5 +1,5 @@
import Map from '../../../../src/ol/Map.js'; import Map from '../../../../src/ol/Map.js';
import Layer, {visibleAtResolution} from '../../../../src/ol/layer/Layer.js'; import Layer, {inView} from '../../../../src/ol/layer/Layer.js';
import {get as getProjection} from '../../../../src/ol/proj.js'; import {get as getProjection} from '../../../../src/ol/proj.js';
import RenderEvent from '../../../../src/ol/render/Event.js'; import RenderEvent from '../../../../src/ol/render/Event.js';
import Source from '../../../../src/ol/source/Source.js'; import Source from '../../../../src/ol/source/Source.js';
@@ -43,6 +43,14 @@ describe('ol.layer.Layer', function() {
expect(layer.getMinResolution()).to.be(0); expect(layer.getMinResolution()).to.be(0);
}); });
it('provides default min zoom', function() {
expect(layer.getMinZoom()).to.be(-Infinity);
});
it('provides default max zoom', function() {
expect(layer.getMaxZoom()).to.be(Infinity);
});
it('provides default layerState', function() { it('provides default layerState', function() {
expect(layer.getLayerState()).to.eql({ expect(layer.getLayerState()).to.eql({
layer: layer, layer: layer,
@@ -54,7 +62,9 @@ describe('ol.layer.Layer', function() {
extent: undefined, extent: undefined,
zIndex: 0, zIndex: 0,
maxResolution: Infinity, maxResolution: Infinity,
minResolution: 0 minResolution: 0,
minZoom: -Infinity,
maxZoom: Infinity
}); });
}); });
@@ -72,6 +82,8 @@ describe('ol.layer.Layer', function() {
zIndex: 10, zIndex: 10,
maxResolution: 500, maxResolution: 500,
minResolution: 0.25, minResolution: 0.25,
minZoom: 1,
maxZoom: 10,
foo: 42 foo: 42
}); });
@@ -79,6 +91,8 @@ describe('ol.layer.Layer', function() {
expect(layer.getVisible()).to.be(false); expect(layer.getVisible()).to.be(false);
expect(layer.getMaxResolution()).to.be(500); expect(layer.getMaxResolution()).to.be(500);
expect(layer.getMinResolution()).to.be(0.25); expect(layer.getMinResolution()).to.be(0.25);
expect(layer.getMinZoom()).to.be(1);
expect(layer.getMaxZoom()).to.be(10);
expect(layer.get('foo')).to.be(42); expect(layer.get('foo')).to.be(42);
expect(layer.getLayerState()).to.eql({ expect(layer.getLayerState()).to.eql({
layer: layer, layer: layer,
@@ -90,7 +104,9 @@ describe('ol.layer.Layer', function() {
extent: undefined, extent: undefined,
zIndex: 10, zIndex: 10,
maxResolution: 500, maxResolution: 500,
minResolution: 0.25 minResolution: 0.25,
minZoom: 1,
maxZoom: 10
}); });
layer.dispose(); layer.dispose();
@@ -108,7 +124,7 @@ describe('ol.layer.Layer', function() {
}); });
}); });
describe('visibleAtResolution', function() { describe('inView', function() {
let layer; let layer;
beforeEach(function() { beforeEach(function() {
@@ -123,36 +139,196 @@ describe('ol.layer.Layer', function() {
layer.dispose(); layer.dispose();
}); });
it('returns false if layer is not visible', function() { const cases = [{
layer.setVisible(false); when: 'layer is not visible',
layer.setMinResolution(3); visible: false,
layer.setMaxResolution(5); view: {
const layerState = layer.getLayerState(); resolution: 4, zoom: 4
expect(visibleAtResolution(layerState, 4)).to.be(false); },
}); inView: false
}, {
when: 'layer is not visible (with min/max zoom and resolution)',
visible: false,
minZoom: 2,
maxZoom: 6,
minResolution: 2,
maxResolution: 6,
view: {
resolution: 4, zoom: 4
},
inView: false
}, {
when: 'view zoom is less than minZoom',
minZoom: 2,
view: {
resolution: 1, zoom: 1
},
inView: false
}, {
when: 'view zoom is less than minZoom (with maxZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 1, zoom: 1
},
inView: false
}, {
when: 'view zoom is equal to minZoom',
minZoom: 2,
view: {
resolution: 2, zoom: 2
},
inView: false
}, {
when: 'view zoom is equal to minZoom (with maxZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 2, zoom: 2
},
inView: false
}, {
when: 'view zoom is greater than minZoom',
minZoom: 2,
view: {
resolution: 3, zoom: 3
},
inView: true
}, {
when: 'view zoom is greater than minZoom (with maxZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 3, zoom: 3
},
inView: true
}, {
when: 'view zoom is equal to maxZoom',
maxZoom: 4,
view: {
resolution: 4, zoom: 4
},
inView: true
}, {
when: 'view zoom is equal to maxZoom (with minZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 4, zoom: 4
},
inView: true
}, {
when: 'view zoom is greater than maxZoom',
maxZoom: 4,
view: {
resolution: 5, zoom: 5
},
inView: false
}, {
when: 'view zoom is greater than maxZoom (with minZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 5, zoom: 5
},
inView: false
}, {
when: 'view resolution is less than minResolution',
minResolution: 2,
view: {
resolution: 1, zoom: 1
},
inView: false
}, {
when: 'view resolution is less than minResolution (with maxResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 1, zoom: 1
},
inView: false
}, {
when: 'view resolution is equal to minResolution',
minResolution: 2,
view: {
resolution: 2, zoom: 2
},
inView: true
}, {
when: 'view resolution is equal to minResolution (with maxResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 2, zoom: 2
},
inView: true
}, {
when: 'view resolution is greater than minResolution',
minResolution: 2,
view: {
resolution: 3, zoom: 3
},
inView: true
}, {
when: 'view resolution is greater than minResolution (with maxResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 3, zoom: 3
},
inView: true
}, {
when: 'view resolution is equal to maxResolution',
maxResolution: 4,
view: {
resolution: 4, zoom: 4
},
inView: false
}, {
when: 'view resolution is equal to maxResolution (with minResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 4, zoom: 4
},
inView: false
}, {
when: 'view resolution is greater than maxResolution',
maxResolution: 4,
view: {
resolution: 5, zoom: 5
},
inView: false
}, {
when: 'view resolution is greater than maxResolution (with minResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 5, zoom: 5
},
inView: false
}];
it('returns false if resolution lower than minResolution', function() { cases.forEach(function(c, i) {
layer.setVisible(true); it('returns ' + c.inView + ' when ' + c.when, function() {
layer.setMinResolution(3); if ('visible' in c) {
layer.setMaxResolution(5); layer.setVisible(c.visible);
const layerState = layer.getLayerState(); }
expect(visibleAtResolution(layerState, 2)).to.be(false); if ('minZoom' in c) {
}); layer.setMinZoom(c.minZoom);
}
it('returns false if resolution greater than maxResolution', function() { if ('maxZoom' in c) {
layer.setVisible(true); layer.setMaxZoom(c.maxZoom);
layer.setMinResolution(3); }
layer.setMaxResolution(5); if ('minResolution' in c) {
const layerState = layer.getLayerState(); layer.setMinResolution(c.minResolution);
expect(visibleAtResolution(layerState, 6)).to.be(false); }
}); if ('maxResolution' in c) {
layer.setMaxResolution(c.maxResolution);
it('returns true otherwise', function() { }
layer.setVisible(true); const layerState = layer.getLayerState();
layer.setMinResolution(3); expect(inView(layerState, c.view)).to.be(c.inView);
layer.setMaxResolution(5); });
const layerState = layer.getLayerState();
expect(visibleAtResolution(layerState, 4)).to.be(true);
}); });
}); });
@@ -189,40 +365,24 @@ describe('ol.layer.Layer', function() {
extent: undefined, extent: undefined,
zIndex: 10, zIndex: 10,
maxResolution: 500, maxResolution: 500,
minResolution: 0.25 minResolution: 0.25,
minZoom: -Infinity,
maxZoom: Infinity
}); });
}); });
it('returns a layerState with clamped values', function() { it('returns a layerState with clamped values', function() {
layer.setOpacity(-1.5); layer.setOpacity(-1.5);
layer.setVisible(false); layer.setVisible(false);
expect(layer.getLayerState()).to.eql({ let state = layer.getLayerState();
layer: layer, expect(state.opacity).to.be(0);
opacity: 0, expect(state.visible).to.be(false);
visible: false,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
maxResolution: Infinity,
minResolution: 0
});
layer.setOpacity(3); layer.setOpacity(3);
layer.setVisible(true); layer.setVisible(true);
expect(layer.getLayerState()).to.eql({ state = layer.getLayerState();
layer: layer, expect(state.opacity).to.be(1);
opacity: 1, expect(state.visible).to.be(true);
visible: true,
managed: true,
hasOverlay: false,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
maxResolution: Infinity,
minResolution: 0
});
}); });
}); });