1 L
.Control
.MiniMap
= L
.Control
.extend({
3 position
: 'bottomright',
8 autoToggleDisplay
: false,
11 aimingRectOptions
: {color
: "#ff7800", weight
: 1, clickable
: false},
12 shadowRectOptions
: {color
: "#000000", weight
: 1, clickable
: false, opacity
:0, fillOpacity
:0}
15 hideText
: 'Hide MiniMap',
17 showText
: 'Show MiniMap',
19 //layer is the map layer to be shown in the minimap
20 initialize: function (layer
, options
) {
21 L
.Util
.setOptions(this, options
);
22 //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)
23 this.options
.aimingRectOptions
.clickable
= false;
24 this.options
.shadowRectOptions
.clickable
= false;
28 onAdd: function (map
) {
32 //Creating the container and stopping events from spilling through to the main map.
33 this._container
= L
.DomUtil
.create('div', 'leaflet-control-minimap');
34 this._container
.style
.width
= this.options
.width
+ 'px';
35 this._container
.style
.height
= this.options
.height
+ 'px';
36 L
.DomEvent
.disableClickPropagation(this._container
);
37 L
.DomEvent
.on(this._container
, 'mousewheel', L
.DomEvent
.stopPropagation
);
40 this._miniMap
= new L
.Map(this._container
,
42 attributionControl
: false,
44 zoomAnimation
: this.options
.zoomAnimation
,
45 autoToggleDisplay
: this.options
.autoToggleDisplay
,
46 touchZoom
: !this.options
.zoomLevelFixed
,
47 scrollWheelZoom
: !this.options
.zoomLevelFixed
,
48 doubleClickZoom
: !this.options
.zoomLevelFixed
,
49 boxZoom
: !this.options
.zoomLevelFixed
,
53 this._miniMap
.addLayer(this._layer
);
55 //These bools are used to prevent infinite loops of the two maps notifying each other that they've moved.
56 this._mainMapMoving
= false;
57 this._miniMapMoving
= false;
59 //Keep a record of this to prevent auto toggling when the user explicitly doesn't want it.
60 this._userToggledDisplay
= false;
61 this._minimized
= false;
63 if (this.options
.toggleDisplay
) {
64 this._addToggleButton();
67 this._miniMap
.whenReady(L
.Util
.bind(function () {
68 this._aimingRect
= L
.rectangle(this._mainMap
.getBounds(), this.options
.aimingRectOptions
).addTo(this._miniMap
);
69 this._shadowRect
= L
.rectangle(this._mainMap
.getBounds(), this.options
.shadowRectOptions
).addTo(this._miniMap
);
70 this._mainMap
.on('moveend', this._onMainMapMoved
, this);
71 this._mainMap
.on('move', this._onMainMapMoving
, this);
72 this._miniMap
.on('movestart', this._onMiniMapMoveStarted
, this);
73 this._miniMap
.on('move', this._onMiniMapMoving
, this);
74 this._miniMap
.on('moveend', this._onMiniMapMoved
, this);
77 return this._container
;
80 addTo: function (map
) {
81 L
.Control
.prototype.addTo
.call(this, map
);
82 this._miniMap
.setView(this._mainMap
.getCenter(), this._decideZoom(true));
83 this._setDisplay(this._decideMinimized());
87 onRemove: function (map
) {
88 this._mainMap
.off('moveend', this._onMainMapMoved
, this);
89 this._mainMap
.off('move', this._onMainMapMoving
, this);
90 this._miniMap
.off('moveend', this._onMiniMapMoved
, this);
92 this._miniMap
.removeLayer(this._layer
);
95 _addToggleButton: function () {
96 this._toggleDisplayButton
= this.options
.toggleDisplay
? this._createButton(
97 '', this.hideText
, 'leaflet-control-minimap-toggle-display', this._container
, this._toggleDisplayButtonClicked
, this) : undefined;
100 _createButton: function (html
, title
, className
, container
, fn
, context
) {
101 var link
= L
.DomUtil
.create('a', className
, container
);
102 link
.innerHTML
= html
;
106 var stop
= L
.DomEvent
.stopPropagation
;
109 .on(link
, 'click', stop
)
110 .on(link
, 'mousedown', stop
)
111 .on(link
, 'dblclick', stop
)
112 .on(link
, 'click', L
.DomEvent
.preventDefault
)
113 .on(link
, 'click', fn
, context
);
118 _toggleDisplayButtonClicked: function () {
119 this._userToggledDisplay
= true;
120 if (!this._minimized
) {
122 this._toggleDisplayButton
.title
= this.showText
;
126 this._toggleDisplayButton
.title
= this.hideText
;
130 _setDisplay: function (minimize
) {
131 if (minimize
!= this._minimized
) {
132 if (!this._minimized
) {
141 _minimize: function () {
143 if (this.options
.toggleDisplay
) {
144 this._container
.style
.width
= '19px';
145 this._container
.style
.height
= '19px';
146 this._toggleDisplayButton
.className
+= ' minimized';
149 this._container
.style
.display
= 'none';
151 this._minimized
= true;
154 _restore: function () {
155 if (this.options
.toggleDisplay
) {
156 this._container
.style
.width
= this.options
.width
+ 'px';
157 this._container
.style
.height
= this.options
.height
+ 'px';
158 this._toggleDisplayButton
.className
= this._toggleDisplayButton
.className
159 .replace(/(?:^|\s)minimized(?!\S)/g, '');
162 this._container
.style
.display
= 'block';
164 this._minimized
= false;
167 _onMainMapMoved: function (e
) {
168 if (!this._miniMapMoving
) {
169 this._mainMapMoving
= true;
170 this._miniMap
.setView(this._mainMap
.getCenter(), this._decideZoom(true));
171 this._setDisplay(this._decideMinimized());
173 this._miniMapMoving
= false;
175 this._aimingRect
.setBounds(this._mainMap
.getBounds());
178 _onMainMapMoving: function (e
) {
179 this._aimingRect
.setBounds(this._mainMap
.getBounds());
182 _onMiniMapMoveStarted:function (e
) {
183 var lastAimingRect
= this._aimingRect
.getBounds();
184 var sw
= this._miniMap
.latLngToContainerPoint(lastAimingRect
.getSouthWest());
185 var ne
= this._miniMap
.latLngToContainerPoint(lastAimingRect
.getNorthEast());
186 this._lastAimingRectPosition
= {sw
:sw
,ne
:ne
};
189 _onMiniMapMoving: function (e
) {
190 if (!this._mainMapMoving
&& this._lastAimingRectPosition
) {
191 this._shadowRect
.setBounds(new L
.LatLngBounds(this._miniMap
.containerPointToLatLng(this._lastAimingRectPosition
.sw
),this._miniMap
.containerPointToLatLng(this._lastAimingRectPosition
.ne
)));
192 this._shadowRect
.setStyle({opacity
:1,fillOpacity
:0.3});
196 _onMiniMapMoved: function (e
) {
197 if (!this._mainMapMoving
) {
198 this._miniMapMoving
= true;
199 this._mainMap
.setView(this._miniMap
.getCenter(), this._decideZoom(false));
200 this._shadowRect
.setStyle({opacity
:0,fillOpacity
:0});
202 this._mainMapMoving
= false;
206 _decideZoom: function (fromMaintoMini
) {
207 if (!this.options
.zoomLevelFixed
) {
209 return this._mainMap
.getZoom() + this.options
.zoomLevelOffset
;
211 var currentDiff
= this._miniMap
.getZoom() - this._mainMap
.getZoom();
212 var proposedZoom
= this._miniMap
.getZoom() - this.options
.zoomLevelOffset
;
215 if (currentDiff
> this.options
.zoomLevelOffset
&& this._mainMap
.getZoom() < this._miniMap
.getMinZoom() - this.options
.zoomLevelOffset
) {
216 //This means the miniMap is zoomed out to the minimum zoom level and can't zoom any more.
217 if (this._miniMap
.getZoom() > this._lastMiniMapZoom
) {
218 //This means the user is trying to zoom in by using the minimap, zoom the main map.
219 toRet
= this._mainMap
.getZoom() + 1;
220 //Also we cheat and zoom the minimap out again to keep it visually consistent.
221 this._miniMap
.setZoom(this._miniMap
.getZoom() -1);
223 //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.
224 //Therefore, we ignore it!
225 toRet
= this._mainMap
.getZoom();
228 //This is what happens in the majority of cases, and always if you configure the min levels + offset in a sane fashion.
229 toRet
= proposedZoom
;
231 this._lastMiniMapZoom
= this._miniMap
.getZoom();
236 return this.options
.zoomLevelFixed
;
238 return this._mainMap
.getZoom();
242 _decideMinimized: function () {
243 if (this._userToggledDisplay
) {
244 return this._minimized
;
247 if (this.options
.autoToggleDisplay
) {
248 if (this._mainMap
.getBounds().contains(this._miniMap
.getBounds())) {
254 return this._minimized
;
259 miniMapControl
: false
262 L
.Map
.addInitHook(function () {
263 if (this.options
.miniMapControl
) {
264 this.miniMapControl
= (new L
.Control
.MiniMap()).addTo(this);
268 L
.control
.minimap = function (options
) {
269 return new L
.Control
.MiniMap(options
);