import Collection from '../../../../src/ol/Collection.js'; import Control from '../../../../src/ol/control/Control.js'; import DoubleClickZoom from '../../../../src/ol/interaction/DoubleClickZoom.js'; import DragPan from '../../../../src/ol/interaction/DragPan.js'; import Feature from '../../../../src/ol/Feature.js'; import GeoJSON from '../../../../src/ol/format/GeoJSON.js'; import ImageLayer from '../../../../src/ol/layer/Image.js'; import ImageState from '../../../../src/ol/ImageState.js'; import ImageStatic from '../../../../src/ol/source/ImageStatic.js'; import Interaction from '../../../../src/ol/interaction/Interaction.js'; import Layer from '../../../../src/ol/layer/Layer.js'; import LayerGroup from '../../../../src/ol/layer/Group.js'; import Map from '../../../../src/ol/Map.js'; import MapBrowserEvent from '../../../../src/ol/MapBrowserEvent.js'; import MapEvent from '../../../../src/ol/MapEvent.js'; import MouseWheelZoom from '../../../../src/ol/interaction/MouseWheelZoom.js'; import Overlay from '../../../../src/ol/Overlay.js'; import PinchZoom from '../../../../src/ol/interaction/PinchZoom.js'; import Property from '../../../../src/ol/layer/Property.js'; import Select from '../../../../src/ol/interaction/Select.js'; import TileLayer from '../../../../src/ol/layer/Tile.js'; import TileLayerRenderer from '../../../../src/ol/renderer/canvas/TileLayer.js'; import VectorLayer from '../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../src/ol/source/Vector.js'; import View from '../../../../src/ol/View.js'; import XYZ from '../../../../src/ol/source/XYZ.js'; import {LineString, Point, Polygon} from '../../../../src/ol/geom.js'; import {TRUE} from '../../../../src/ol/functions.js'; import { clearUserProjection, fromLonLat, get as getProjection, transform, useGeographic, } from '../../../../src/ol/proj.js'; import {createXYZ} from '../../../../src/ol/tilegrid.js'; import {defaults as defaultInteractions} from '../../../../src/ol/interaction.js'; import {tile as tileStrategy} from '../../../../src/ol/loadingstrategy.js'; describe('ol/Map', function () { describe('constructor', function () { it('creates a new map', function () { const map = new Map({}); expect(map).to.be.a(Map); }); it('accepts a promise for view options', (done) => { let resolve; const map = new Map({ view: new Promise((r) => { resolve = r; }), }); expect(map.getView()).to.be.a(View); expect(map.getView().isDef()).to.be(false); map.once('change:view', () => { const view = map.getView(); expect(view).to.be.a(View); expect(view.isDef()).to.be(true); expect(view.getCenter()).to.eql([1, 2]); expect(view.getZoom()).to.be(3); done(); }); resolve({ center: [1, 2], zoom: 3, }); }); it('allows the view to be set with a promise later after construction', (done) => { const map = new Map({ view: new View({zoom: 1, center: [0, 0]}), }); expect(map.getView()).to.be.a(View); expect(map.getView().isDef()).to.be(true); let resolve; map.setView( new Promise((r) => { resolve = r; }) ); expect(map.getView()).to.be.a(View); expect(map.getView().isDef()).to.be(false); map.once('change:view', () => { const view = map.getView(); expect(view).to.be.a(View); expect(view.isDef()).to.be(true); expect(view.getCenter()).to.eql([1, 2]); expect(view.getZoom()).to.be(3); done(); }); resolve({ center: [1, 2], zoom: 3, }); }); it('creates a set of default interactions', function () { const map = new Map({}); const interactions = map.getInteractions(); const length = interactions.getLength(); expect(length).to.be.greaterThan(0); for (let i = 0; i < length; ++i) { expect(interactions.item(i).getMap()).to.be(map); } }); it('creates the viewport', function () { const map = new Map({}); const viewport = map.getViewport(); const className = 'ol-viewport' + ('ontouchstart' in window ? ' ol-touch' : ''); expect(viewport.className).to.be(className); }); it('creates the overlay containers', function () { const map = new Map({}); const container = map.getOverlayContainer(); expect(container.className).to.be('ol-overlaycontainer'); const containerStop = map.getOverlayContainerStopEvent(); expect(containerStop.className).to.be('ol-overlaycontainer-stopevent'); }); it('calls setMap for controls added by other controls', function () { let subSetMapCalled = false; class SubControl extends Control { setMap(map) { super.setMap(map); subSetMapCalled = true; } } class MainControl extends Control { setMap(map) { super.setMap(map); map.addControl( new SubControl({ element: document.createElement('div'), }) ); } } new Map({ target: document.createElement('div'), controls: [ new MainControl({ element: document.createElement('div'), }), ], }); expect(subSetMapCalled).to.be(true); }); }); describe('#addLayer()', function () { it('adds a layer to the map', function () { const map = new Map({}); const layer = new TileLayer(); map.addLayer(layer); expect(map.getLayers().item(0)).to.be(layer); expect(layer.get(Property.MAP)).to.be(map); }); it('throws if a layer is added twice', function () { const map = new Map({}); const layer = new TileLayer(); map.addLayer(layer); const call = function () { map.addLayer(layer); }; expect(call).to.throwException(); }); }); describe('#removeLayer()', function () { it('removes a layer from the map', function () { const map = new Map({}); const layer = new TileLayer(); map.addLayer(layer); expect(layer.get(Property.MAP)).to.be(map); map.removeLayer(layer); expect(layer.get(Property.MAP)).to.be(null); }); it('removes a layer group from the map', function () { const map = new Map({}); const layer = new TileLayer(); const group = new LayerGroup({layers: [layer]}); map.addLayer(group); expect(layer.get(Property.MAP)).to.be(map); map.removeLayer(group); expect(layer.get(Property.MAP)).to.be(null); }); }); describe('#setLayerGroup()', function () { it('sets the layer group', function () { const map = new Map({}); const layer = new Layer({}); const group = new LayerGroup({layers: [layer]}); map.setLayerGroup(group); expect(map.getLayerGroup()).to.be(group); expect(layer.get(Property.MAP)).to.be(map); }); it('removes the map property from old layers', function () { const oldLayer = new Layer({}); const map = new Map({layers: [oldLayer]}); expect(oldLayer.get(Property.MAP)).to.be(map); const layer = new Layer({}); const group = new LayerGroup({layers: [layer]}); map.setLayerGroup(group); expect(layer.get(Property.MAP)).to.be(map); expect(oldLayer.get(Property.MAP)).to.be(null); }); }); describe('#getAllLayers()', function () { it('returns all layers, also from inside groups', function () { const map = new Map({}); const layer = new TileLayer(); const group = new LayerGroup({layers: [layer]}); map.addLayer(group); const allLayers = map.getAllLayers(); expect(allLayers.length).to.be(1); expect(allLayers[0]).to.be(layer); }); }); describe('#setLayers()', function () { it('adds an array of layers to the map', function () { const map = new Map({}); const layer0 = new TileLayer(); const layer1 = new TileLayer(); map.setLayers([layer0, layer1]); const collection = map.getLayers(); expect(collection.getLength()).to.be(2); expect(collection.item(0)).to.be(layer0); expect(collection.item(1)).to.be(layer1); expect(layer0.get(Property.MAP)).to.be(map); expect(layer1.get(Property.MAP)).to.be(map); }); it('clears any existing layers', function () { const oldLayer = new TileLayer(); const map = new Map({layers: [oldLayer]}); expect(oldLayer.get(Property.MAP)).to.be(map); const newLayer1 = new TileLayer(); const newLayer2 = new TileLayer(); map.setLayers([newLayer1, newLayer2]); expect(newLayer1.get(Property.MAP)).to.be(map); expect(newLayer2.get(Property.MAP)).to.be(map); expect(oldLayer.get(Property.MAP)).to.be(null); expect(map.getLayers().getLength()).to.be(2); }); it('also works with collections', function () { const map = new Map({}); const layer0 = new TileLayer(); const layer1 = new TileLayer(); map.setLayers(new Collection([layer0, layer1])); const collection = map.getLayers(); expect(collection.getLength()).to.be(2); expect(collection.item(0)).to.be(layer0); expect(collection.item(1)).to.be(layer1); }); }); describe('#addInteraction()', function () { it('adds an interaction to the map', function () { const map = new Map({}); const interaction = new Interaction({}); const before = map.getInteractions().getLength(); map.addInteraction(interaction); const after = map.getInteractions().getLength(); expect(after).to.be(before + 1); expect(interaction.getMap()).to.be(map); }); }); describe('#removeInteraction()', function () { it('removes an interaction from the map', function () { const map = new Map({}); const interaction = new Interaction({}); const before = map.getInteractions().getLength(); map.addInteraction(interaction); map.removeInteraction(interaction); expect(map.getInteractions().getLength()).to.be(before); expect(interaction.getMap()).to.be(null); }); }); describe('movestart/moveend event', function () { let target, view, map; beforeEach(function () { target = document.createElement('div'); const style = target.style; style.position = 'absolute'; style.left = '-1000px'; style.top = '-1000px'; style.width = '360px'; style.height = '180px'; document.body.appendChild(target); view = new View({ projection: 'EPSG:4326', }); map = new Map({ target: target, view: view, layers: [ new TileLayer({ source: new XYZ({ url: '#{x}/{y}/{z}', }), }), ], }); }); afterEach(function () { map.dispose(); document.body.removeChild(target); }); it('are fired only once after view changes', function (done) { const center = [10, 20]; const zoom = 3; let startCalls = 0; let endCalls = 0; map.on('movestart', function () { ++startCalls; expect(startCalls).to.be(1); }); map.on('moveend', function () { ++endCalls; expect(endCalls).to.be(1); expect(view.getCenter()).to.eql(center); expect(view.getZoom()).to.be(zoom); window.setTimeout(done, 1000); }); view.setCenter(center); view.setZoom(zoom); }); it('are fired in sequence', function (done) { view.setCenter([0, 0]); view.setResolution(0.703125); map.renderSync(); const center = [10, 20]; const zoom = 3; const calls = []; map.on('movestart', function (e) { calls.push('start'); expect(calls).to.eql(['start']); expect(e.frameState.viewState.center).to.eql([0, 0]); expect(e.frameState.viewState.resolution).to.be(0.703125); }); map.on('moveend', function () { calls.push('end'); expect(calls).to.eql(['start', 'end']); expect(view.getCenter()).to.eql(center); expect(view.getZoom()).to.be(zoom); done(); }); view.setCenter(center); view.setZoom(zoom); }); }); describe('rendercomplete event', function () { let map; beforeEach(function () { const target = document.createElement('div'); target.style.width = '100px'; target.style.height = '100px'; document.body.appendChild(target); map = new Map({ target: target, layers: [ new TileLayer({ opacity: 0.5, source: new XYZ({ url: 'spec/ol/data/osm-{z}-{x}-{y}.png', }), }), new ImageLayer({ source: new ImageStatic({ url: 'spec/ol/data/osm-0-0-0.png', imageExtent: getProjection('EPSG:3857').getExtent(), projection: 'EPSG:3857', }), }), new VectorLayer({ source: new VectorSource({ url: 'spec/ol/data/point.json', format: new GeoJSON(), }), }), new VectorLayer({ source: new VectorSource({ url: 'spec/ol/data/point.json', format: new GeoJSON(), strategy: tileStrategy(createXYZ()), }), }), new VectorLayer({ source: new VectorSource({ features: [new Feature(new Point([0, 0]))], }), }), new VectorLayer({ source: new VectorSource({ loader: function (extent, resolution, projection) { this.addFeature(new Feature(new Point([0, 0]))); }, }), }), ], }); }); afterEach(function () { document.body.removeChild(map.getTargetElement()); map.setTarget(null); map.dispose(); }); it('triggers when all tiles and sources are loaded and faded in', function (done) { map.once('rendercomplete', function () { const layers = map.getLayers().getArray(); expect(map.tileQueue_.getTilesLoading()).to.be(0); expect(layers[1].getSource().image_.getState()).to.be( ImageState.LOADED ); expect(layers[2].getSource().getFeatures().length).to.be(1); done(); }); map.setView( new View({ center: [0, 0], zoom: 0, }) ); }); }); describe('#getFeaturesAtPixel', function () { let target, map, layer; beforeEach(function () { target = document.createElement('div'); target.style.width = '100px'; target.style.height = '100px'; document.body.appendChild(target); layer = new VectorLayer({ source: new VectorSource({ features: [ new Feature( new LineString([ [-50, 0], [50, 0], ]) ), ], }), }); map = new Map({ target: target, layers: [layer], view: new View({ center: [0, 0], zoom: 17, }), }); map.renderSync(); }); afterEach(function () { document.body.removeChild(target); }); it('returns an empty array if no feature was found', function () { const features = map.getFeaturesAtPixel([0, 0]); expect(features).to.be.an(Array); expect(features).to.be.empty(); }); it('returns an array of found features', function () { const features = map.getFeaturesAtPixel([50, 50]); expect(features).to.be.an(Array); expect(features[0]).to.be.an(Feature); }); it('returns an array of found features with declutter: true', function () { const layer = map.getLayers().item(0); map.removeLayer(layer); const otherLayer = new VectorLayer({ declutter: true, source: layer.getSource(), }); map.addLayer(otherLayer); map.renderSync(); const features = map.getFeaturesAtPixel([50, 50]); expect(features).to.be.an(Array); expect(features[0]).to.be.a(Feature); }); it('respects options', function () { const otherLayer = new VectorLayer({ source: new VectorSource(), }); map.addLayer(otherLayer); map.renderSync(); const features = map.getFeaturesAtPixel([50, 50], { layerFilter: function (layer) { return layer === otherLayer; }, }); expect(features).to.be.an(Array); expect(features).to.be.empty(); }); it('finds off-world geometries', function () { const line1 = new LineString([ [130, 0], [230, 0], ]); line1.transform('EPSG:4326', 'EPSG:3857'); const line2 = new LineString([ [-230, 0], [-130, 0], ]); line2.transform('EPSG:4326', 'EPSG:3857'); layer.getSource().addFeature(new Feature(line1)); layer.getSource().addFeature(new Feature(line2)); map.getView().setCenter(fromLonLat([180, 0])); map.renderSync(); let features = map.getFeaturesAtPixel([60, 50]); expect(features).to.be.an(Array); expect(features.length).to.be(2); features = map.getFeaturesAtPixel([60, 50], {checkWrapped: false}); expect(features).to.be.an(Array); expect(features.length).to.be(1); map.getView().setCenter(fromLonLat([-180, 0])); map.renderSync(); features = map.getFeaturesAtPixel([40, 50]); expect(features).to.be.an(Array); expect(features.length).to.be(2); features = map.getFeaturesAtPixel([40, 50], {checkWrapped: false}); expect(features).to.be.an(Array); expect(features.length).to.be(1); }); }); describe('#getFeaturesAtPixel - useGeographic', function () { let target, map; const size = 256; beforeEach(function () { useGeographic(); target = document.createElement('div'); target.style.width = size + 'px'; target.style.height = size + 'px'; document.body.appendChild(target); map = new Map({ target: target, layers: [ new VectorLayer({ source: new VectorSource({ features: [ new Feature( new Polygon([ [ [-100, 40], [-90, 40], [-90, 50], [-100, 50], [-100, 40], ], ]) ), ], }), }), ], view: new View({ center: [0, 0], zoom: 0, }), }); map.renderSync(); }); afterEach(function () { clearUserProjection(); document.body.removeChild(target); }); it('returns an empty array if no feature was found', function () { const features = map.getFeaturesAtPixel([size / 2, size / 2]); expect(features).to.be.an(Array); expect(features).to.be.empty(); }); it('returns an array of found features', function () { const coordinate = [-95, 45]; const pixel = map.getPixelFromCoordinate(coordinate); const features = map.getFeaturesAtPixel(pixel); expect(features).to.be.an(Array); expect(features[0]).to.be.a(Feature); }); }); describe('#hasFeatureAtPixel - useGeographic', function () { let target, map; const size = 256; beforeEach(function () { useGeographic(); target = document.createElement('div'); target.style.width = size + 'px'; target.style.height = size + 'px'; document.body.appendChild(target); map = new Map({ target: target, layers: [ new VectorLayer({ source: new VectorSource({ features: [ new Feature( new Polygon([ [ [-100, 40], [-90, 40], [-90, 50], [-100, 50], [-100, 40], ], ]) ), ], }), }), ], view: new View({ center: [0, 0], zoom: 0, }), }); map.renderSync(); }); afterEach(function () { clearUserProjection(); document.body.removeChild(target); }); it('returns false if no feature was found', function () { const has = map.hasFeatureAtPixel([size / 2, size / 2]); expect(has).to.be(false); }); it('returns true if there are features found', function () { const coordinate = [-95, 45]; const pixel = map.getPixelFromCoordinate(coordinate); const has = map.hasFeatureAtPixel(pixel); expect(has).to.be(true); }); }); describe('#forEachLayerAtPixel()', function () { let target, map, original, log; beforeEach(function (done) { log = []; original = TileLayerRenderer.prototype.getDataAtPixel; TileLayerRenderer.prototype.getDataAtPixel = function (pixel) { log.push(pixel.slice()); }; target = document.createElement('div'); const style = target.style; style.position = 'absolute'; style.left = '-1000px'; style.top = '-1000px'; style.width = '360px'; style.height = '180px'; document.body.appendChild(target); map = new Map({ target: target, view: new View({ center: [0, 0], zoom: 1, }), layers: [ new TileLayer({ source: new XYZ(), }), new TileLayer({ source: new XYZ(), }), new TileLayer({ source: new XYZ(), }), ], }); map.once('postrender', function () { done(); }); }); afterEach(function () { TileLayerRenderer.prototype.getDataAtPixel = original; map.dispose(); document.body.removeChild(target); log = null; }); it('calls each layer renderer with the same pixel', function () { const pixel = [10, 20]; map.forEachLayerAtPixel(pixel, function () {}); expect(log.length).to.equal(3); expect(log[0].length).to.equal(2); expect(log[0]).to.eql(log[1]); expect(log[1]).to.eql(log[2]); }); }); describe('#render()', function () { let target, map; beforeEach(function () { target = document.createElement('div'); const style = target.style; style.position = 'absolute'; style.left = '-1000px'; style.top = '-1000px'; style.width = '360px'; style.height = '180px'; document.body.appendChild(target); map = new Map({ target: target, view: new View({ projection: 'EPSG:4326', center: [0, 0], resolution: 1, }), }); }); afterEach(function () { map.dispose(); document.body.removeChild(target); }); it('is called when the view.changed() is called', function () { const view = map.getView(); const spy = sinon.spy(map, 'render'); view.changed(); expect(spy.callCount).to.be(1); }); it('is not called on view changes after the view has been removed', function () { const view = map.getView(); map.setView(null); const spy = sinon.spy(map, 'render'); view.changed(); expect(spy.callCount).to.be(0); }); it('calls renderFrame_ and results in an postrender event', function (done) { const spy = sinon.spy(map, 'renderFrame_'); map.render(); map.once('postrender', function (event) { expect(event).to.be.a(MapEvent); expect(typeof spy.firstCall.args[0]).to.be('number'); spy.restore(); const frameState = event.frameState; expect(frameState).not.to.be(null); done(); }); }); it('uses the same render frame for subsequent calls', function (done) { map.render(); const id1 = map.animationDelayKey_; let id2 = null; map.once('postrender', function () { expect(id2).to.be(id1); done(); }); map.render(); id2 = map.animationDelayKey_; }); it('creates a new render frame after renderSync()', function (done) { let id2 = null; map.render(); const id1 = map.animationDelayKey_; map.once('postrender', function () { expect(id2).to.not.be(id1); done(); }); map.renderSync(); id2 = map.animationDelayKey_; }); it('results in an postrender event (for zero height map)', function (done) { target.style.height = '0px'; map.updateSize(); map.render(); map.once('postrender', function (event) { expect(event).to.be.a(MapEvent); const frameState = event.frameState; expect(frameState).to.be(null); done(); }); }); it('results in an postrender event (for zero width map)', function (done) { target.style.width = '0px'; map.updateSize(); map.render(); map.once('postrender', function (event) { expect(event).to.be.a(MapEvent); const frameState = event.frameState; expect(frameState).to.be(null); done(); }); }); }); describe('dispose', function () { let map; beforeEach(function () { map = new Map({ target: document.createElement('div'), }); }); it('removes the viewport from its parent', function () { map.dispose(); expect(map.getViewport().parentNode).to.be(null); }); it('removes window listeners', function () { map.dispose(); expect(map.targetChangeHandlerKeys_).to.be(null); }); }); describe('#setTarget', function () { /** @type {Map|undefined} */ let map; beforeEach(function () { map = new Map({ target: document.createElement('div'), }); expect(map.targetChangeHandlerKeys_).to.be.ok(); }); describe('map with target not attached to dom', function () { it('has undefined as size with target not in document', function () { expect(map.getSize()).to.be(undefined); }); }); describe('call setTarget with null', function () { it('unregisters the viewport resize listener', function () { map.setTarget(null); expect(map.targetChangeHandlerKeys_).to.be(null); }); }); describe('call setTarget with an element', function () { it('registers a viewport resize listener', function () { map.setTarget(null); map.setTarget(document.createElement('div')); expect(map.targetChangeHandlerKeys_).to.be.ok(); }); }); }); describe('create interactions', function () { let options, event, hasTabIndex, hasFocus, isPrimary; beforeEach(function () { options = { altShiftDragRotate: false, doubleClickZoom: false, keyboard: false, mouseWheelZoom: false, shiftDragZoom: false, dragPan: false, pinchRotate: false, pinchZoom: false, }; hasTabIndex = true; hasFocus = true; isPrimary = true; event = { map: { getTargetElement: function () { return { hasAttribute: function (attribute) { return hasTabIndex; }, contains: function () { return hasFocus; }, }; }, getOwnerDocument: function () { return {}; }, }, originalEvent: { isPrimary: isPrimary, button: 0, }, target: { getTargetElement: function () { return { contains: function () { return hasFocus; }, }; }, }, }; }); describe('create mousewheel interaction', function () { it('creates mousewheel interaction', function () { options.mouseWheelZoom = true; const interactions = defaultInteractions(options); expect(interactions.getLength()).to.eql(1); expect(interactions.item(0)).to.be.a(MouseWheelZoom); expect(interactions.item(0).useAnchor_).to.eql(true); interactions.item(0).setMouseAnchor(false); expect(interactions.item(0).useAnchor_).to.eql(false); expect(interactions.item(0).condition_).to.be(TRUE); }); it('does not use the default condition when onFocusOnly option is set', function () { options.onFocusOnly = true; options.mouseWheelZoom = true; const interactions = defaultInteractions(options); expect(interactions.item(0).condition_).to.not.be(TRUE); hasTabIndex = true; hasFocus = true; expect(interactions.item(0).condition_(event)).to.be(true); hasTabIndex = true; hasFocus = false; expect(interactions.item(0).condition_(event)).to.be(false); hasTabIndex = false; expect(interactions.item(0).condition_(event)).to.be(true); }); }); describe('create dragpan interaction', function () { it('creates dragpan interaction', function () { options.dragPan = true; const interactions = defaultInteractions(options); expect(interactions.getLength()).to.eql(1); expect(interactions.item(0)).to.be.a(DragPan); expect(interactions.item(0).condition_(event)).to.be(true); hasTabIndex = true; hasFocus = false; expect(interactions.item(0).condition_(event)).to.be(true); event.originalEvent.altKey = true; expect(interactions.item(0).condition_(event)).to.be(false); delete event.originalEvent.altKey; event.originalEvent.button = 1; expect(interactions.item(0).condition_(event)).to.be(false); }); it('does not use the default condition when onFocusOnly option is set', function () { options.onFocusOnly = true; options.dragPan = true; const interactions = defaultInteractions(options); hasTabIndex = true; hasFocus = true; expect(interactions.item(0).condition_(event)).to.be(true); hasTabIndex = true; hasFocus = false; expect(interactions.item(0).condition_(event)).to.be(false); hasTabIndex = false; expect(interactions.item(0).condition_(event)).to.be(true); }); }); describe('create pinchZoom interaction', function () { it('creates pinchZoom interaction', function () { options.pinchZoom = true; const interactions = defaultInteractions(options); expect(interactions.getLength()).to.eql(1); expect(interactions.item(0)).to.be.a(PinchZoom); }); }); describe('create double click interaction', function () { beforeEach(function () { options.doubleClickZoom = true; }); describe('default zoomDelta', function () { it('create double click interaction with default delta', function () { const interactions = defaultInteractions(options); expect(interactions.getLength()).to.eql(1); expect(interactions.item(0)).to.be.a(DoubleClickZoom); expect(interactions.item(0).delta_).to.eql(1); }); }); describe('set zoomDelta', function () { it('create double click interaction with set delta', function () { options.zoomDelta = 7; const interactions = defaultInteractions(options); expect(interactions.getLength()).to.eql(1); expect(interactions.item(0)).to.be.a(DoubleClickZoom); expect(interactions.item(0).delta_).to.eql(7); }); }); }); describe('#getEventPixel', function () { let target; beforeEach(function () { target = document.createElement('div'); target.style.position = 'absolute'; target.style.top = '10px'; target.style.left = '20px'; target.style.width = '800px'; target.style.height = '400px'; document.body.appendChild(target); }); afterEach(function () { document.body.removeChild(target); }); it('works with touchend events', function () { const map = new Map({ target: target, }); const browserEvent = { type: 'touchend', target: target, changedTouches: [ { clientX: 100, clientY: 200, }, ], }; const position = map.getEventPixel(browserEvent); // 80 = clientX - target.style.left expect(position[0]).to.eql(80); // 190 = clientY - target.style.top expect(position[1]).to.eql(190); }); }); describe('#getOverlayById()', function () { let target, map, overlay, overlay_target; beforeEach(function () { target = document.createElement('div'); const style = target.style; style.position = 'absolute'; style.left = '-1000px'; style.top = '-1000px'; style.width = '360px'; style.height = '180px'; document.body.appendChild(target); map = new Map({ target: target, view: new View({ projection: 'EPSG:4326', center: [0, 0], resolution: 1, }), }); overlay_target = document.createElement('div'); }); afterEach(function () { map.removeOverlay(overlay); map.dispose(); document.body.removeChild(target); }); it('returns an overlay by id', function () { overlay = new Overlay({ id: 'foo', element: overlay_target, position: [0, 0], }); map.addOverlay(overlay); expect(map.getOverlayById('foo')).to.be(overlay); }); it('returns null when no overlay is found', function () { overlay = new Overlay({ id: 'foo', element: overlay_target, position: [0, 0], }); map.addOverlay(overlay); expect(map.getOverlayById('bar')).to.be(null); }); it('returns null after removing overlay', function () { overlay = new Overlay({ id: 'foo', element: overlay_target, position: [0, 0], }); map.addOverlay(overlay); expect(map.getOverlayById('foo')).to.be(overlay); map.removeOverlay(overlay); expect(map.getOverlayById('foo')).to.be(null); }); }); describe('getCoordinateFromPixel() and getPixelFromCoordinate()', function () { let target, view, map; const centerGeographic = [2.460938, 48.850258]; const centerMercator = transform( centerGeographic, getProjection('EPSG:4326'), getProjection('EPSG:3857') ); const screenCenter = [500, 500]; beforeEach(function () { target = document.createElement('div'); const style = target.style; style.position = 'absolute'; style.left = '-1000px'; style.top = '-1000px'; style.width = `${screenCenter[0] * 2}px`; style.height = `${screenCenter[1] * 2}px`; document.body.appendChild(target); useGeographic(); view = new View({ center: centerGeographic, zoom: 3, }); map = new Map({ target: target, view: view, layers: [ new TileLayer({ source: new XYZ({ url: '#{x}/{y}/{z}', }), }), ], }); }); afterEach(function () { map.dispose(); document.body.removeChild(target); clearUserProjection(); }); it('gets coordinates in user projection', function (done) { map.renderSync(); const coordinateGeographic = map.getCoordinateFromPixel(screenCenter); expect(coordinateGeographic[0]).to.roughlyEqual( centerGeographic[0], 1e-5 ); expect(coordinateGeographic[1]).to.roughlyEqual( centerGeographic[1], 1e-5 ); done(); }); it('gets coordinates in view projection', function (done) { map.renderSync(); const coordinateMercator = map.getCoordinateFromPixelInternal(screenCenter); expect(coordinateMercator[0]).to.roughlyEqual(centerMercator[0], 1e-5); expect(coordinateMercator[1]).to.roughlyEqual(centerMercator[1], 1e-5); done(); }); it('gets pixel from coordinates in user projection', function (done) { map.renderSync(); const pixel = map.getPixelFromCoordinate(centerGeographic); expect(pixel).to.eql(screenCenter); done(); }); it('gets pixel from coordinates in view projection', function (done) { map.renderSync(); const pixel = map.getPixelFromCoordinateInternal(centerMercator); expect(pixel).to.eql(screenCenter); done(); }); }); }); describe('#handleMapBrowserEvent()', function () { let map, target, dragpan; beforeEach(function () { target = document.createElement('div'); target.style.width = '100px'; target.style.height = '100px'; document.body.appendChild(target); dragpan = new DragPan(); map = new Map({ target: target, interactions: [dragpan], layers: [ new TileLayer({ source: new XYZ({ url: 'spec/ol/data/osm-{z}-{x}-{y}.png', }), }), ], view: new View({ zoom: 0, center: [0, 0], }), }); map.renderSync(); }); afterEach(function () { map.setTarget(null); document.body.removeChild(target); }); it('calls handleEvent on interaction', function () { const spy = sinon.spy(dragpan, 'handleEvent'); map.handleMapBrowserEvent( new MapBrowserEvent('pointermove', map, new PointerEvent('pointermove')) ); expect(spy.callCount).to.be(1); spy.restore(); }); it('does not call handleEvent on interaction when map has no target', function () { map.setTarget(null); const spy = sinon.spy(dragpan, 'handleEvent'); map.handleMapBrowserEvent( new MapBrowserEvent('pointermove', map, new PointerEvent('pointermove')) ); expect(spy.callCount).to.be(0); spy.restore(); }); it('does not call handleEvent on interaction that has been removed', function () { const spy = sinon.spy(dragpan, 'handleEvent'); let callCount = 0; const interaction = new Interaction({ handleEvent: function () { ++callCount; map.removeInteraction(dragpan); return true; }, }); map.addInteraction(interaction); map.handleMapBrowserEvent( new MapBrowserEvent('pointermove', map, new PointerEvent('pointermove')) ); expect(callCount).to.be(1); expect(spy.callCount).to.be(0); spy.restore(); }); it('does not call handleEvent on interaction when MapBrowserEvent propagation stopped', function () { const select = new Select(); const selectStub = sinon.stub(select, 'handleEvent'); selectStub.callsFake(function (e) { e.stopPropagation(); return true; }); map.addInteraction(select); const spy = sinon.spy(dragpan, 'handleEvent'); map.handleMapBrowserEvent( new MapBrowserEvent('pointermove', map, new PointerEvent('pointermove')) ); expect(spy.callCount).to.be(0); expect(selectStub.callCount).to.be(1); spy.restore(); selectStub.restore(); }); }); });