import Circle from '../../../../src/ol/geom/Circle.js'; import LineString from '../../../../src/ol/geom/LineString.js'; import Map from '../../../../src/ol/Map.js'; import Point from '../../../../src/ol/geom/Point.js'; import View, { createCenterConstraint, createResolutionConstraint, createRotationConstraint, isNoopAnimation, } from '../../../../src/ol/View.js'; import ViewHint from '../../../../src/ol/ViewHint.js'; import {clearUserProjection, useGeographic} from '../../../../src/ol/proj.js'; import {createEmpty} from '../../../../src/ol/extent.js'; describe('ol/View', function () { describe('constructor (defaults)', function () { let view; beforeEach(function () { view = new View(); }); it('creates an instance', function () { expect(view).to.be.a(View); }); it('provides default rotation', function () { expect(view.getRotation()).to.be(0); }); }); describe('parameter initialization with resolution/zoom constraints', function () { it('correctly handles max resolution constraint', function () { const view = new View({ maxResolution: 1000, resolution: 1200, }); view.setViewportSize(); expect(view.getResolution()).to.eql(1000); expect(view.targetResolution_).to.eql(1000); }); it('correctly handles min resolution constraint', function () { const view = new View({ maxResolution: 1024, minResolution: 128, resolution: 50, }); view.setViewportSize(); expect(view.getResolution()).to.eql(128); expect(view.targetResolution_).to.eql(128); }); it('correctly handles resolutions array constraint', function () { let view = new View({ resolutions: [1024, 512, 256, 128, 64, 32], resolution: 1200, }); view.setViewportSize(); expect(view.getResolution()).to.eql(1024); expect(view.targetResolution_).to.eql(1024); view = new View({ resolutions: [1024, 512, 256, 128, 64, 32], resolution: 10, }); view.setViewportSize(); expect(view.getResolution()).to.eql(32); expect(view.targetResolution_).to.eql(32); }); it('correctly handles min zoom constraint', function () { const view = new View({ minZoom: 3, zoom: 2, }); view.setViewportSize(); expect(view.getZoom()).to.eql(3); expect(view.targetResolution_).to.eql(view.getMaxResolution()); }); it('correctly handles max zoom constraint', function () { const view = new View({ maxZoom: 4, zoom: 5, }); view.setViewportSize(); expect(view.getZoom()).to.eql(4); expect(view.targetResolution_).to.eql( view.getMaxResolution() / Math.pow(2, 4) ); }); it('correctly handles extent constraint', function () { // default viewport size is 100x100 const view = new View({ extent: [0, 0, 50, 50], resolution: 1, }); view.setViewportSize(); expect(view.getResolution()).to.eql(0.5); expect(view.targetResolution_).to.eql(0.5); }); }); describe('create constraints', function () { describe('create center constraint', function () { describe('with no options', function () { it('gives a correct center constraint function', function () { const options = {}; const size = [512, 256]; const resolution = 1e5; const fn = createCenterConstraint(options); expect(fn([0, 0], resolution, size)).to.eql([0, 0]); expect(fn([42, -100], resolution, size)).to.eql([42, -100]); }); }); describe('panning off the edge of the world', function () { it('disallows going north off the world', function () { const options = { projection: 'EPSG:4326', }; const size = [360, 180]; const resolution = 0.5; const fn = createCenterConstraint(options); expect(fn([0, 0], resolution, size)).to.eql([0, 0]); expect(fn([0, 60], resolution, size)).to.eql([0, 45]); expect(fn([180, 60], resolution, size)).to.eql([180, 45]); expect(fn([-180, 60], resolution, size)).to.eql([-180, 45]); }); it('disallows going south off the world', function () { const options = { projection: 'EPSG:4326', }; const size = [360, 180]; const resolution = 0.5; const fn = createCenterConstraint(options); expect(fn([0, 0], resolution, size)).to.eql([0, 0]); expect(fn([0, -60], resolution, size)).to.eql([0, -45]); expect(fn([180, -60], resolution, size)).to.eql([180, -45]); expect(fn([-180, -60], resolution, size)).to.eql([-180, -45]); }); }); describe('with multiWorld: true', function () { it('gives a correct center constraint function', function () { const options = {multiWorld: true}; const size = [512, 256]; const resolution = 1e5; const fn = createCenterConstraint(options); expect(fn([0, 0], resolution, size)).to.eql([0, 0]); expect(fn([42, -100], resolution, size)).to.eql([42, -100]); }); }); describe('with extent option and center only', function () { it('gives a correct center constraint function', function () { const options = { extent: [0, 0, 1, 1], constrainOnlyCenter: true, }; const fn = createCenterConstraint(options); expect(fn([0, 0])).to.eql([0, 0]); expect(fn([-10, 0])).to.eql([0, 0]); expect(fn([100, 100])).to.eql([1, 1]); }); }); describe('with extent option', function () { it('gives a correct center constraint function', function () { const options = { extent: [0, 0, 1, 1], }; const fn = createCenterConstraint(options); const res = 1; const size = [0.15, 0.1]; expect(fn([0, 0], res, size)).to.eql([0.075, 0.05]); expect(fn([0.5, 0.5], res, size)).to.eql([0.5, 0.5]); expect(fn([10, 10], res, size)).to.eql([0.925, 0.95]); const overshootCenter = fn([10, 10], res, size, true); expect(overshootCenter[0] > 0.925).to.eql(true); expect(overshootCenter[1] > 0.95).to.eql(true); expect(overshootCenter[0] < 9).to.eql(true); expect(overshootCenter[1] < 9).to.eql(true); }); }); }); describe('create resolution constraint', function () { describe('with no options', function () { const size = [200, 200]; it('gives a correct resolution constraint function', function () { const options = {}; const fn = createResolutionConstraint(options).constraint; expect(fn(156543.03392804097, 0, size)).to.roughlyEqual( 156543.03392804097, 1e-9 ); expect(fn(78271.51696402048, 0, size)).to.roughlyEqual( 78271.51696402048, 1e-10 ); }); }); describe('with maxResolution, maxZoom, and zoomFactor options', function () { const size = [200, 200]; it('gives a correct resolution constraint function', function () { const options = { maxResolution: 81, maxZoom: 3, zoomFactor: 3, }; const info = createResolutionConstraint(options); const maxResolution = info.maxResolution; expect(maxResolution).to.eql(81); const minResolution = info.minResolution; expect(minResolution).to.eql(3); const fn = info.constraint; expect(fn(82, 0, size)).to.eql(81); expect(fn(81, 0, size)).to.eql(81); expect(fn(27, 0, size)).to.eql(27); expect(fn(9, 0, size)).to.eql(9); expect(fn(3, 0, size)).to.eql(3); expect(fn(2, 0, size)).to.eql(3); }); }); describe('with resolutions', function () { const size = [200, 200]; it('gives a correct resolution constraint function', function () { const options = { resolutions: [97, 76, 65, 54, 0.45], }; const info = createResolutionConstraint(options); const maxResolution = info.maxResolution; expect(maxResolution).to.eql(97); const minResolution = info.minResolution; expect(minResolution).to.eql(0.45); const fn = info.constraint; expect(fn(97, 0, size)).to.eql(97); expect(fn(76, 0, size)).to.eql(76); expect(fn(65, 0, size)).to.eql(65); expect(fn(54, 0, size)).to.eql(54); expect(fn(0.45, 0, size)).to.eql(0.45); }); }); describe('with zoom related options', function () { const defaultMaxRes = 156543.03392804097; const size = [200, 200]; function getConstraint(options) { return createResolutionConstraint(options).constraint; } it('works with only maxZoom', function () { const maxZoom = 10; const constraint = getConstraint({ maxZoom: maxZoom, }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes, 1e-9 ); expect(constraint(0, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, maxZoom), 1e-9 ); }); it('works with only minZoom', function () { const minZoom = 5; const constraint = getConstraint({ minZoom: minZoom, }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, minZoom), 1e-9 ); expect(constraint(0, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, 28), 1e-9 ); }); it('works with maxZoom and minZoom', function () { const minZoom = 2; const maxZoom = 11; const constraint = getConstraint({ minZoom: minZoom, maxZoom: maxZoom, }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, minZoom), 1e-9 ); expect(constraint(0, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(2, maxZoom), 1e-9 ); }); it('works with maxZoom, minZoom, and zoomFactor', function () { const minZoom = 4; const maxZoom = 8; const zoomFactor = 3; const constraint = getConstraint({ minZoom: minZoom, maxZoom: maxZoom, zoomFactor: zoomFactor, }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(zoomFactor, minZoom), 1e-9 ); expect(constraint(0, 0, size)).to.roughlyEqual( defaultMaxRes / Math.pow(zoomFactor, maxZoom), 1e-9 ); }); }); describe('with resolution related options', function () { const defaultMaxRes = 156543.03392804097; const size = [200, 200]; function getConstraint(options) { return createResolutionConstraint(options).constraint; } it('works with only maxResolution', function () { const maxResolution = 10e6; const constraint = getConstraint({ multiWorld: true, maxResolution: maxResolution, }); expect(constraint(maxResolution * 3, 0, size)).to.roughlyEqual( maxResolution, 1e-9 ); const minResolution = constraint(0, 0, size); const defaultMinRes = defaultMaxRes / Math.pow(2, 28); expect(minResolution).to.be.greaterThan(defaultMinRes); expect(minResolution / defaultMinRes).to.be.lessThan(2); }); it('works with only minResolution', function () { const minResolution = 100; const constraint = getConstraint({ minResolution: minResolution, }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes, 1e-9 ); const constrainedMinRes = constraint(0, 0, size); expect(constrainedMinRes).to.be.greaterThan(minResolution); expect(constrainedMinRes / minResolution).to.be.lessThan(2); }); it('works with minResolution and maxResolution', function () { const constraint = getConstraint({ maxResolution: 500, minResolution: 100, constrainResolution: true, }); expect(constraint(600, 0, size)).to.be(500); expect(constraint(500, 0, size)).to.be(500); expect(constraint(400, 0, size)).to.be(500); expect(constraint(300, 0, size)).to.be(250); expect(constraint(200, 0, size)).to.be(250); expect(constraint(100, 0, size)).to.be(125); expect(constraint(0, 0, size)).to.be(125); }); it('accepts minResolution, maxResolution, and zoomFactor', function () { const constraint = getConstraint({ maxResolution: 500, minResolution: 1, zoomFactor: 10, constrainResolution: true, }); expect(constraint(1000, 0, size)).to.be(500); expect(constraint(500, 0, size)).to.be(500); expect(constraint(100, 0, size)).to.be(50); expect(constraint(50, 0, size)).to.be(50); expect(constraint(10, 0, size)).to.be(5); expect(constraint(1, 0, size)).to.be(5); }); it('accepts extent and uses the smallest value', function () { const constraint = getConstraint({ extent: [0, 0, 4000, 6000], }); expect(constraint(1000, 0, size)).to.be(20); expect(constraint(500, 0, size)).to.be(20); expect(constraint(100, 0, size)).to.be(20); expect(constraint(50, 0, size)).to.be(20); expect(constraint(10, 0, size)).to.be(10); expect(constraint(1, 0, size)).to.be(1); }); it('accepts extent and showFullExtent and uses the larger value', function () { const constraint = getConstraint({ extent: [0, 0, 4000, 6000], showFullExtent: true, }); expect(constraint(1000, 0, size)).to.be(30); expect(constraint(500, 0, size)).to.be(30); expect(constraint(100, 0, size)).to.be(30); expect(constraint(50, 0, size)).to.be(30); expect(constraint(10, 0, size)).to.be(10); expect(constraint(1, 0, size)).to.be(1); }); }); describe('overspecified options (prefers resolution)', function () { const defaultMaxRes = 156543.03392804097; const size = [200, 200]; function getConstraint(options) { return createResolutionConstraint(options).constraint; } it('respects maxResolution over minZoom', function () { const maxResolution = 10e6; const minZoom = 8; const constraint = getConstraint({ multiWorld: true, maxResolution: maxResolution, minZoom: minZoom, }); expect(constraint(maxResolution * 3, 0, size)).to.roughlyEqual( maxResolution, 1e-9 ); const minResolution = constraint(0, 0, size); const defaultMinRes = defaultMaxRes / Math.pow(2, 28); expect(minResolution).to.be.greaterThan(defaultMinRes); expect(minResolution / defaultMinRes).to.be.lessThan(2); }); it('respects minResolution over maxZoom', function () { const minResolution = 100; const maxZoom = 50; const constraint = getConstraint({ minResolution: minResolution, maxZoom: maxZoom, }); expect(constraint(defaultMaxRes, 0, size)).to.roughlyEqual( defaultMaxRes, 1e-9 ); const constrainedMinRes = constraint(0, 0, size); expect(constrainedMinRes).to.be.greaterThan(minResolution); expect(constrainedMinRes / minResolution).to.be.lessThan(2); }); }); describe('Map views that show more than one world', function () { const defaultMaxRes = 156543.03392804097; const size = [512, 512]; const maxResolution = 160000; const resolutions = [160000, 80000, 40000, 20000, 10000, 5000]; function getConstraint(options) { return createResolutionConstraint(options).constraint; } it('are disabled by default', function () { const fn = getConstraint({}); expect(fn(defaultMaxRes, 0, size)).to.be(defaultMaxRes / 2); }); it('can be enabled by setting multiWorld to true', function () { const fn = getConstraint({ multiWorld: true, }); expect(fn(defaultMaxRes, 0, size)).to.be(defaultMaxRes); }); it('disabled, with constrainResolution', function () { const fn = getConstraint({ maxResolution: maxResolution, constrainResolution: true, }); expect(fn(defaultMaxRes, 0, size)).to.be(maxResolution / 4); }); it('enabled, with constrainResolution', function () { const fn = getConstraint({ maxResolution: maxResolution, constrainResolution: true, multiWorld: true, }); expect(fn(defaultMaxRes, 0, size)).to.be(maxResolution); }); it('disabled, with resolutions array', function () { const fn = getConstraint({ resolutions: resolutions, }); expect(fn(defaultMaxRes, 0, size)).to.be(defaultMaxRes / 2); }); it('enabled, with resolutions array', function () { const fn = getConstraint({ resolutions: resolutions, multiWorld: true, }); expect(fn(defaultMaxRes, 0, size)).to.be(defaultMaxRes); }); it('disabled, with resolutions array and constrainResolution', function () { const fn = getConstraint({ resolutions: resolutions, constrainResolution: true, }); expect(fn(defaultMaxRes, 0, size)).to.be(resolutions[2]); }); it('enabled, with resolutions array and constrainResolution', function () { const fn = getConstraint({ resolutions: resolutions, constrainResolution: true, multiWorld: true, }); expect(fn(defaultMaxRes, 0, size)).to.be(resolutions[0]); }); }); }); describe('create rotation constraint', function () { it('gives a correct rotation constraint function', function () { const options = {}; const fn = createRotationConstraint(options); expect(fn(0.01, 0)).to.eql(0); expect(fn(0.15, 0)).to.eql(0.15); }); }); }); describe('#setResolution()', function () { it('does not change center when set to undefined', function () { const center = [1, 1]; const view = new View({ center: center.slice(), resolution: 1, }); view.setResolution(undefined); expect(view.getCenter()).to.eql(center); }); }); describe('#setCenter()', function () { it('allows setting undefined center', function () { const view = new View({ center: [0, 0], resolution: 1, }); view.setCenter(undefined); expect(view.getCenter()).to.be(undefined); }); }); describe('#setHint()', function () { it('changes a view hint', function () { const view = new View({ center: [0, 0], zoom: 0, }); expect(view.getHints()).to.eql([0, 0]); expect(view.getInteracting()).to.eql(false); view.setHint(ViewHint.INTERACTING, 1); expect(view.getHints()).to.eql([0, 1]); expect(view.getInteracting()).to.eql(true); }); it('triggers the change event', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); view.on('change', function () { expect(view.getHints()).to.eql([0, 1]); expect(view.getInteracting()).to.eql(true); done(); }); view.setHint(ViewHint.INTERACTING, 1); }); }); describe('#getUpdatedOptions_()', function () { it('applies minZoom to constructor options', function () { const view = new View({ center: [0, 0], minZoom: 2, zoom: 10, }); const options = view.getUpdatedOptions_({minZoom: 3}); expect(options.center).to.eql([0, 0]); expect(options.minZoom).to.eql(3); expect(options.zoom).to.eql(10); }); it('returns the current properties with getProperties()', function () { const view = new View({ center: [0, 0], minZoom: 2, zoom: 10, }); view.setZoom(8); view.setCenter([1, 2]); view.setRotation(1); const options = view.getProperties(); expect(options.minZoom).to.eql(2); expect(options.zoom).to.eql(8); expect(options.center).to.eql([1, 2]); expect(options.rotation).to.eql(1); }); it('applies the current zoom', function () { const view = new View({ center: [0, 0], zoom: 10, }); view.setZoom(8); const options = view.getUpdatedOptions_(); expect(options.center).to.eql([0, 0]); expect(options.zoom).to.eql(8); }); it('applies the current resolution if resolution was originally supplied', function () { const view = new View({ center: [0, 0], maxResolution: 2000, resolution: 1000, }); view.setResolution(500); const options = view.getUpdatedOptions_(); expect(options.center).to.eql([0, 0]); expect(options.resolution).to.eql(500); }); it('applies the current center', function () { const view = new View({ center: [0, 0], zoom: 10, }); view.setCenter([1, 2]); const options = view.getUpdatedOptions_(); expect(options.center).to.eql([1, 2]); expect(options.zoom).to.eql(10); }); it('applies the current rotation', function () { const view = new View({ center: [0, 0], zoom: 10, }); view.setRotation(Math.PI / 6); const options = view.getUpdatedOptions_(); expect(options.center).to.eql([0, 0]); expect(options.zoom).to.eql(10); expect(options.rotation).to.eql(Math.PI / 6); }); }); describe('#animate()', function () { const originalRequestAnimationFrame = window.requestAnimationFrame; const originalCancelAnimationFrame = window.cancelAnimationFrame; beforeEach(function () { window.requestAnimationFrame = function (callback) { return setTimeout(callback, 1); }; window.cancelAnimationFrame = function (key) { return clearTimeout(key); }; }); afterEach(function () { window.requestAnimationFrame = originalRequestAnimationFrame; window.cancelAnimationFrame = originalCancelAnimationFrame; }); it('can be called to animate view properties', function (done) { const view = new View({ center: [0, 0], zoom: 5, }); view.animate( { zoom: 4, duration: 25, }, function (complete) { expect(complete).to.be(true); expect(isNaN(view.nextResolution_)).to.be(true); expect(view.getCenter()).to.eql([0, 0]); expect(view.getZoom()).to.eql(4); expect(view.getAnimating()).to.be(false); done(); } ); expect(view.getAnimating()).to.eql(true); expect(isNaN(view.nextResolution_)).to.be(false); }); it('allows duration to be zero', function (done) { const view = new View({ center: [0, 0], zoom: 5, }); view.animate( { zoom: 4, duration: 0, }, function (complete) { expect(complete).to.be(true); expect(view.getCenter()).to.eql([0, 0]); expect(view.getZoom()).to.eql(4); expect(view.getAnimating()).to.eql(false); done(); } ); }); it('immediately completes for no-op animations', function () { const view = new View({ center: [0, 0], zoom: 5, }); view.animate({ zoom: 5, center: [0, 0], duration: 25, }); expect(view.getAnimating()).to.eql(false); }); describe('Set final animation state if view is not defined.', function () { it('immediately completes if view is not defined before', function () { const view = new View(); const center = [1, 2]; const zoom = 3; const rotation = 0.4; view.animate({ zoom: zoom, center: center, rotation: rotation, duration: 25, }); expect(view.getAnimating()).to.eql(false); expect(view.getCenter()).to.eql(center); expect(view.getZoom()).to.eql(zoom); expect(view.getRotation()).to.eql(rotation); }); it('prefers zoom over resolution', function () { const view = new View(); const zoom = 1; view.animate({ center: [0, 0], zoom: zoom, resolution: 1, }); expect(view.getZoom()).to.eql(zoom); }); it('uses all animation steps to get final state', function () { const view = new View(); const center = [1, 2]; const resolution = 3; const rotation = 0.4; view.animate( {center: [2, 3]}, { center: center, rotation: 4, }, { rotation: rotation, }, {resolution: resolution} ); expect(view.getAnimating()).to.be(false); expect(view.getCenter()).to.eql(center); expect(view.getResolution()).to.be(resolution); expect(view.getRotation()).to.be(rotation); }); it('animates remaining steps after it becomes defined', function () { const view = new View(); const center = [1, 2]; const resolution = 3; view.animate( {center: [2, 3]}, { resolution: resolution, center: center, }, { rotation: 2, duration: 25, } ); expect(view.getAnimating()).to.be(true); expect(view.getCenter()).to.eql(center); expect(view.getResolution()).to.be(resolution); expect(view.getRotation()).to.roughlyEqual(0, 0.02); }); }); it('prefers zoom over resolution', function (done) { const view = new View({ center: [0, 0], zoom: 5, }); view.animate( { zoom: 4, resolution: view.getResolution() * 3, duration: 25, }, function (complete) { expect(complete).to.be(true); expect(view.getZoom()).to.be(4); done(); } ); }); it('avoids going under minResolution', function (done) { const maxZoom = 14; const view = new View({ center: [0, 0], zoom: 0, maxZoom: maxZoom, }); const minResolution = view.getMinResolution(); view.animate( { resolution: minResolution, duration: 10, }, function (complete) { expect(complete).to.be(true); expect(view.getResolution()).to.be(minResolution); expect(view.getZoom()).to.be(maxZoom); done(); } ); }); it('takes the shortest arc to the target rotation', function (done) { const view = new View({ center: [0, 0], zoom: 0, rotation: (Math.PI / 180) * 1, }); view.animate( { rotation: (Math.PI / 180) * 359, duration: 0, }, function (complete) { expect(complete).to.be(true); expect(view.getRotation()).to.roughlyEqual( (Math.PI / 180) * -1, 1e-12 ); done(); } ); }); it('normalizes rotation to angles between -180 and 180 degrees after the anmiation', function (done) { const view = new View({ center: [0, 0], zoom: 0, rotation: (Math.PI / 180) * 1, }); view.animate( { rotation: (Math.PI / 180) * -181, duration: 0, }, function (complete) { expect(complete).to.be(true); expect(view.getRotation()).to.roughlyEqual( (Math.PI / 180) * 179, 1e-12 ); done(); } ); }); it('calls a callback when animation completes', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); view.animate( { zoom: 1, duration: 25, }, function (complete) { expect(complete).to.be(true); done(); } ); }); it('allows the callback to trigger another animation', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); function firstCallback(complete) { expect(complete).to.be(true); view.animate( { zoom: 2, duration: 10, }, secondCallback ); } function secondCallback(complete) { expect(complete).to.be(true); done(); } view.animate( { zoom: 1, duration: 25, }, firstCallback ); }); it('calls callback with false when animation is interrupted', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); view.animate( { zoom: 1, duration: 25, }, function (complete) { expect(complete).to.be(false); done(); } ); view.setCenter([1, 2]); // interrupt the animation }); it('calls a callback even if animation is a no-op', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); view.animate( { zoom: 0, duration: 25, }, function (complete) { expect(complete).to.be(true); done(); } ); }); it('calls a callback if view is not defined before', function (done) { const view = new View(); view.animate( { zoom: 10, duration: 25, }, function (complete) { expect(view.getZoom()).to.be(10); expect(complete).to.be(true); done(); } ); }); it('can run multiple animations in series', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); let checked = false; view.animate( { zoom: 2, duration: 25, }, { center: [10, 10], duration: 25, }, function (complete) { expect(checked).to.be(true); expect(view.getZoom()).to.roughlyEqual(2, 1e-5); expect(view.getCenter()).to.eql([10, 10]); expect(complete).to.be(true); done(); } ); setTimeout(function () { expect(view.getCenter()).to.eql([0, 0]); checked = true; }, 10); }); it('properly sets the ANIMATING hint', function (done) { const view = new View({ center: [0, 0], zoom: 0, rotation: 0, }); let count = 3; function decrement() { --count; if (count === 0) { expect(view.getHints()[ViewHint.ANIMATING]).to.be(0); done(); } } view.animate( { center: [1, 2], duration: 25, }, decrement ); expect(view.getHints()[ViewHint.ANIMATING]).to.be(1); view.animate( { zoom: 1, duration: 25, }, decrement ); expect(view.getHints()[ViewHint.ANIMATING]).to.be(2); view.animate( { rotation: Math.PI, duration: 25, }, decrement ); expect(view.getHints()[ViewHint.ANIMATING]).to.be(3); }); it('clears the ANIMATING hint when animations are cancelled', function () { const view = new View({ center: [0, 0], zoom: 0, rotation: 0, }); view.animate({ center: [1, 2], duration: 25, }); expect(view.getHints()[ViewHint.ANIMATING]).to.be(1); view.animate({ zoom: 1, duration: 25, }); expect(view.getHints()[ViewHint.ANIMATING]).to.be(2); view.animate({ rotation: Math.PI, duration: 25, }); expect(view.getHints()[ViewHint.ANIMATING]).to.be(3); // cancel animations view.setCenter([10, 20]); expect(view.getHints()[ViewHint.ANIMATING]).to.be(0); }); it('completes multiple staggered animations run in parallel', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); let calls = 0; view.animate( { zoom: 1, duration: 25, }, function () { ++calls; } ); setTimeout(function () { expect(view.getZoom() > 0).to.be(true); expect(view.getZoom() < 1).to.be(true); expect(view.getAnimating()).to.be(true); view.animate( { zoom: 2, duration: 50, }, function () { expect(calls).to.be(1); expect(view.getZoom()).to.be(2); expect(view.getAnimating()).to.be(false); done(); } ); }, 10); }); it('completes complex animation using resolution', function (done) { const view = new View({ center: [0, 0], resolution: 2, }); let calls = 0; function onAnimateEnd() { if (calls == 2) { expect(view.getAnimating()).to.be(false); done(); } } view.animate( { center: [100, 100], duration: 50, }, function () { ++calls; expect(view.getCenter()).to.eql([100, 100]); onAnimateEnd(); } ); view.animate( { resolution: 2000, duration: 25, }, { resolution: 2, duration: 25, }, function () { ++calls; expect(view.getResolution()).to.be(2); onAnimateEnd(); } ); setTimeout(function () { expect(view.getResolution() > 2).to.be(true); expect(view.getResolution() < 2000).to.be(true); expect(view.getAnimating()).to.be(true); }, 10); setTimeout(function () { expect(view.getResolution() > 2).to.be(true); expect(view.getResolution() < 2000).to.be(true); expect(view.getAnimating()).to.be(true); }, 40); }); it('completes even though Map#setSize is called', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); const map = new Map({ view, }); map.setSize([110, 90]); view.animate( { zoom: 1, duration: 25, }, function () { expect(view.getZoom()).to.be(1); done(); } ); setTimeout(function () { map.setSize([100, 100]); }, 10); }); it('completes even though Map#updateSize is called', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); const map = new Map({ view, }); view.animate( { zoom: 1, duration: 25, }, function () { expect(view.getZoom()).to.be(1); done(); } ); setTimeout(function () { map.updateSize(); }, 10); }); }); describe('#cancelAnimations()', function () { const originalRequestAnimationFrame = window.requestAnimationFrame; const originalCancelAnimationFrame = window.cancelAnimationFrame; beforeEach(function () { window.requestAnimationFrame = function (callback) { return setTimeout(callback, 1); }; window.cancelAnimationFrame = function (key) { return clearTimeout(key); }; }); afterEach(function () { window.requestAnimationFrame = originalRequestAnimationFrame; window.cancelAnimationFrame = originalCancelAnimationFrame; }); it('cancels a currently running animation', function (done) { const view = new View({ center: [0, 0], zoom: 0, rotation: 0, }); view.animate({ rotation: 10, duration: 50, }); setTimeout(function () { expect(view.getAnimating()).to.be(true); view.once('change', function () { expect(view.getAnimating()).to.be(false); done(); }); view.cancelAnimations(); }, 10); }); it('cancels a multiple animations', function (done) { const view = new View({ center: [0, 0], zoom: 0, rotation: 0, }); view.animate( { rotation: 10, duration: 50, }, { zoom: 10, duration: 50, } ); view.animate({ center: [10, 30], duration: 100, }); setTimeout(function () { expect(view.getAnimating()).to.be(true); view.once('change', function () { expect(view.getAnimating()).to.be(false); done(); }); view.cancelAnimations(); }, 10); }); it('calls callbacks with false to indicate animations did not complete', function (done) { const view = new View({ center: [0, 0], zoom: 0, }); view.animate( { zoom: 10, duration: 50, }, function (complete) { expect(view.getAnimating()).to.be(false); expect(complete).to.be(false); done(); } ); setTimeout(function () { expect(view.getAnimating()).to.be(true); view.cancelAnimations(); }, 10); }); }); describe('#getResolutions', function () { let view; const resolutions = [512, 256, 128, 64, 32, 16]; it('returns correct resolutions', function () { view = new View({ resolutions: resolutions, }); expect(view.getResolutions()).to.be(resolutions); }); it('returns resolutions as undefined', function () { view = new View(); expect(view.getResolutions()).to.be(undefined); }); }); describe('#getZoom', function () { let view; beforeEach(function () { view = new View({ resolutions: [1024, 512, 256, 128, 64, 32, 16, 8], }); }); it('returns correct zoom levels (with resolutions array)', function () { view.setResolution(undefined); expect(view.getZoom()).to.be(undefined); view.setResolution(513); expect(view.getZoom()).to.roughlyEqual( Math.log(1024 / 513) / Math.LN2, 1e-9 ); view.setResolution(512); expect(view.getZoom()).to.be(1); view.setResolution(100); expect(view.getZoom()).to.roughlyEqual(3.35614, 1e-5); view.setResolution(65); expect(view.getZoom()).to.roughlyEqual(3.97763, 1e-5); view.setResolution(64); expect(view.getZoom()).to.be(4); view.setResolution(16); expect(view.getZoom()).to.be(6); view.setResolution(15); expect(view.getZoom()).to.roughlyEqual( Math.log(1024 / 15) / Math.LN2, 1e-9 ); }); it('works for resolution arrays with variable zoom factors', function () { const view = new View({ resolutions: [10, 5, 2, 1], }); view.setZoom(1); expect(view.getZoom()).to.be(1); view.setZoom(1.3); expect(view.getZoom()).to.be(1.3); view.setZoom(2); expect(view.getZoom()).to.be(2); view.setZoom(2.7); expect(view.getZoom()).to.be(2.7); view.setZoom(3); expect(view.getZoom()).to.be(3); }); }); describe('#getZoom() - constrained', function () { it('returns correct zoom levels', function () { const view = new View({ minZoom: 10, maxZoom: 20, }); view.setZoom(5); expect(view.getZoom()).to.be(10); view.setZoom(10); expect(view.getZoom()).to.be(10); view.setZoom(15); expect(view.getZoom()).to.be(15); view.setZoom(15.3); expect(view.getZoom()).to.be(15.3); view.setZoom(20); expect(view.getZoom()).to.be(20); view.setZoom(25); expect(view.getZoom()).to.be(20); }); }); describe('#getZoom() - overspecified', function () { it('gives maxResolution precedence over minZoom', function () { const view = new View({ maxResolution: 100, minZoom: 2, // this should get ignored }); view.setResolution(100); expect(view.getZoom()).to.be(0); view.setZoom(0); expect(view.getResolution()).to.be(100); }); }); describe('#getZoomForResolution', function () { it('returns correct zoom levels', function () { const view = new View(); const max = view.getMaxResolution(); expect(view.getZoomForResolution(max)).to.be(0); expect(view.getZoomForResolution(max / 2)).to.be(1); expect(view.getZoomForResolution(max / 4)).to.be(2); expect(view.getZoomForResolution(2 * max)).to.be(-1); }); it('returns correct zoom levels for specifically configured resolutions', function () { const view = new View({ resolutions: [10, 8, 6, 4, 2], }); expect(view.getZoomForResolution(10)).to.be(0); expect(view.getZoomForResolution(8)).to.be(1); expect(view.getZoomForResolution(6)).to.be(2); expect(view.getZoomForResolution(4)).to.be(3); expect(view.getZoomForResolution(2)).to.be(4); }); }); describe('#getResolutionForZoom', function () { it('returns correct zoom resolution', function () { const view = new View(); const max = view.getMaxZoom(); const min = view.getMinZoom(); expect(view.getResolutionForZoom(max)).to.be(view.getMinResolution()); expect(view.getResolutionForZoom(max + 1)).to.be( view.getMinResolution() / 2 ); expect(view.getResolutionForZoom(min)).to.be(view.getMaxResolution()); expect(view.getResolutionForZoom(min - 1)).to.be( view.getMaxResolution() * 2 ); }); it('returns correct zoom levels for specifically configured resolutions', function () { const view = new View({ resolutions: [10, 8, 6, 4, 2], }); expect(view.getResolutionForZoom(-1)).to.be(10); expect(view.getResolutionForZoom(0)).to.be(10); expect(view.getResolutionForZoom(1)).to.be(8); expect(view.getResolutionForZoom(2)).to.be(6); expect(view.getResolutionForZoom(3)).to.be(4); expect(view.getResolutionForZoom(4)).to.be(2); expect(view.getResolutionForZoom(5)).to.be(2); }); it('returns correct zoom levels for resolutions with variable zoom levels', function () { const view = new View({ resolutions: [50, 10, 5, 2.5, 1.25, 0.625], }); expect(view.getResolutionForZoom(-1)).to.be(50); expect(view.getResolutionForZoom(0)).to.be(50); expect(view.getResolutionForZoom(0.5)).to.be(50 / Math.pow(5, 0.5)); expect(view.getResolutionForZoom(1)).to.be(10); expect(view.getResolutionForZoom(2)).to.be(5); expect(view.getResolutionForZoom(2.75)).to.be(5 / Math.pow(2, 0.75)); expect(view.getResolutionForZoom(3)).to.be(2.5); expect(view.getResolutionForZoom(4)).to.be(1.25); expect(view.getResolutionForZoom(5)).to.be(0.625); expect(view.getResolutionForZoom(6)).to.be(0.625); }); }); describe('#getMaxZoom', function () { it('returns the zoom level for the min resolution', function () { const view = new View(); expect(view.getMaxZoom()).to.be( view.getZoomForResolution(view.getMinResolution()) ); }); it('works for a view configured with a maxZoom', function () { const view = new View({ maxZoom: 10, }); expect(view.getMaxZoom()).to.be(10); }); }); describe('#getMinZoom', function () { it('returns the zoom level for the max resolution', function () { const view = new View(); expect(view.getMinZoom()).to.be( view.getZoomForResolution(view.getMaxResolution()) ); }); it('works for views configured with a minZoom', function () { const view = new View({ minZoom: 3, }); expect(view.getMinZoom()).to.be(3); }); }); describe('#setMaxZoom', function () { describe('with resolutions property in view', function () { it('changes the zoom level when the level is over max zoom', function () { const view = new View({ resolutions: [100000, 50000, 25000, 12500, 6250, 3125], zoom: 4, }); view.setMaxZoom(2); expect(view.getZoom()).to.be(2); }); }); describe('with no resolutions property in view', function () { it('changes the zoom level when the level is over max zoom', function () { const view = new View({ zoom: 4, }); view.setMaxZoom(2); expect(view.getZoom()).to.be(2); }); }); }); describe('#setMinZoom', function () { describe('with resolutions property in view', function () { it('changes the zoom level when the level is under min zoom', function () { const view = new View({ resolutions: [100000, 50000, 25000, 12500, 6250, 3125], zoom: 4, }); view.setMinZoom(5); expect(view.getZoom()).to.be(5); }); }); describe('with no resolutions property in view', function () { it('changes the zoom level when the level is under min zoom', function () { const view = new View({ zoom: 4, }); view.setMinZoom(5); expect(view.getZoom()).to.be(5); }); }); }); describe('#calculateExtent', function () { it('returns the expected extent', function () { const view = new View({ resolutions: [512], zoom: 0, center: [0, 0], }); const extent = view.calculateExtent([100, 200]); expect(extent[0]).to.be(-25600); expect(extent[1]).to.be(-51200); expect(extent[2]).to.be(25600); expect(extent[3]).to.be(51200); }); it('returns the expected extent with rotation', function () { const view = new View({ resolutions: [512], zoom: 0, center: [0, 0], rotation: Math.PI / 2, }); const extent = view.calculateExtent([100, 200]); expect(extent[0]).to.roughlyEqual(-51200, 1e-9); expect(extent[1]).to.roughlyEqual(-25600, 1e-9); expect(extent[2]).to.roughlyEqual(51200, 1e-9); expect(extent[3]).to.roughlyEqual(25600, 1e-9); }); it('works with a view padding', function () { const view = new View({ resolutions: [1], zoom: 0, center: [0, 0], padding: [10, 20, 30, 40], }); let extent = view.calculateExtent(); expect(extent).to.eql([-20, -30, 20, 30]); view.padding = [0, 0, 0, 0]; extent = view.calculateExtent(); expect(extent).to.eql([-60, -60, 40, 40]); }); }); describe('#getViewportSize_()', function () { let map, target; beforeEach(function () { target = document.createElement('div'); target.style.width = '200px'; target.style.height = '150px'; document.body.appendChild(target); map = new Map({ target: target, }); }); afterEach(function () { map.setTarget(null); document.body.removeChild(target); }); it('correctly initializes the viewport size', function () { const size = map.getView().getViewportSize_(); expect(size).to.eql([200, 150]); }); it('correctly updates the viewport size', function () { target.style.width = '300px'; target.style.height = '200px'; map.updateSize(); const size = map.getView().getViewportSize_(); expect(size).to.eql([300, 200]); }); it('calculates the size correctly', function () { let size = map.getView().getViewportSize_(Math.PI / 2); expect(size[0]).to.roughlyEqual(150, 1e-9); expect(size[1]).to.roughlyEqual(200, 1e-9); size = map.getView().getViewportSize_(Math.PI); expect(size[0]).to.roughlyEqual(200, 1e-9); expect(size[1]).to.roughlyEqual(150, 1e-9); }); }); describe('#getViewportSizeMinusPadding_()', function () { let map, target; beforeEach(function () { target = document.createElement('div'); target.style.width = '200px'; target.style.height = '150px'; document.body.appendChild(target); map = new Map({ target: target, }); }); afterEach(function () { map.setTarget(null); document.body.removeChild(target); }); it('same as getViewportSize_ when no padding is set', function () { const size = map.getView().getViewportSizeMinusPadding_(); expect(size).to.eql(map.getView().getViewportSize_()); }); it('correctly updates when the padding is changed', function () { map.getView().padding = [1, 2, 3, 4]; const size = map.getView().getViewportSizeMinusPadding_(); expect(size).to.eql([194, 146]); }); }); describe('fit', function () { const originalRequestAnimationFrame = window.requestAnimationFrame; const originalCancelAnimationFrame = window.cancelAnimationFrame; beforeEach(function () { window.requestAnimationFrame = function (callback) { return setTimeout(callback, 1); }; window.cancelAnimationFrame = function (key) { return clearTimeout(key); }; }); afterEach(function () { window.requestAnimationFrame = originalRequestAnimationFrame; window.cancelAnimationFrame = originalCancelAnimationFrame; }); let view; beforeEach(function () { view = new View({ center: [0, 0], resolutions: [200, 100, 50, 20, 10, 5, 2, 1], zoom: 5, }); }); it('fits correctly to the geometry (with unconstrained resolution)', function () { view.fit( new LineString([ [6000, 46000], [6000, 47100], [7000, 46000], ]), {size: [200, 200], padding: [100, 0, 0, 100]} ); expect(view.getResolution()).to.be(11); expect(view.getCenter()[0]).to.be(5950); expect(view.getCenter()[1]).to.be(47100); view.fit(new Circle([6000, 46000], 1000), {size: [200, 200]}); expect(view.getResolution()).to.be(10); expect(view.getCenter()[0]).to.be(6000); expect(view.getCenter()[1]).to.be(46000); view.setRotation(Math.PI / 8); view.fit(new Circle([6000, 46000], 1000), {size: [200, 200]}); expect(view.getResolution()).to.roughlyEqual(10, 1e-9); expect(view.getCenter()[0]).to.roughlyEqual(6000, 1e-9); expect(view.getCenter()[1]).to.roughlyEqual(46000, 1e-9); view.setRotation(Math.PI / 4); view.fit( new LineString([ [6000, 46000], [6000, 47100], [7000, 46000], ]), {size: [200, 200], padding: [100, 0, 0, 100]} ); expect(view.getResolution()).to.roughlyEqual(14.849242404917458, 1e-9); expect(view.getCenter()[0]).to.roughlyEqual(5200, 1e-9); expect(view.getCenter()[1]).to.roughlyEqual(46300, 1e-9); }); it('fits correctly to the geometry', function () { view.setConstrainResolution(true); view.fit( new LineString([ [6000, 46000], [6000, 47100], [7000, 46000], ]), {size: [200, 200], padding: [100, 0, 0, 100]} ); expect(view.getResolution()).to.be(20); expect(view.getCenter()[0]).to.be(5500); expect(view.getCenter()[1]).to.be(47550); view.fit( new LineString([ [6000, 46000], [6000, 47100], [7000, 46000], ]), {size: [200, 200], padding: [100, 0, 0, 100], nearest: true} ); expect(view.getResolution()).to.be(10); expect(view.getCenter()[0]).to.be(6000); expect(view.getCenter()[1]).to.be(47050); view.fit(new Point([6000, 46000]), { size: [200, 200], padding: [100, 0, 0, 100], minResolution: 2, }); expect(view.getResolution()).to.be(2); expect(view.getCenter()[0]).to.be(5900); expect(view.getCenter()[1]).to.be(46100); view.fit(new Point([6000, 46000]), { size: [200, 200], padding: [100, 0, 0, 100], maxZoom: 6, }); expect(view.getResolution()).to.be(2); expect(view.getZoom()).to.be(6); expect(view.getCenter()[0]).to.be(5900); expect(view.getCenter()[1]).to.be(46100); }); it('fits correctly to the extent', function () { view.fit([1000, 1000, 2000, 2000], {size: [200, 200]}); expect(view.getResolution()).to.be(5); expect(view.getCenter()[0]).to.be(1500); expect(view.getCenter()[1]).to.be(1500); }); it('fits correctly to the extent when a padding is configured', function () { view.padding = [100, 0, 0, 100]; view.setViewportSize([200, 200]); view.fit([1000, 1000, 2000, 2000]); expect(view.getResolution()).to.be(10); expect(view.getCenter()[0]).to.be(1500); expect(view.getCenter()[1]).to.be(1500); }); it('fits correctly to the extent when a view extent is configured', function () { view.set('extent', [1500, 0, 2500, 10000]); view.applyOptions_(view.getProperties()); view.fit([1000, 1000, 2000, 2000]); expect(view.calculateExtent()).eql([1500, 1000, 2500, 2000]); }); it('throws on invalid geometry/extent value', function () { expect(function () { view.fit(true, [200, 200]); }).to.throwException(); }); it('throws on empty extent', function () { expect(function () { view.fit(createEmpty()); }).to.throwException(); }); it('animates when duration is defined', function (done) { view.fit( new LineString([ [6000, 46000], [6000, 47100], [7000, 46000], ]), { size: [200, 200], padding: [100, 0, 0, 100], duration: 25, } ); expect(view.getAnimating()).to.eql(true); setTimeout(function () { expect(view.getResolution()).to.be(11); expect(view.getCenter()[0]).to.be(5950); expect(view.getCenter()[1]).to.be(47100); expect(view.getAnimating()).to.eql(false); done(); }, 50); }); it('calls a callback when duration is not defined', function (done) { view.fit( new LineString([ [6000, 46000], [6000, 47100], [7000, 46000], ]), { callback: function (complete) { expect(complete).to.be(true); done(); }, } ); }); it('calls a callback when animation completes', function (done) { view.fit( new LineString([ [6000, 46000], [6000, 47100], [7000, 46000], ]), { duration: 25, callback: function (complete) { expect(complete).to.be(true); done(); }, } ); }); }); describe('centerOn', function () { let view; beforeEach(function () { view = new View({ resolutions: [200, 100, 50, 20, 10, 5, 2, 1], }); }); it('fit correctly to the coordinates', function () { view.setResolution(10); view.centerOn([6000, 46000], [400, 400], [300, 300]); expect(view.getCenter()[0]).to.be(5000); expect(view.getCenter()[1]).to.be(47000); view.setRotation(Math.PI / 4); view.centerOn([6000, 46000], [400, 400], [300, 300]); expect(view.getCenter()[0]).to.roughlyEqual(4585.78643762691, 1e-9); expect(view.getCenter()[1]).to.roughlyEqual(46000, 1e-9); }); }); describe('#beginInteraction() and endInteraction()', function () { let view; beforeEach(function () { view = new View(); }); it('correctly changes the view hint', function () { view.beginInteraction(); expect(view.getHints()[1]).to.be(1); view.beginInteraction(); expect(view.getHints()[1]).to.be(2); view.endInteraction(); view.endInteraction(); expect(view.getHints()[1]).to.be(0); }); }); describe('#getConstrainedZoom()', function () { let view; it('works correctly without constraint', function () { view = new View({ zoom: 0, }); expect(view.getConstrainedZoom(3)).to.be(3); }); it('works correctly with resolution constraints', function () { view = new View({ zoom: 4, minZoom: 4, maxZoom: 8, }); expect(view.getConstrainedZoom(3)).to.be(4); expect(view.getConstrainedZoom(10)).to.be(8); }); it('works correctly with a specific resolution set', function () { view = new View({ zoom: 0, resolutions: [512, 256, 128, 64, 32, 16, 8], }); expect(view.getConstrainedZoom(0)).to.be(0); expect(view.getConstrainedZoom(4)).to.be(4); expect(view.getConstrainedZoom(8)).to.be(6); }); }); describe('#getConstrainedResolution()', function () { let view; const defaultMaxRes = 156543.03392804097; it('works correctly by snapping to power of 2', function () { view = new View(); expect(view.getConstrainedResolution(1000000)).to.be(defaultMaxRes); expect(view.getConstrainedResolution(defaultMaxRes / 8)).to.be( defaultMaxRes / 8 ); }); it('works correctly by snapping to a custom zoom factor', function () { view = new View({ maxResolution: 2500, zoomFactor: 5, maxZoom: 4, constrainResolution: true, }); expect(view.getConstrainedResolution(90, 1)).to.be(100); expect(view.getConstrainedResolution(90, -1)).to.be(20); expect(view.getConstrainedResolution(20)).to.be(20); expect(view.getConstrainedResolution(5)).to.be(4); expect(view.getConstrainedResolution(1)).to.be(4); }); it('works correctly with a specific resolution set', function () { view = new View({ zoom: 0, resolutions: [512, 256, 128, 64, 32, 16, 8], constrainResolution: true, }); expect(view.getConstrainedResolution(1000, 1)).to.be(512); expect(view.getConstrainedResolution(260, 1)).to.be(512); expect(view.getConstrainedResolution(260)).to.be(256); expect(view.getConstrainedResolution(30)).to.be(32); expect(view.getConstrainedResolution(30, -1)).to.be(16); expect(view.getConstrainedResolution(4, -1)).to.be(8); }); }); describe('#adjustRotation()', function () { it('changes view rotation with anchor', function () { const view = new View({ resolution: 1, center: [0, 0], }); view.adjustRotation(Math.PI / 2); expect(view.getRotation()).to.be(Math.PI / 2); expect(view.getCenter()).to.eql([0, 0]); view.adjustRotation(-Math.PI); expect(view.getRotation()).to.be(-Math.PI / 2); expect(view.getCenter()).to.eql([0, 0]); view.adjustRotation(Math.PI / 3, [50, 0]); expect(view.getRotation()).to.roughlyEqual(-Math.PI / 6, 1e-9); expect(view.getCenter()[0]).to.roughlyEqual( 50 * (1 - Math.cos(Math.PI / 3)), 1e-9 ); expect(view.getCenter()[1]).to.roughlyEqual( -50 * Math.sin(Math.PI / 3), 1e-9 ); }); it('does not change view parameters if rotation is disabled', function () { const view = new View({ resolution: 1, enableRotation: false, center: [0, 0], }); view.adjustRotation(Math.PI / 2); expect(view.getRotation()).to.be(0); expect(view.getCenter()).to.eql([0, 0]); view.adjustRotation(-Math.PI * 3, [-50, 0]); expect(view.getRotation()).to.be(0); expect(view.getCenter()).to.eql([0, 0]); }); }); describe('#adjustZoom()', function () { it('changes view resolution', function () { const view = new View({ resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25], }); view.adjustZoom(1); expect(view.getResolution()).to.be(0.5); view.adjustZoom(-1); expect(view.getResolution()).to.be(1); view.adjustZoom(2); expect(view.getResolution()).to.be(0.25); view.adjustZoom(-2); expect(view.getResolution()).to.be(1); }); it('changes view resolution and center relative to the anchor', function () { const view = new View({ center: [0, 0], resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25], }); view.adjustZoom(1, [10, 10]); expect(view.getCenter()).to.eql([5, 5]); view.adjustZoom(-1, [0, 0]); expect(view.getCenter()).to.eql([10, 10]); view.adjustZoom(2, [0, 0]); expect(view.getCenter()).to.eql([2.5, 2.5]); view.adjustZoom(-2, [0, 0]); expect(view.getCenter()).to.eql([10, 10]); }); it('changes view resolution and center relative to the anchor, while respecting the extent (center only)', function () { const view = new View({ center: [0, 0], extent: [-2.5, -2.5, 2.5, 2.5], constrainOnlyCenter: true, resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25], }); view.adjustZoom(1, [10, 10]); expect(view.getCenter()).to.eql([2.5, 2.5]); view.adjustZoom(-1, [0, 0]); expect(view.getCenter()).to.eql([2.5, 2.5]); view.adjustZoom(2, [10, 10]); expect(view.getCenter()).to.eql([2.5, 2.5]); view.adjustZoom(-2, [0, 0]); expect(view.getCenter()).to.eql([2.5, 2.5]); }); it('changes view resolution and center relative to the anchor, while respecting the extent', function () { const map = new Map({}); const view = new View({ center: [50, 50], extent: [0, 0, 100, 100], resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25, 0.125], }); map.setView(view); view.adjustZoom(1, [100, 100]); expect(view.getCenter()).to.eql([75, 75]); view.adjustZoom(-1, [75, 75]); expect(view.getCenter()).to.eql([50, 50]); view.adjustZoom(2, [100, 100]); expect(view.getCenter()).to.eql([87.5, 87.5]); view.adjustZoom(-3, [0, 0]); expect(view.getCenter()).to.eql([50, 50]); expect(view.getResolution()).to.eql(1); }); it('changes view resolution and center relative to the anchor, while respecting the extent (rotated)', function () { const map = new Map({}); const view = new View({ center: [50, 50], extent: [-100, -100, 100, 100], resolution: 1, resolutions: [2, 1, 0.5, 0.25, 0.125], rotation: Math.PI / 4, }); map.setView(view); const halfSize = 100 * Math.SQRT1_2; view.adjustZoom(1, [100, 100]); expect(view.getCenter()).to.eql([100 - halfSize / 2, 100 - halfSize / 2]); view.setCenter([0, 50]); view.adjustZoom(-1, [0, 0]); expect(view.getCenter()).to.eql([0, 100 - halfSize]); }); }); describe('#adjustZoom() - useGeographic', () => { beforeEach(useGeographic); afterEach(clearUserProjection); it('changes view resolution', () => { const view = new View({ resolution: 1, resolutions: [4, 2, 1, 0.5, 0.25], }); view.adjustZoom(1); expect(view.getResolution()).to.be(0.5); view.adjustZoom(-1); expect(view.getResolution()).to.be(1); view.adjustZoom(2); expect(view.getResolution()).to.be(0.25); view.adjustZoom(-2); expect(view.getResolution()).to.be(1); }); it('changes view resolution and center relative to the anchor', function () { const view = new View({ center: [0, 0], zoom: 0, }); let center; view.adjustZoom(1, [90, 45]); center = view.getCenter(); expect(center[0]).to.be(45); expect(center[1]).to.roughlyEqual(24.4698, 1e-4); view.adjustZoom(-1, [90, 45]); center = view.getCenter(); expect(center[0]).to.roughlyEqual(0, 1e-10); expect(center[1]).to.roughlyEqual(0, 1e-10); view.adjustZoom(2, [-90, -45]); center = view.getCenter(); expect(center[0]).to.be(-67.5); expect(center[1]).to.roughlyEqual(-35.3836, 1e-4); view.adjustZoom(-2, [-90, -45]); center = view.getCenter(); expect(center[0]).to.roughlyEqual(0, 1e-10); expect(center[1]).to.roughlyEqual(0, 1e-10); }); }); describe('#getCenter', function () { let view; beforeEach(function () { view = new View({ center: [0, 0], resolutions: [1], zoom: 0, }); view.setViewportSize([100, 100]); }); it('Correctly shifts the viewport center when a padding is set', function () { view.padding = [50, 0, 0, 50]; expect(view.getCenter()).to.eql([25, -25]); }); }); }); describe('does not start unexpected animations during interaction', function () { let map; beforeEach(function () { map = new Map({ target: createMapDiv(512, 256), }); }); afterEach(function () { disposeMap(map); }); it('works when initialized with #setCenter() and #setZoom()', function (done) { const view = map.getView(); let callCount = 0; view.on('change:resolution', function () { ++callCount; }); view.setCenter([0, 0]); view.setZoom(0); view.beginInteraction(); view.endInteraction(); setTimeout(function () { expect(callCount).to.be(1); done(); }, 500); }); it('works when initialized with #animate()', function (done) { const view = map.getView(); let callCount = 0; view.on('change:resolution', function () { ++callCount; }); view.animate({ center: [0, 0], zoom: 0, }); view.beginInteraction(); view.endInteraction(); setTimeout(function () { expect(callCount).to.be(1); done(); }, 500); }); }); describe('ol.View.isNoopAnimation()', function () { const cases = [ { animation: { sourceCenter: [0, 0], targetCenter: [0, 0], sourceResolution: 1, targetResolution: 1, sourceRotation: 0, targetRotation: 0, }, noop: true, }, { animation: { sourceCenter: [0, 0], targetCenter: [0, 1], sourceResolution: 1, targetResolution: 1, sourceRotation: 0, targetRotation: 0, }, noop: false, }, { animation: { sourceCenter: [0, 0], targetCenter: [0, 0], sourceResolution: 1, targetResolution: 0, sourceRotation: 0, targetRotation: 0, }, noop: false, }, { animation: { sourceCenter: [0, 0], targetCenter: [0, 0], sourceResolution: 1, targetResolution: 1, sourceRotation: 0, targetRotation: 1, }, noop: false, }, { animation: { sourceCenter: [0, 0], targetCenter: [0, 0], }, noop: true, }, { animation: { sourceCenter: [1, 0], targetCenter: [0, 0], }, noop: false, }, { animation: { sourceResolution: 1, targetResolution: 1, }, noop: true, }, { animation: { sourceResolution: 0, targetResolution: 1, }, noop: false, }, { animation: { sourceRotation: 10, targetRotation: 10, }, noop: true, }, { animation: { sourceRotation: 0, targetRotation: 10, }, noop: false, }, ]; cases.forEach(function (c, i) { it('works for case ' + i, function () { const noop = isNoopAnimation(c.animation); expect(noop).to.equal(c.noop); }); }); });