├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── bower.json ├── buildscripts └── svgtopng.js ├── dist ├── Control.MiniMap.min.css ├── Control.MiniMap.min.js └── images │ ├── toggle.png │ └── toggle.svg ├── example ├── example.html ├── example_customRects.html ├── example_differentminimised.html ├── example_dist.html ├── example_events.html ├── example_fixedcenter.html ├── example_fixedview.html ├── example_fixedzoom.html ├── example_i18n.html ├── example_larger.html ├── example_layerchange.html ├── example_layergroup.html ├── example_lowzoom.html ├── example_startminimised.html ├── example_toggleDisplay.html ├── example_topleft.html ├── fullscreen.css └── local_pubs_restaurant_norway.js ├── package.json ├── readme.md └── src ├── Control.MiniMap.css ├── Control.MiniMap.js └── images ├── toggle.png └── toggle.svg /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - node 5 | - "0.12" 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Norkart AS 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are 5 | permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of 8 | conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | of conditions and the following disclaimer in the documentation and/or other materials 12 | provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 17 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 21 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet-minimap", 3 | "description": "A plugin for Leaflet that provides a minimap in the corner of the map view.", 4 | "main": ["dist/Control.MiniMap.min.js", 5 | "dist/Control.MiniMap.min.css"], 6 | "authors": [ 7 | "Norkart AS" 8 | ], 9 | "license": "BSD-2-Clause", 10 | "keywords": [ 11 | "maps", 12 | "leaflet", 13 | "client", 14 | "minimap" 15 | ], 16 | "homepage": "https://github.com/Norkart/Leaflet-MiniMap", 17 | "moduleType": [ 18 | "amd", 19 | "es6", 20 | "globals", 21 | "node" 22 | ], 23 | "ignore": [ 24 | "**/.*", 25 | "node_modules", 26 | "bower_components", 27 | "example", 28 | "buildscripts", 29 | "src" 30 | ], 31 | "dependencies": { 32 | "leaflet": ">=0.7.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /buildscripts/svgtopng.js: -------------------------------------------------------------------------------- 1 | var svg2png = require('svg2png'); 2 | var ncp = require('ncp'); 3 | var path = require('path'); 4 | 5 | svg2png(path.resolve('src/images/toggle.svg'), path.resolve('src/images/toggle.png'), 1.0, function (err) { 6 | ncp('src/images/toggle.png', 'dist/images/toggle.png'); 7 | }); 8 | -------------------------------------------------------------------------------- /dist/Control.MiniMap.min.css: -------------------------------------------------------------------------------- 1 | .leaflet-control-minimap{border:rgba(255,255,255,1) solid;box-shadow:0 1px 5px rgba(0,0,0,.65);border-radius:3px;background:#f8f8f9;transition:all .6s}.leaflet-control-minimap a{background-color:rgba(255,255,255,1);background-repeat:no-repeat;z-index:99999;transition:all .6s}.leaflet-control-minimap a.minimized-bottomright{-webkit-transform:rotate(180deg);transform:rotate(180deg);border-radius:0}.leaflet-control-minimap a.minimized-topleft{-webkit-transform:rotate(0deg);transform:rotate(0deg);border-radius:0}.leaflet-control-minimap a.minimized-bottomleft{-webkit-transform:rotate(270deg);transform:rotate(270deg);border-radius:0}.leaflet-control-minimap a.minimized-topright{-webkit-transform:rotate(90deg);transform:rotate(90deg);border-radius:0}.leaflet-control-minimap-toggle-display{background-image:url(images/toggle.svg);background-size:cover;position:absolute;border-radius:3px 0 0}.leaflet-oldie .leaflet-control-minimap-toggle-display{background-image:url(images/toggle.png)}.leaflet-control-minimap-toggle-display-bottomright{bottom:0;right:0}.leaflet-control-minimap-toggle-display-topleft{top:0;left:0;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.leaflet-control-minimap-toggle-display-bottomleft{bottom:0;left:0;-webkit-transform:rotate(90deg);transform:rotate(90deg)}.leaflet-control-minimap-toggle-display-topright{top:0;right:0;-webkit-transform:rotate(270deg);transform:rotate(270deg)}.leaflet-oldie .leaflet-control-minimap{border:1px solid #999}.leaflet-oldie .leaflet-control-minimap a{background-color:#fff}.leaflet-oldie .leaflet-control-minimap a.minimized{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2)} -------------------------------------------------------------------------------- /dist/Control.MiniMap.min.js: -------------------------------------------------------------------------------- 1 | (function(factory,window){if(typeof define==="function"&&define.amd){define(["leaflet"],factory)}else if(typeof exports==="object"){module.exports=factory(require("leaflet"))}if(typeof window!=="undefined"&&window.L){window.L.Control.MiniMap=factory(L);window.L.control.minimap=function(layer,options){return new window.L.Control.MiniMap(layer,options)}}})(function(L){var MiniMap=L.Control.extend({includes:L.Evented?L.Evented.prototype:L.Mixin.Events,options:{position:"bottomright",toggleDisplay:false,zoomLevelOffset:-5,zoomLevelFixed:false,centerFixed:false,zoomAnimation:false,autoToggleDisplay:false,minimized:false,width:150,height:150,collapsedWidth:19,collapsedHeight:19,aimingRectOptions:{color:"#ff7800",weight:1,clickable:false},shadowRectOptions:{color:"#000000",weight:1,clickable:false,opacity:0,fillOpacity:0},strings:{hideText:"Hide MiniMap",showText:"Show MiniMap"},mapOptions:{}},initialize:function(layer,options){L.Util.setOptions(this,options);this.options.aimingRectOptions.clickable=false;this.options.shadowRectOptions.clickable=false;this._layer=layer},onAdd:function(map){this._mainMap=map;this._container=L.DomUtil.create("div","leaflet-control-minimap");this._container.style.width=this.options.width+"px";this._container.style.height=this.options.height+"px";L.DomEvent.disableClickPropagation(this._container);L.DomEvent.on(this._container,"mousewheel",L.DomEvent.stopPropagation);var mapOptions={attributionControl:false,dragging:!this.options.centerFixed,zoomControl:false,zoomAnimation:this.options.zoomAnimation,autoToggleDisplay:this.options.autoToggleDisplay,touchZoom:this.options.centerFixed?"center":!this._isZoomLevelFixed(),scrollWheelZoom:this.options.centerFixed?"center":!this._isZoomLevelFixed(),doubleClickZoom:this.options.centerFixed?"center":!this._isZoomLevelFixed(),boxZoom:!this._isZoomLevelFixed(),crs:map.options.crs};mapOptions=L.Util.extend(this.options.mapOptions,mapOptions);this._miniMap=new L.Map(this._container,mapOptions);this._miniMap.addLayer(this._layer);this._mainMapMoving=false;this._miniMapMoving=false;this._userToggledDisplay=false;this._minimized=false;if(this.options.toggleDisplay){this._addToggleButton()}this._miniMap.whenReady(L.Util.bind(function(){this._aimingRect=L.rectangle(this._mainMap.getBounds(),this.options.aimingRectOptions).addTo(this._miniMap);this._shadowRect=L.rectangle(this._mainMap.getBounds(),this.options.shadowRectOptions).addTo(this._miniMap);this._mainMap.on("moveend",this._onMainMapMoved,this);this._mainMap.on("move",this._onMainMapMoving,this);this._miniMap.on("movestart",this._onMiniMapMoveStarted,this);this._miniMap.on("move",this._onMiniMapMoving,this);this._miniMap.on("moveend",this._onMiniMapMoved,this)},this));return this._container},addTo:function(map){L.Control.prototype.addTo.call(this,map);var center=this.options.centerFixed||this._mainMap.getCenter();this._miniMap.setView(center,this._decideZoom(true));this._setDisplay(this.options.minimized);return this},onRemove:function(map){this._mainMap.off("moveend",this._onMainMapMoved,this);this._mainMap.off("move",this._onMainMapMoving,this);this._miniMap.off("moveend",this._onMiniMapMoved,this);this._miniMap.removeLayer(this._layer)},changeLayer:function(layer){this._miniMap.removeLayer(this._layer);this._layer=layer;this._miniMap.addLayer(this._layer)},_addToggleButton:function(){this._toggleDisplayButton=this.options.toggleDisplay?this._createButton("",this._toggleButtonInitialTitleText(),"leaflet-control-minimap-toggle-display leaflet-control-minimap-toggle-display-"+this.options.position,this._container,this._toggleDisplayButtonClicked,this):undefined;this._toggleDisplayButton.style.width=this.options.collapsedWidth+"px";this._toggleDisplayButton.style.height=this.options.collapsedHeight+"px"},_toggleButtonInitialTitleText:function(){if(this.options.minimized){return this.options.strings.showText}else{return this.options.strings.hideText}},_createButton:function(html,title,className,container,fn,context){var link=L.DomUtil.create("a",className,container);link.innerHTML=html;link.href="#";link.title=title;var stop=L.DomEvent.stopPropagation;L.DomEvent.on(link,"click",stop).on(link,"mousedown",stop).on(link,"dblclick",stop).on(link,"click",L.DomEvent.preventDefault).on(link,"click",fn,context);return link},_toggleDisplayButtonClicked:function(){this._userToggledDisplay=true;if(!this._minimized){this._minimize()}else{this._restore()}},_setDisplay:function(minimize){if(minimize!==this._minimized){if(!this._minimized){this._minimize()}else{this._restore()}}},_minimize:function(){if(this.options.toggleDisplay){this._container.style.width=this.options.collapsedWidth+"px";this._container.style.height=this.options.collapsedHeight+"px";this._toggleDisplayButton.className+=" minimized-"+this.options.position;this._toggleDisplayButton.title=this.options.strings.showText}else{this._container.style.display="none"}this._minimized=true;this._onToggle()},_restore:function(){if(this.options.toggleDisplay){this._container.style.width=this.options.width+"px";this._container.style.height=this.options.height+"px";this._toggleDisplayButton.className=this._toggleDisplayButton.className.replace("minimized-"+this.options.position,"");this._toggleDisplayButton.title=this.options.strings.hideText}else{this._container.style.display="block"}this._minimized=false;this._onToggle()},_onMainMapMoved:function(e){if(!this._miniMapMoving){var center=this.options.centerFixed||this._mainMap.getCenter();this._mainMapMoving=true;this._miniMap.setView(center,this._decideZoom(true));this._setDisplay(this._decideMinimized())}else{this._miniMapMoving=false}this._aimingRect.setBounds(this._mainMap.getBounds())},_onMainMapMoving:function(e){this._aimingRect.setBounds(this._mainMap.getBounds())},_onMiniMapMoveStarted:function(e){if(!this.options.centerFixed){var lastAimingRect=this._aimingRect.getBounds();var sw=this._miniMap.latLngToContainerPoint(lastAimingRect.getSouthWest());var ne=this._miniMap.latLngToContainerPoint(lastAimingRect.getNorthEast());this._lastAimingRectPosition={sw:sw,ne:ne}}},_onMiniMapMoving:function(e){if(!this.options.centerFixed){if(!this._mainMapMoving&&this._lastAimingRectPosition){this._shadowRect.setBounds(new L.LatLngBounds(this._miniMap.containerPointToLatLng(this._lastAimingRectPosition.sw),this._miniMap.containerPointToLatLng(this._lastAimingRectPosition.ne)));this._shadowRect.setStyle({opacity:1,fillOpacity:.3})}}},_onMiniMapMoved:function(e){if(!this._mainMapMoving){this._miniMapMoving=true;this._mainMap.setView(this._miniMap.getCenter(),this._decideZoom(false));this._shadowRect.setStyle({opacity:0,fillOpacity:0})}else{this._mainMapMoving=false}},_isZoomLevelFixed:function(){var zoomLevelFixed=this.options.zoomLevelFixed;return this._isDefined(zoomLevelFixed)&&this._isInteger(zoomLevelFixed)},_decideZoom:function(fromMaintoMini){if(!this._isZoomLevelFixed()){if(fromMaintoMini){return this._mainMap.getZoom()+this.options.zoomLevelOffset}else{var currentDiff=this._miniMap.getZoom()-this._mainMap.getZoom();var proposedZoom=this._miniMap.getZoom()-this.options.zoomLevelOffset;var toRet;if(currentDiff>this.options.zoomLevelOffset&&this._mainMap.getZoom()this._lastMiniMapZoom){toRet=this._mainMap.getZoom()+1;this._miniMap.setZoom(this._miniMap.getZoom()-1)}else{toRet=this._mainMap.getZoom()}}else{toRet=proposedZoom}this._lastMiniMapZoom=this._miniMap.getZoom();return toRet}}else{if(fromMaintoMini){return this.options.zoomLevelFixed}else{return this._mainMap.getZoom()}}},_decideMinimized:function(){if(this._userToggledDisplay){return this._minimized}if(this.options.autoToggleDisplay){if(this._mainMap.getBounds().contains(this._miniMap.getBounds())){return true}return false}return this._minimized},_isInteger:function(value){return typeof value==="number"},_isDefined:function(value){return typeof value!=="undefined"},_onToggle:function(){L.Util.requestAnimFrame(function(){L.DomEvent.on(this._container,"transitionend",this._fireToggleEvents,this);if(!L.Browser.any3d){L.Util.requestAnimFrame(this._fireToggleEvents,this)}},this)},_fireToggleEvents:function(){L.DomEvent.off(this._container,"transitionend",this._fireToggleEvents,this);var data={minimized:this._minimized};this.fire(this._minimized?"minimize":"restore",data);this.fire("toggle",data)}});L.Map.mergeOptions({miniMapControl:false});L.Map.addInitHook(function(){if(this.options.miniMapControl){this.miniMapControl=(new MiniMap).addTo(this)}});return MiniMap},window); -------------------------------------------------------------------------------- /dist/images/toggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Norkart/Leaflet-MiniMap/f5bfff8929dd9959cbf070c0d20b1bf42bbe85a3/dist/images/toggle.png -------------------------------------------------------------------------------- /dist/images/toggle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/example_customRects.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /example/example_differentminimised.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/example_dist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Minified Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/example_events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo Events 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 26 | 27 | 28 |
29 | 30 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /example/example_fixedcenter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Fixed Center Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /example/example_fixedview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Fixed Zoom Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /example/example_fixedzoom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Fixed Zoom Level Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/example_i18n.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo in German 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/example_larger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /example/example_layerchange.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example/example_layergroup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /example/example_lowzoom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Test - Same zoom limits 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/example_startminimised.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/example_toggleDisplay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/example_topleft.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MiniMap Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/fullscreen.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | html, body, #map { 6 | height: 100%; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet-minimap", 3 | "version": "3.6.1", 4 | "author": "Norkart AS", 5 | "description": "A plugin for Leaflet that provides a minimap in the corner of the map view.", 6 | "keywords": [ 7 | "maps", 8 | "leaflet", 9 | "client", 10 | "minimap" 11 | ], 12 | "license": "BSD-2-Clause", 13 | "readmeFilename": "README.md", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/Norkart/Leaflet-MiniMap.git" 17 | }, 18 | "main": "dist/Control.MiniMap.min.js", 19 | "homepage": "https://github.com/Norkart/Leaflet-MiniMap", 20 | "bugs": { 21 | "url": "https://github.com/Norkart/Leaflet-MiniMap/issues" 22 | }, 23 | "devDependencies": { 24 | "clean-css": "3.0.7", 25 | "happiness": "^1.0.7", 26 | "leaflet": ">=1.0.3", 27 | "ncp": "1.0.1", 28 | "svg2png": "1.1.0", 29 | "svgo": "0.5.0", 30 | "uglify-js": "2.6.0" 31 | }, 32 | "scripts": { 33 | "build:js": "uglifyjs --output dist/Control.MiniMap.min.js src/Control.MiniMap.js", 34 | "build:css": "cleancss --skip-rebase --output dist/Control.MiniMap.min.css src/Control.MiniMap.css", 35 | "build:img": "svgo src/images/toggle.svg dist/images/toggle.svg && node buildscripts/svgtopng.js", 36 | "build": "npm run build:css && npm run build:js && npm run build:img", 37 | "test": "happiness src/**/*.js", 38 | "preversion": "npm test && npm run build" 39 | }, 40 | "happiness": { 41 | "globals": [ 42 | "define", 43 | "L" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Leaflet.MiniMap 2 | 3 | Leaflet.MiniMap is a simple minimap control that you can drop into your leaflet map, and it will create a small map in the corner which shows the same as the main map with a set zoom offset. (By default it is `-5`.) 4 | 5 | [![npm version](https://badge.fury.io/js/leaflet-minimap.svg)](https://www.npmjs.com/package/leaflet-minimap) 6 | [![Bower version](https://badge.fury.io/bo/leaflet-minimap.svg)](https://github.com/Norkart/Leaflet-MiniMap) 7 | [![js-happiness-style](https://img.shields.io/badge/code%20style-happiness-brightgreen.svg?style=flat-square)](https://github.com/JedWatson/happiness) 8 | [![Build Status](https://travis-ci.org/Norkart/Leaflet-MiniMap.svg)](https://travis-ci.org/Norkart/Leaflet-MiniMap) 9 | [![CDNJS](https://img.shields.io/cdnjs/v/leaflet-minimap.svg)](https://cdnjs.com/libraries/leaflet-minimap) 10 | 11 | ## Using the MiniMap control 12 | 13 | The control can be inserted in two lines: First you have to construct a layer for it to use, and then you create and attach the minimap control. Don't reuse the layer you added to the main map, strange behaviour will ensue! Alternatively, you can pass in a LayerGroup with multiple layers (for example with overlays or suitably themed markers). Marker layers can't be reused either. (See issue #52 for a discussion of synchronising marker layers.) 14 | 15 | From the [example](http://norkart.github.com/Leaflet-MiniMap/example.html): 16 | 17 | ```js 18 | var osm2 = new L.TileLayer(osmUrl, {minZoom: 0, maxZoom: 13, attribution: osmAttrib}); 19 | var miniMap = new L.Control.MiniMap(osm2).addTo(map); 20 | ``` 21 | 22 | As the minimap control inherits from leaflet's control, positioning is handled automatically by leaflet. However, you can still style the minimap and set its size by modifying the css file. 23 | 24 | **Note:** Leaflet version 0.7.7 or higher is required, but the plugin has been 25 | tested and verified to work fine with Leaflet 1.0.0-beta2. 26 | 27 | ### Example usage in CommonJS compatible environments (Node/Browserify) 28 | 29 | ```js 30 | var MiniMap = require('leaflet-minimap'); 31 | new MiniMap(layer, options).addTo(map); 32 | ``` 33 | If you prefer ES6 style (for example with babel): 34 | 35 | ```js 36 | import MiniMap from 'leaflet-minimap'; 37 | new MiniMap(layer, options).addTo(map); 38 | ``` 39 | ### Example usage in AMD compatible environments (RequireJS) 40 | 41 | ```js 42 | require(['leaflet-minimap'], function(MiniMap) { 43 | new Minimap(layer, options).addTo(map); 44 | }); 45 | ``` 46 | 47 | ## Available Methods 48 | 49 | `changeLayer`: Swaps out the minimap layer for the one provided. See the _layerchange_ example for hints on good uses. 50 | 51 | ## Available Options 52 | The mini map uses options which can be set in the same way as other leaflet options, and these are the available options: 53 | 54 | `position`: The standard Leaflet.Control position parameter, used like all the other controls. Defaults to 'bottomright'. 55 | 56 | `width`: The width of the minimap in pixels. Defaults to 150. 57 | 58 | `height`: The height of the minimap in pixels. Defaults to 150. 59 | 60 | `collapsedWidth`: The width of the toggle marker and the minimap when collapsed, in pixels. Defaults to 19. 61 | 62 | `collapsedHeight`: The height of the toggle marker and the minimap when collapsed, in pixels. Defaults to 19. 63 | 64 | `zoomLevelOffset`: The offset applied to the zoom in the minimap compared to the zoom of the main map. Can be positive or negative, defaults to -5. 65 | 66 | `zoomLevelFixed`: Overrides the offset to apply a fixed zoom level to the minimap regardless of the main map zoom. Set it to any valid zoom level, if unset `zoomLevelOffset` is used instead. 67 | 68 | `centerFixed`: Applies a fixed position to the minimap regardless of the main map's view / position. Prevents panning the minimap, but does allow zooming (both in the minimap and the main map). If the minimap is zoomed, it will always zoom around the `centerFixed` point. You can pass in a LatLng-equivalent object. Defaults to false. 69 | 70 | `zoomAnimation`: Sets whether the minimap should have an animated zoom. (Will cause it to lag a bit after the movement of the main map.) Defaults to false. 71 | 72 | `toggleDisplay`: Sets whether the minimap should have a button to minimise it. Defaults to false. 73 | 74 | `autoToggleDisplay`: Sets whether the minimap should hide automatically if the parent map bounds does not fit within the minimap bounds. Especially useful when 'zoomLevelFixed' is set. 75 | 76 | `minimized`: Sets whether the minimap should start in a minimized position. 77 | 78 | `aimingRectOptions`: Sets the style of the aiming rectangle by passing in a [Path.Options object](http://leafletjs.com/reference.html#path-options). (Clickable will always be overridden and set to false.) 79 | 80 | `shadowRectOptions`: Sets the style of the aiming shadow rectangle by passing in a [Path.Options object](http://leafletjs.com/reference.html#path-options). (Clickable will always be overridden and set to false.) 81 | 82 | `strings`: Overrides the default strings allowing for translation. See below for available strings and `example/example_i18n.html` for an example. 83 | 84 | `mapOptions`: Sets Leaflet options for the MiniMap map. It does not override the MiniMap default map options but extends them. 85 | 86 | ### Available Strings 87 | 88 | `hideText`: The text to be displayed as Tooltip when hovering over the toggle button on the MiniMap and it is visible. Defaults to 'Hide MiniMap' 89 | 90 | `showText`: The text to be displayed as Tooltip when hovering over the toggle button on the MiniMap and it is hidden. Defaults to 'Show MiniMap' 91 | 92 | ### Available Events 93 | 94 | The MiniMap fires `minimize`, `restore` events and `toggle` for both. 95 | 96 | ## Building minified versions 97 | First, install node.js on your system. Then run `npm install` to get the dependencies, and `npm build` to build 98 | the minified js and css. Use `npm test` to lint the code so you can check that it follows our 99 | coding standard. (Any pull requests will be checked this way by the build server.) 100 | 101 | ## Loading minified version through node modules 102 | Node modules does not link css to minified version directly. 103 | Add`require('../../node_modules/leaflet-minimap/dist/Control.MiniMap.min.css');` to the code. 104 | -------------------------------------------------------------------------------- /src/Control.MiniMap.css: -------------------------------------------------------------------------------- 1 | .leaflet-control-minimap { 2 | border:solid rgba(255, 255, 255, 1.0) 4px; 3 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 4 | border-radius: 3px; 5 | background: #f8f8f9; 6 | transition: all .6s; 7 | } 8 | 9 | .leaflet-control-minimap a { 10 | background-color: rgba(255, 255, 255, 1.0); 11 | background-repeat: no-repeat; 12 | z-index: 99999; 13 | transition: all .6s; 14 | } 15 | 16 | .leaflet-control-minimap a.minimized-bottomright { 17 | -webkit-transform: rotate(180deg); 18 | transform: rotate(180deg); 19 | border-radius: 0px; 20 | } 21 | 22 | .leaflet-control-minimap a.minimized-topleft { 23 | -webkit-transform: rotate(0deg); 24 | transform: rotate(0deg); 25 | border-radius: 0px; 26 | } 27 | 28 | .leaflet-control-minimap a.minimized-bottomleft { 29 | -webkit-transform: rotate(270deg); 30 | transform: rotate(270deg); 31 | border-radius: 0px; 32 | } 33 | 34 | .leaflet-control-minimap a.minimized-topright { 35 | -webkit-transform: rotate(90deg); 36 | transform: rotate(90deg); 37 | border-radius: 0px; 38 | } 39 | 40 | .leaflet-control-minimap-toggle-display{ 41 | background-image: url("images/toggle.svg"); 42 | background-size: cover; 43 | position: absolute; 44 | border-radius: 3px 0px 0px 0px; 45 | } 46 | 47 | .leaflet-oldie .leaflet-control-minimap-toggle-display{ 48 | background-image: url("images/toggle.png"); 49 | } 50 | 51 | .leaflet-control-minimap-toggle-display-bottomright { 52 | bottom: 0; 53 | right: 0; 54 | } 55 | 56 | .leaflet-control-minimap-toggle-display-topleft{ 57 | top: 0; 58 | left: 0; 59 | -webkit-transform: rotate(180deg); 60 | transform: rotate(180deg); 61 | } 62 | 63 | .leaflet-control-minimap-toggle-display-bottomleft{ 64 | bottom: 0; 65 | left: 0; 66 | -webkit-transform: rotate(90deg); 67 | transform: rotate(90deg); 68 | } 69 | 70 | .leaflet-control-minimap-toggle-display-topright{ 71 | top: 0; 72 | right: 0; 73 | -webkit-transform: rotate(270deg); 74 | transform: rotate(270deg); 75 | } 76 | 77 | /* Old IE */ 78 | .leaflet-oldie .leaflet-control-minimap { 79 | border: 1px solid #999; 80 | } 81 | 82 | .leaflet-oldie .leaflet-control-minimap a { 83 | background-color: #fff; 84 | } 85 | 86 | .leaflet-oldie .leaflet-control-minimap a.minimized { 87 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 88 | } 89 | -------------------------------------------------------------------------------- /src/Control.MiniMap.js: -------------------------------------------------------------------------------- 1 | // Following https://github.com/Leaflet/Leaflet/blob/master/PLUGIN-GUIDE.md 2 | (function (factory, window) { 3 | 4 | // define an AMD module that relies on 'leaflet' 5 | if (typeof define === 'function' && define.amd) { 6 | define(['leaflet'], factory); 7 | 8 | // define a Common JS module that relies on 'leaflet' 9 | } else if (typeof exports === 'object') { 10 | module.exports = factory(require('leaflet')); 11 | } 12 | 13 | // attach your plugin to the global 'L' variable 14 | if (typeof window !== 'undefined' && window.L) { 15 | window.L.Control.MiniMap = factory(L); 16 | window.L.control.minimap = function (layer, options) { 17 | return new window.L.Control.MiniMap(layer, options); 18 | }; 19 | } 20 | }(function (L) { 21 | 22 | var MiniMap = L.Control.extend({ 23 | 24 | includes: L.Evented ? L.Evented.prototype : L.Mixin.Events, 25 | 26 | options: { 27 | position: 'bottomright', 28 | toggleDisplay: false, 29 | zoomLevelOffset: -5, 30 | zoomLevelFixed: false, 31 | centerFixed: false, 32 | zoomAnimation: false, 33 | autoToggleDisplay: false, 34 | minimized: false, 35 | width: 150, 36 | height: 150, 37 | collapsedWidth: 19, 38 | collapsedHeight: 19, 39 | aimingRectOptions: {color: '#ff7800', weight: 1, interactive: false}, 40 | shadowRectOptions: {color: '#000000', weight: 1, interactive: false, opacity: 0, fillOpacity: 0}, 41 | strings: {hideText: 'Hide MiniMap', showText: 'Show MiniMap'}, 42 | mapOptions: {} // Allows definition / override of Leaflet map options. 43 | }, 44 | 45 | // layer is the map layer to be shown in the minimap 46 | initialize: function (layer, options) { 47 | L.Util.setOptions(this, options); 48 | // Make sure the aiming rects are non-clickable even if the user tries to set them clickable (most likely by forgetting to specify them false) 49 | this.options.aimingRectOptions.interactive = false; 50 | this.options.shadowRectOptions.interactive = false; 51 | this._layer = layer; 52 | }, 53 | 54 | onAdd: function (map) { 55 | 56 | this._mainMap = map; 57 | 58 | // Creating the container and stopping events from spilling through to the main map. 59 | this._container = L.DomUtil.create('div', 'leaflet-control-minimap'); 60 | this._container.style.width = this.options.width + 'px'; 61 | this._container.style.height = this.options.height + 'px'; 62 | L.DomEvent.disableClickPropagation(this._container); 63 | L.DomEvent.on(this._container, 'mousewheel', L.DomEvent.stopPropagation); 64 | 65 | var mapOptions = { 66 | attributionControl: false, 67 | dragging: !this.options.centerFixed, 68 | zoomControl: false, 69 | zoomAnimation: this.options.zoomAnimation, 70 | autoToggleDisplay: this.options.autoToggleDisplay, 71 | touchZoom: this.options.centerFixed ? 'center' : !this._isZoomLevelFixed(), 72 | scrollWheelZoom: this.options.centerFixed ? 'center' : !this._isZoomLevelFixed(), 73 | doubleClickZoom: this.options.centerFixed ? 'center' : !this._isZoomLevelFixed(), 74 | boxZoom: !this._isZoomLevelFixed(), 75 | crs: map.options.crs 76 | }; 77 | mapOptions = L.Util.extend(this.options.mapOptions, mapOptions); // merge with priority of the local mapOptions object. 78 | 79 | this._miniMap = new L.Map(this._container, mapOptions); 80 | 81 | this._miniMap.addLayer(this._layer); 82 | 83 | // These bools are used to prevent infinite loops of the two maps notifying each other that they've moved. 84 | this._mainMapMoving = false; 85 | this._miniMapMoving = false; 86 | 87 | // Keep a record of this to prevent auto toggling when the user explicitly doesn't want it. 88 | this._userToggledDisplay = false; 89 | this._minimized = false; 90 | 91 | if (this.options.toggleDisplay) { 92 | this._addToggleButton(); 93 | } 94 | 95 | this._miniMap.whenReady(L.Util.bind(function () { 96 | this._aimingRect = L.rectangle(this._mainMap.getBounds(), this.options.aimingRectOptions).addTo(this._miniMap); 97 | this._shadowRect = L.rectangle(this._mainMap.getBounds(), this.options.shadowRectOptions).addTo(this._miniMap); 98 | this._mainMap.on('moveend', this._onMainMapMoved, this); 99 | this._mainMap.on('move', this._onMainMapMoving, this); 100 | this._miniMap.on('movestart', this._onMiniMapMoveStarted, this); 101 | this._miniMap.on('move', this._onMiniMapMoving, this); 102 | this._miniMap.on('moveend', this._onMiniMapMoved, this); 103 | }, this)); 104 | 105 | return this._container; 106 | }, 107 | 108 | addTo: function (map) { 109 | L.Control.prototype.addTo.call(this, map); 110 | 111 | var center = this.options.centerFixed || this._mainMap.getCenter(); 112 | this._miniMap.setView(center, this._decideZoom(true)); 113 | this._setDisplay(this.options.minimized); 114 | return this; 115 | }, 116 | 117 | onRemove: function (map) { 118 | this._mainMap.off('moveend', this._onMainMapMoved, this); 119 | this._mainMap.off('move', this._onMainMapMoving, this); 120 | this._miniMap.off('moveend', this._onMiniMapMoved, this); 121 | 122 | this._miniMap.removeLayer(this._layer); 123 | }, 124 | 125 | changeLayer: function (layer) { 126 | this._miniMap.removeLayer(this._layer); 127 | this._layer = layer; 128 | this._miniMap.addLayer(this._layer); 129 | }, 130 | 131 | _addToggleButton: function () { 132 | this._toggleDisplayButton = this.options.toggleDisplay ? this._createButton( 133 | '', this._toggleButtonInitialTitleText(), ('leaflet-control-minimap-toggle-display leaflet-control-minimap-toggle-display-' + 134 | this.options.position), this._container, this._toggleDisplayButtonClicked, this) : undefined; 135 | 136 | this._toggleDisplayButton.style.width = this.options.collapsedWidth + 'px'; 137 | this._toggleDisplayButton.style.height = this.options.collapsedHeight + 'px'; 138 | }, 139 | 140 | _toggleButtonInitialTitleText: function () { 141 | if (this.options.minimized) { 142 | return this.options.strings.showText; 143 | } else { 144 | return this.options.strings.hideText; 145 | } 146 | }, 147 | 148 | _createButton: function (html, title, className, container, fn, context) { 149 | var link = L.DomUtil.create('a', className, container); 150 | link.innerHTML = html; 151 | link.href = '#'; 152 | link.title = title; 153 | 154 | var stop = L.DomEvent.stopPropagation; 155 | 156 | L.DomEvent 157 | .on(link, 'click', stop) 158 | .on(link, 'mousedown', stop) 159 | .on(link, 'dblclick', stop) 160 | .on(link, 'click', L.DomEvent.preventDefault) 161 | .on(link, 'click', fn, context); 162 | 163 | return link; 164 | }, 165 | 166 | _toggleDisplayButtonClicked: function () { 167 | this._userToggledDisplay = true; 168 | if (!this._minimized) { 169 | this._minimize(); 170 | } else { 171 | this._restore(); 172 | } 173 | }, 174 | 175 | _setDisplay: function (minimize) { 176 | if (minimize !== this._minimized) { 177 | if (!this._minimized) { 178 | this._minimize(); 179 | } else { 180 | this._restore(); 181 | } 182 | } 183 | }, 184 | 185 | _minimize: function () { 186 | // hide the minimap 187 | if (this.options.toggleDisplay) { 188 | this._container.style.width = this.options.collapsedWidth + 'px'; 189 | this._container.style.height = this.options.collapsedHeight + 'px'; 190 | this._toggleDisplayButton.className += (' minimized-' + this.options.position); 191 | this._toggleDisplayButton.title = this.options.strings.showText; 192 | } else { 193 | this._container.style.display = 'none'; 194 | } 195 | this._minimized = true; 196 | this._onToggle(); 197 | }, 198 | 199 | _restore: function () { 200 | if (this.options.toggleDisplay) { 201 | this._container.style.width = this.options.width + 'px'; 202 | this._container.style.height = this.options.height + 'px'; 203 | this._toggleDisplayButton.className = this._toggleDisplayButton.className 204 | .replace('minimized-' + this.options.position, ''); 205 | this._toggleDisplayButton.title = this.options.strings.hideText; 206 | } else { 207 | this._container.style.display = 'block'; 208 | } 209 | this._minimized = false; 210 | this._onToggle(); 211 | }, 212 | 213 | _onMainMapMoved: function (e) { 214 | if (!this._miniMapMoving) { 215 | var center = this.options.centerFixed || this._mainMap.getCenter(); 216 | 217 | this._mainMapMoving = true; 218 | this._miniMap.setView(center, this._decideZoom(true)); 219 | this._setDisplay(this._decideMinimized()); 220 | } else { 221 | this._miniMapMoving = false; 222 | } 223 | this._aimingRect.setBounds(this._mainMap.getBounds()); 224 | }, 225 | 226 | _onMainMapMoving: function (e) { 227 | this._aimingRect.setBounds(this._mainMap.getBounds()); 228 | }, 229 | 230 | _onMiniMapMoveStarted: function (e) { 231 | if (!this.options.centerFixed) { 232 | var lastAimingRect = this._aimingRect.getBounds(); 233 | var sw = this._miniMap.latLngToContainerPoint(lastAimingRect.getSouthWest()); 234 | var ne = this._miniMap.latLngToContainerPoint(lastAimingRect.getNorthEast()); 235 | this._lastAimingRectPosition = {sw: sw, ne: ne}; 236 | } 237 | }, 238 | 239 | _onMiniMapMoving: function (e) { 240 | if (!this.options.centerFixed) { 241 | if (!this._mainMapMoving && this._lastAimingRectPosition) { 242 | this._shadowRect.setBounds(new L.LatLngBounds(this._miniMap.containerPointToLatLng(this._lastAimingRectPosition.sw), this._miniMap.containerPointToLatLng(this._lastAimingRectPosition.ne))); 243 | this._shadowRect.setStyle({opacity: 1, fillOpacity: 0.3}); 244 | } 245 | } 246 | }, 247 | 248 | _onMiniMapMoved: function (e) { 249 | if (!this._mainMapMoving) { 250 | this._miniMapMoving = true; 251 | this._mainMap.setView(this._miniMap.getCenter(), this._decideZoom(false)); 252 | this._shadowRect.setStyle({opacity: 0, fillOpacity: 0}); 253 | } else { 254 | this._mainMapMoving = false; 255 | } 256 | }, 257 | 258 | _isZoomLevelFixed: function () { 259 | var zoomLevelFixed = this.options.zoomLevelFixed; 260 | return this._isDefined(zoomLevelFixed) && this._isInteger(zoomLevelFixed); 261 | }, 262 | 263 | _decideZoom: function (fromMaintoMini) { 264 | if (!this._isZoomLevelFixed()) { 265 | if (fromMaintoMini) { 266 | return this._mainMap.getZoom() + this.options.zoomLevelOffset; 267 | } else { 268 | var currentDiff = this._miniMap.getZoom() - this._mainMap.getZoom(); 269 | var proposedZoom = this._miniMap.getZoom() - this.options.zoomLevelOffset; 270 | var toRet; 271 | 272 | if (currentDiff > this.options.zoomLevelOffset && this._mainMap.getZoom() < this._miniMap.getMinZoom() - this.options.zoomLevelOffset) { 273 | // This means the miniMap is zoomed out to the minimum zoom level and can't zoom any more. 274 | if (this._miniMap.getZoom() > this._lastMiniMapZoom) { 275 | // This means the user is trying to zoom in by using the minimap, zoom the main map. 276 | toRet = this._mainMap.getZoom() + 1; 277 | // Also we cheat and zoom the minimap out again to keep it visually consistent. 278 | this._miniMap.setZoom(this._miniMap.getZoom() - 1); 279 | } else { 280 | // Either the user is trying to zoom out past the mini map's min zoom or has just panned using it, we can't tell the difference. 281 | // Therefore, we ignore it! 282 | toRet = this._mainMap.getZoom(); 283 | } 284 | } else { 285 | // This is what happens in the majority of cases, and always if you configure the min levels + offset in a sane fashion. 286 | toRet = proposedZoom; 287 | } 288 | this._lastMiniMapZoom = this._miniMap.getZoom(); 289 | return toRet; 290 | } 291 | } else { 292 | if (fromMaintoMini) { 293 | return this.options.zoomLevelFixed; 294 | } else { 295 | return this._mainMap.getZoom(); 296 | } 297 | } 298 | }, 299 | 300 | _decideMinimized: function () { 301 | if (this._userToggledDisplay) { 302 | return this._minimized; 303 | } 304 | 305 | if (this.options.autoToggleDisplay) { 306 | if (this._mainMap.getBounds().contains(this._miniMap.getBounds())) { 307 | return true; 308 | } 309 | return false; 310 | } 311 | 312 | return this._minimized; 313 | }, 314 | 315 | _isInteger: function (value) { 316 | return typeof value === 'number'; 317 | }, 318 | 319 | _isDefined: function (value) { 320 | return typeof value !== 'undefined'; 321 | }, 322 | 323 | _onToggle: function () { 324 | L.Util.requestAnimFrame(function () { 325 | L.DomEvent.on(this._container, 'transitionend', this._fireToggleEvents, this); 326 | if (!L.Browser.any3d) { 327 | L.Util.requestAnimFrame(this._fireToggleEvents, this); 328 | } 329 | }, this); 330 | }, 331 | 332 | _fireToggleEvents: function () { 333 | L.DomEvent.off(this._container, 'transitionend', this._fireToggleEvents, this); 334 | var data = { minimized: this._minimized }; 335 | this.fire(this._minimized ? 'minimize' : 'restore', data); 336 | this.fire('toggle', data); 337 | } 338 | }); 339 | 340 | L.Map.mergeOptions({ 341 | miniMapControl: false 342 | }); 343 | 344 | L.Map.addInitHook(function () { 345 | if (this.options.miniMapControl) { 346 | this.miniMapControl = (new MiniMap()).addTo(this); 347 | } 348 | }); 349 | 350 | return MiniMap; 351 | 352 | }, window)); 353 | -------------------------------------------------------------------------------- /src/images/toggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Norkart/Leaflet-MiniMap/f5bfff8929dd9959cbf070c0d20b1bf42bbe85a3/src/images/toggle.png -------------------------------------------------------------------------------- /src/images/toggle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 39 | 41 | 48 | 54 | 55 | 62 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 96 | 97 | 104 | 110 | 111 | 118 | 124 | 125 | 132 | 138 | 139 | 146 | 152 | 153 | 160 | 166 | 167 | 174 | 180 | 181 | 195 | 196 | 198 | 199 | 201 | image/svg+xml 202 | 204 | 205 | 206 | 207 | 208 | 213 | 219 | 224 | 225 | --------------------------------------------------------------------------------