/** * @classdesc * Make map cursor act as a magnify lense * * @constructor * @extends {ol.control.Control} * @param {olx.control.Otpions=} opt_options MagnifierControl options. */ ol.control.MagnifierControl = function(opt_options) { /** * get and set config options * @scaleOffSet {integer} (optional) default is 2. * @radius {integer} (optional) default is 100. * @lineWidth {integer} (optional) default is 5. * @strokeStyle {stirng) (optional) red+green+blue+alpha chanel * @layers {ol.layer[]} (optional) an Array of layers. Default is parent's map exisitng layers */ var options = opt_options || {}; //console.log("this.getMap()",this.getMap()) options.scaleOffSet = typeof(options.scaleOffSet) !== 'undefined' ? options.scaleOffSet : 2; options.radius = typeof(options.radius) !== 'undefined' ? options.radius : 100; options.lineWidth = typeof(options.lineWidth) !== 'undefined' ? options.lineWidth : 5; options.strokeStyle = typeof(options.strokeStyle) !== 'undefined' ? options.strokeStyle : 'rgba(255,0,0,1)'; options.layers = typeof(options.layers) !== 'undefined' ? options.layers : []; options.synchMaps = typeof(options.synchMaps) !== 'undefined' ? options.synchMaps : false; //attach options to the control this.options = options; //vars with global scope this.initialised = false; this.magmap;//ol.map this.mousePosition = null; this.targetMapDivId; //control toggle button var button = document.createElement('button'); button.innerHTML = 'M'; var this_ = this; /** * toggling the magnifing lense * intialise if first time * else show/hide the lense */ this.toggleMagCntrl = function() { if (this_.isVisble===true ){//need to hide lense and detach all listners document.getElementById('magmap').style.display = 'none'; //remove all the listeners ol.Observable.unByKey(this_.precomposeListener); ol.Observable.unByKey(this_.postcomposeListener); ol.Observable.unByKey(this_.mousemoveListener); //this_.getMap().unByKey(this_.moveendListener); document.getElementById(this_.targetMapDivId).removeEventListener("mouseout", this_.mouseOutFn); window.removeEventListener('resize', this_.resizeFn); this_.isVisble = false; } else {//initialise if not visible and show. Attach the listeners if (this_.initialised === false){ this_.initMagCntrl(); } //asign all the listeners this_.precomposeListener = this_.magmap.on('precompose', function(event) { this_.precomposeFn(event); }); this_.postcomposeListener = this_.magmap.on('postcompose', function(event) { this_.postcomposeFn(event); }); this_.mousemoveListener = this_.getMap().on('pointermove', function(event) { this_.mouseMoveFn(event); }); document.getElementById(this_.targetMapDivId).addEventListener("mouseout", this_.mouseOutFn); window.addEventListener ("resize", this_.resizeFn, false); document.getElementById('magmap').style.display = 'block'; this_.resizeFn(); this_.isVisble = true; } }; button.addEventListener('click', this_.toggleMagCntrl, false); button.addEventListener('touchstart', this_.toggleMagCntrl, false); var element = document.createElement('div'); element.className = 'ol-unselectable ol-control'; element.style.zIndex = '9999';//set one level above the magnifier so been visible element.style.top = '65px'; element.style.left = '.5em'; element.appendChild(button); ol.control.Control.call(this, { element : element, target : options.target }); }; ol.inherits(ol.control.MagnifierControl, ol.control.Control); /** * initialising the magnifying lense * * @returns {undefined} */ ol.control.MagnifierControl.prototype.initMagCntrl = function(){ if (this.initialised===false){ this.targetMapDivId = this.getMap().getTarget(); var magelement = document.createElement('div'); magelement.id = 'magmap'; magelement.style.zIndex = "9998"; //set one level below the magnify button so button is visible magelement.style.display = 'inline-block'; magelement.style.position = 'absolute'; magelement.style['pointer-events'] = 'none'; //this allowing interaction above magmap element. Love it!!!!! document.getElementById(this.targetMapDivId).appendChild(magelement); //disable the deafult controls var magnControls = ol.control.defaults({ rotate : true, //keep it true zoom : false, attribution : false }); if (this.options.layers.length===0){ if (this.options.synchMaps === false){ this.options.layers = this.getMap().getLayers().getArray(); //get as array so remove any synch } else { //in this case (default) magnmap layers should stay in sunc with map layers. //great attitude of ol.Collection. in it? //if not means have been passed as an array. So no sync. this.options.layers = this.getMap().getLayers();//returns ol.Collection } } else { this.options.synchMaps = false; //set it false as not suppling layers means no synch may exist } //create the map for the magnify this.magmap = new ol.Map({ layers : this.options.layers, target : 'magmap', controls : magnControls, interactions :ol.interaction.defaults({ shiftDragZoom : false, dragPan : false, altShiftDragRotate : false, doubleClickZoom : false, mouseWheelZoom : false }), view:new ol.View({ center : this.getMap().getView().getCenter(), zoom : this.getMap().getView().getZoom(), rotation : this.getMap().getView().getRotation() }) }); this.magmap.setSize(this.getMap().getSize()); this.initListeners(); this.resizeFn(); this.initialised = true; } }; /** * setting the listeners related */ ol.control.MagnifierControl.prototype.initListeners = function(){ var this_ = this; this.resizeFn = function(event){ var magMapEl = document.getElementById('magmap'); var oriMapEl = document.getElementById(this_.targetMapDivId); magMapEl.style.width = oriMapEl.getBoundingClientRect().width + 'px'; magMapEl.style.height = oriMapEl.getBoundingClientRect().height + 'px'; magMapEl.style.left = oriMapEl.getBoundingClientRect().left + 'px'; magMapEl.style.top = oriMapEl.getBoundingClientRect().top + 'px'; this_.getMap().updateSize(); this_.magmap.updateSize(); }; /** * the precompose liistener function * create a circle around the mouse * using the canvas */ this.precomposeFn = function(event){ var ctx = event.context; var pixelRatio = event.frameState.pixelRatio; ctx.save(); ctx.beginPath(); if (this_.mousePosition) { // only show a circle around the mouse ctx.arc( this_.mousePosition[0] * pixelRatio, this_.mousePosition[1] * pixelRatio, this_.options.radius * pixelRatio, 0, 2 * Math.PI ); ctx.lineWidth = this_.options.lineWidth * pixelRatio; ctx.strokeStyle = this_.options.strokeStyle; ctx.stroke(); } ctx.clip(); }; /** * */ this.postcomposeFn = function(event){ var ctx = event.context; ctx.restore(); }; /** * */ this.mouseMoveFn = function(event){ var coords = event.coordinate; this_.mousePosition = event.pixel; var magcenter = this_.magmap.getView().getCenter(); var mapcenter = this_.getMap().getView().getCenter(); var xdif= magcenter[0] - mapcenter[0]; var ydif= magcenter[1] - mapcenter[1]; //seems to need some correction var coords1 = [coords[0]-(xdif/(this_.options.scaleOffSet+1)),coords[1]-(ydif/(this_.options.scaleOffSet+1))]; console.log("this_.options.scaleOffSet+1-----",this_.options.scaleOffSet+1); if (this_.options.scaleOffSet===0){ this_.magmap.getView().setCenter(this_.getMap().getView().getCenter()); } else { this_.magmap.getView().setCenter(coords1); } this_.magmap.getView().setZoom( this_.getMap().getView().getZoom()+this_.options.scaleOffSet ); this_.magmap.getView().setRotation( this_.getMap().getView().getRotation() ); this_.magmap.render(); }; this.mouseOutFn = function(){ this_.mousePosition = null; this_.magmap.render(); }; } /** * destroy the control * */ ol.control.MagnifierControl.prototype.destroy = function(){ if (this.initialised===true){ //destory all the listeners ol.Observable.unByKey(this.precomposeListener); ol.Observable.unByKey(this.postcomposeListener); ol.Observable.unByKey(this.mousemoveListener); document.getElementById(this.targetMapDivId).removeEventListener("mouseout", this.mouseOutFn); window.removeEventListener('resize', this.resizeFn); //remove the control if (this.getMap()){ this.getMap().removeControl(this); } //remove the element holding the magn map var element = document.getElementById(this.magmap.getTarget()); element.parentNode.removeChild(element); //set init vars to false and magnmap to null } else { if (this.getMap()){ this.getMap().removeControl(this); } } this.isVisble=false; this.initialised=false; }; /** * very dirty way to reset the layers * need to recreate the map itself. No matter the goodness of ol.collection * sometimes is a pain in the a** */ ol.control.MagnifierControl.prototype.setLayers = function(lyrs) { if (this.initialised===true){ var existingLyrs = new ol.Collection(this.magmap.getLayers().getArray()).getArray(); this.options.synchMaps = false; this.magmap.setTarget(null); this.magmap = null; ol.Observable.unByKey(this.mousemoveListener); var magnControls = ol.control.defaults({ rotate : true, //keep it true zoom : false, attribution : false }); this.magmap = new ol.Map({ layers : lyrs, target : 'magmap', controls : magnControls, interactions :ol.interaction.defaults({ shiftDragZoom : false, dragPan : false, altShiftDragRotate : false, doubleClickZoom : false, mouseWheelZoom : false }), view:new ol.View({ center : this.getMap().getView().getCenter(), zoom : this.getMap().getView().getZoom(), rotation : this.getMap().getView().getRotation() }) }); this.magmap.setSize(this.getMap().getSize()); var this_ = this; console.log("this",this) if (this_.isVisble===true ){ this.precomposeListener = this.magmap.on('precompose', function(event) { this_.precomposeFn(event); }); this.postcomposeListener = this.magmap.on('postcompose', function(event) { this_.postcomposeFn(event); }); this.mousemoveListener = this.getMap().on('pointermove', function(event) { this_.mouseMoveFn(event); }); document.getElementById(this.targetMapDivId).addEventListener("mouseout", this_.mouseOutFn); window.addEventListener ("resize", this_.resizeFn, false); document.getElementById('magmap').style.display = 'block'; } else { //remove all the listeners ol.Observable.unByKey(this_.precomposeListener); ol.Observable.unByKey(this_.postcomposeListener); ol.Observable.unByKey(this_.mousemoveListener); document.getElementById(this_.targetMapDivId).removeEventListener("mouseout", this_.mouseOutFn); window.removeEventListener('resize', this_.resizeFn); this_.isVisble = false; } this.resizeFn(); this.initialised = true; this.options.synchMaps = false; } else { //this.options.layers = lyrs; console.log("action when not initialised") } }; var key = 'AuxBBMjAlE3BNG6BEaiNVni3ic3XH1uFPrS_ctXzHKohBObdCOBN_rP-n4hkAo9h'; var roads = new ol.layer.Tile({ source: new ol.source.OSM() }); var imagery = new ol.layer.Tile({ source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'}) }); var magnCntrl = new ol.control.MagnifierControl({ scaleOffSet:2,//the diff between our map and magn map radius:100//,//the radius of magn lense //layers:[ro4ds] }); var map = new ol.Map({ controls: ol.control.defaults({ attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ collapsible: false }) }).extend([ magnCntrl ]), layers: [roads], target: 'mymap', view: new ol.View({ center: ol.proj.fromLonLat([-109, 46.5]), zoom: 2 }) });
<div id="mymap" class="map"> </div>
.map { width: 100%; height: 300px; }