leaflet-rotate 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,14 @@
1
1
  (function (factory) {
2
2
  typeof define === 'function' && define.amd ? define(factory) :
3
3
  factory();
4
- }((function () { 'use strict';
4
+ })((function () { 'use strict';
5
5
 
6
6
  /**
7
- * L.DomUtil
7
+ * @external L.DomUtil
8
+ *
9
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/dom/DomUtil.js
8
10
  */
11
+
9
12
  const domUtilProto = L.extend({}, L.DomUtil);
10
13
 
11
14
  L.extend(L.DomUtil, {
@@ -15,7 +18,7 @@
15
18
 
16
19
  if (!bearing) {
17
20
  offset = pos._round();
18
- return domUtilProto.setTransform.call(this, el, offset, scale);
21
+ return domUtilProto.setTransform.apply(this, arguments);
19
22
  }
20
23
 
21
24
  pos = pos.rotateFrom(bearing, pivot);
@@ -26,9 +29,9 @@
26
29
  ' rotate(' + bearing + 'rad)';
27
30
  },
28
31
 
29
- setPosition: function(el, point, bearing, pivot) { // (HTMLElement, Point[, Boolean])
32
+ setPosition: function(el, point, bearing, pivot, scale) { // (HTMLElement, Point[, Boolean])
30
33
  if (!bearing) {
31
- return domUtilProto.setPosition.call(this, el, point);
34
+ return domUtilProto.setPosition.apply(this, arguments);
32
35
  }
33
36
 
34
37
  /*eslint-disable */
@@ -36,7 +39,7 @@
36
39
  /*eslint-enable */
37
40
 
38
41
  if (L.Browser.any3d) {
39
- L.DomUtil.setTransform(el, point, undefined, bearing, pivot);
42
+ L.DomUtil.setTransform(el, point, scale, bearing, pivot);
40
43
  } else {
41
44
  el.style.left = point.x + 'px';
42
45
  el.style.top = point.y + 'px';
@@ -50,41 +53,48 @@
50
53
  });
51
54
 
52
55
  /**
53
- * L.Draggable
56
+ * @external L.Draggable
57
+ *
58
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/dom/Draggable.js
54
59
  */
60
+
55
61
  L.Draggable.include({
56
62
 
57
- updateMapBearing: function(mapBearing) {
58
- this._mapBearing = mapBearing;
59
- },
63
+ // updateMapBearing: function(mapBearing) {
64
+ // this._mapBearing = mapBearing;
65
+ // },
60
66
 
61
67
  });
62
68
 
63
69
  /**
64
- * L.Point
70
+ * @external L.Point
71
+ *
72
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/geometry/Point.js
65
73
  */
74
+
66
75
  L.extend(L.Point.prototype, {
67
76
 
68
- // Rotate around (0,0) by applying the 2D rotation matrix:
69
- // ⎡ x' ⎤ = ⎡ cos θ -sin θ ⎤ ⎡ x ⎤
70
- // ⎣ y' ⎦ ⎣ sin θ cos θ ⎦ ⎣ y ⎦
71
- // Theta must be given in radians.
77
+ /**
78
+ * Rotate around (0,0) by applying the 2D rotation matrix:
79
+ *
80
+ * ⎡ x' ⎤ = ⎡ cos θ -sin θ ⎤ ⎡ x ⎤
81
+ * ⎣ y' ⎦ ⎣ sin θ cos θ ⎦ ⎣ y ⎦
82
+ *
83
+ * @param theta must be given in radians.
84
+ */
72
85
  rotate: function(theta) {
73
- if (!theta) { return this; }
74
- var sinTheta = Math.sin(theta);
75
- var cosTheta = Math.cos(theta);
76
-
77
- return new L.Point(
78
- this.x * cosTheta - this.y * sinTheta,
79
- this.x * sinTheta + this.y * cosTheta
80
- );
81
- },
82
-
83
- // Rotate around (pivot.x, pivot.y) by:
84
- // 1. subtract (pivot.x, pivot.y)
85
- // 2. rotate around (0, 0)
86
- // 3. add (pivot.x, pivot.y) back
87
- // same as `this.subtract(pivot).rotate(theta).add(pivot)`
86
+ return this.rotateFrom(theta, new L.Point(0,0))
87
+ },
88
+
89
+ /**
90
+ * Rotate around (pivot.x, pivot.y) by:
91
+ *
92
+ * 1. subtract (pivot.x, pivot.y)
93
+ * 2. rotate around (0, 0)
94
+ * 3. add (pivot.x, pivot.y) back
95
+ *
96
+ * same as `this.subtract(pivot).rotate(theta).add(pivot)`
97
+ */
88
98
  rotateFrom: function(theta, pivot) {
89
99
  if (!theta) { return this; }
90
100
  var sinTheta = Math.sin(theta);
@@ -103,65 +113,73 @@
103
113
  });
104
114
 
105
115
  /**
106
- * L.DivOverlay
116
+ * @external L.DivOverlay
117
+ *
118
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/DivOverlay.js
107
119
  */
120
+
108
121
  const divOverlayProto = L.extend({}, L.DivOverlay.prototype);
109
122
 
110
123
  L.DivOverlay.include({
111
124
 
112
125
  getEvents: function() {
113
- return L.extend(divOverlayProto.getEvents.call(this), { rotate: this._updatePosition });
126
+ return L.extend(divOverlayProto.getEvents.apply(this, arguments), { rotate: this._updatePosition });
114
127
  },
115
128
 
116
129
  _updatePosition: function() {
117
- if (!this._map) { return; }
118
-
119
- var pos = this._map.latLngToLayerPoint(this._latlng),
120
- offset = L.point(this.options.offset),
121
- anchor = this._getAnchor();
122
-
123
- if (this._zoomAnimated) {
124
- // TODO: use divOverlayProto._updatePosition
125
- if (this._map._rotate) {
126
- pos = this._map.rotatedPointToMapPanePoint(pos);
127
- }
128
- L.DomUtil.setPosition(this._container, pos.add(anchor));
129
- } else {
130
- offset = offset.add(pos).add(anchor);
130
+ // 0. update anchor (leaflet v1.9.3)
131
+ divOverlayProto._updatePosition.apply(this, arguments);
132
+ // 1. subtract anchor
133
+ // 2. rotate element
134
+ // 3. restore anchor
135
+ if (this._map && this._map._rotate && this._zoomAnimated) {
136
+ var anchor = this._getAnchor();
137
+ var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
138
+ L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
131
139
  }
132
140
 
133
- var bottom = this._containerBottom = -offset.y,
134
- left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
135
-
136
- // bottom position the popup in case the height of the popup changes (images loading etc)
137
- this._container.style.bottom = bottom + 'px';
138
- this._container.style.left = left + 'px';
139
141
  },
140
142
 
141
143
  });
142
144
 
143
145
  /**
144
- * L.Popup
146
+ * @external L.Popup
147
+ *
148
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/Popup.js
145
149
  */
150
+
146
151
  const popupProto = L.extend({}, L.Popup.prototype);
147
152
 
148
153
  L.Popup.include({
149
154
 
150
155
  _animateZoom: function(e) {
151
- if (!this._map._rotate) {
152
- popupProto._animateZoom.call(this, e);
156
+ // 0. update anchor (leaflet v1.9.3)
157
+ popupProto._animateZoom.apply(this, arguments);
158
+ // 1. subtract anchor
159
+ // 2. rotate element
160
+ // 3. restore anchor
161
+ if (this._map && this._map._rotate) {
162
+ var anchor = this._getAnchor();
163
+ var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
164
+ L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
153
165
  }
154
- var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
155
- anchor = this._getAnchor();
156
-
157
- pos = this._map.rotatedPointToMapPanePoint(pos);
158
-
159
- L.DomUtil.setPosition(this._container, pos.add(anchor));
160
166
  },
161
167
 
168
+ /**
169
+ * Hot fix for L.Popup.mergeOptions({ keepInView: true, });
170
+ *
171
+ * @see https://github.com/fnicollet/Leaflet/pull/21
172
+ */
162
173
  _adjustPan: function() {
163
174
  if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
164
175
 
176
+ // We can endlessly recurse if keepInView is set and the view resets.
177
+ // Let's guard against that by exiting early if we're responding to our own autopan.
178
+ if (this._autopanning) {
179
+ this._autopanning = false;
180
+ return;
181
+ }
182
+
165
183
  var map = this._map,
166
184
  marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
167
185
  containerHeight = this._container.offsetHeight + marginBottom,
@@ -171,7 +189,7 @@
171
189
  layerPos._add(L.DomUtil.getPosition(this._container));
172
190
 
173
191
  // var containerPos = map.layerPointToContainerPoint(layerPos);
174
- // TODO: use popupProto._adjustPan
192
+ /** @TODO use popupProto._adjustPan */
175
193
  var containerPos = layerPos._add(this._map._getMapPanePos()),
176
194
  padding = L.point(this.options.autoPanPadding),
177
195
  paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
@@ -198,6 +216,10 @@
198
216
  // @event autopanstart: Event
199
217
  // Fired when the map starts autopanning when opening a popup.
200
218
  if (dx || dy) {
219
+ // Track that we're autopanning, as this function will be re-ran on moveend
220
+ if (this.options.keepInView) {
221
+ this._autopanning = true;
222
+ }
201
223
  map
202
224
  .fire('autopanstart')
203
225
  .panBy([dx, dy]);
@@ -207,27 +229,30 @@
207
229
  });
208
230
 
209
231
  /**
210
- * L.Tooltip
232
+ * @external L.Tooltip
233
+ *
234
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/Tooltip.js
211
235
  */
236
+
212
237
  const tooltipProto = L.extend({}, L.Tooltip.prototype);
213
238
 
214
239
  L.Tooltip.include({
215
240
 
216
- _updatePosition: function() {
241
+ _animateZoom: function(e) {
217
242
  if (!this._map._rotate) {
218
- return tooltipProto._updatePosition.call(this);
243
+ return tooltipProto._animateZoom.apply(this, arguments);
219
244
  }
220
- var pos = this._map.latLngToLayerPoint(this._latlng);
245
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
221
246
 
222
247
  pos = this._map.rotatedPointToMapPanePoint(pos);
223
248
  this._setPosition(pos);
224
249
  },
225
250
 
226
- _animateZoom: function(e) {
251
+ _updatePosition: function() {
227
252
  if (!this._map._rotate) {
228
- return tooltipProto._animateZoom.call(this, e);
253
+ return tooltipProto._updatePosition.apply(this, arguments);
229
254
  }
230
- var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
255
+ var pos = this._map.latLngToLayerPoint(this._latlng);
231
256
 
232
257
  pos = this._map.rotatedPointToMapPanePoint(pos);
233
258
  this._setPosition(pos);
@@ -236,9 +261,12 @@
236
261
  });
237
262
 
238
263
  /**
239
- * L.Icon
264
+ * @external L.Icon
265
+ *
266
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Icon.js
240
267
  */
241
- const iconProto = L.extend({}, L.Icon.prototype);
268
+
269
+ L.extend({}, L.Icon.prototype);
242
270
 
243
271
  L.Icon.include({
244
272
 
@@ -259,7 +287,7 @@
259
287
  if (anchor) {
260
288
  img.style.marginLeft = (-anchor.x) + 'px';
261
289
  img.style.marginTop = (-anchor.y) + 'px';
262
- // TODO: use iconProto._setIconStyles
290
+ /** @TODO use iconProto._setIconStyles */
263
291
  img.style[L.DomUtil.TRANSFORM + "Origin"] = anchor.x + "px " + anchor.y + "px 0px";
264
292
  }
265
293
 
@@ -272,33 +300,55 @@
272
300
  });
273
301
 
274
302
  /**
275
- * L.Handler.MarkerDrag
303
+ * @external L.Marker
304
+ * @external L.Handler.MarkerDrag
305
+ *
306
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Marker.js
276
307
  */
277
- var markerDragProto;
308
+
309
+ const markerProto = L.extend({}, L.Marker.prototype);
310
+
311
+ L.Marker.mergeOptions({
312
+
313
+ // @option rotation: Number = 0
314
+ // Rotation of this marker in rad
315
+ rotation: 0,
316
+
317
+ // @option rotateWithView: Boolean = false
318
+ // Rotate this marker when map rotates
319
+ rotateWithView: false,
320
+
321
+ // @option scale: Number = undefined
322
+ // Scale of the marker icon
323
+ scale: undefined,
324
+
325
+ });
326
+
327
+ var markerDragProto; // retrived at runtime (see below: L.Marker::_initInteraction())
278
328
 
279
329
  var MarkerDrag = {
280
330
 
281
- _onDragStart: function() {
282
- if (!this._marker._map._rotate) {
283
- return markerDragProto._onDragStart.call(this)
284
- }
285
- this._draggable.updateMapBearing(this._marker._map._bearing);
286
- },
331
+ // _onDragStart: function() {
332
+ // if (!this._marker._map._rotate) {
333
+ // return markerDragProto._onDragStart.apply(this, arguments);
334
+ // }
335
+ // this._draggable.updateMapBearing(this._marker._map._bearing);
336
+ // },
287
337
 
288
338
  _onDrag: function(e) {
289
339
  var marker = this._marker,
290
- // TODO: use markerDragProto._onDrag
340
+ /** @TODO use markerDragProto._onDrag */
291
341
  rotated_marker = marker.options.rotation || marker.options.rotateWithView,
292
342
  shadow = marker._shadow,
293
343
  iconPos = L.DomUtil.getPosition(marker._icon);
294
344
 
295
- // TODO: use markerDragProto._onDrag
345
+ /** @TODO use markerDragProto._onDrag */
296
346
  // update shadow position
297
347
  if (!rotated_marker && shadow) {
298
348
  L.DomUtil.setPosition(shadow, iconPos);
299
349
  }
300
350
 
301
- // TODO: use markerDragProto._onDrag
351
+ /** @TODO use markerDragProto._onDrag */
302
352
  if (marker._map._rotate) {
303
353
  // Reverse calculation from mapPane coordinates to rotatePane coordinates
304
354
  iconPos = marker._map.mapPanePointToRotatedPoint(iconPos);
@@ -309,7 +359,7 @@
309
359
  e.latlng = latlng;
310
360
  e.oldLatLng = this._oldLatLng;
311
361
 
312
- // TODO: use markerDragProto._onDrag
362
+ /** @TODO use markerDragProto._onDrag */
313
363
  if (rotated_marker) marker.setLatLng(latlng); // use `setLatLng` to presisit rotation. low efficiency
314
364
  else marker.fire('move', e); // `setLatLng` will trig 'move' event. we imitate here.
315
365
 
@@ -323,47 +373,32 @@
323
373
  if (this._marker._map._rotate) {
324
374
  this._marker.update();
325
375
  }
326
- markerDragProto._onDragEnd.call(this, e);
376
+ markerDragProto._onDragEnd.apply(this, arguments);
327
377
  },
328
378
 
329
379
  };
330
380
 
331
- /**
332
- * L.Marker
333
- */
334
- const markerProto = L.extend({}, L.Marker.prototype);
335
-
336
- L.Marker.mergeOptions({
337
-
338
- // @option rotation: Number = 0
339
- // Rotation of this marker in rad
340
- rotation: 0,
341
-
342
- // @option rotateWithView: Boolean = false
343
- // Rotate this marker when map rotates
344
- rotateWithView: false,
345
-
346
- });
347
-
348
381
  L.Marker.include({
349
382
 
350
383
  getEvents: function() {
351
- return L.extend(markerProto.getEvents.call(this), { rotate: this.update });
384
+ return L.extend(markerProto.getEvents.apply(this, arguments), { rotate: this.update });
352
385
  },
353
386
 
354
387
  onAdd: function(map) {
355
- markerProto.onAdd.call(this, map);
388
+ markerProto.onAdd.apply(this, arguments);
356
389
  map.on('rotate', this.update, this);
357
390
  },
358
391
 
359
392
  _initInteraction: function() {
360
- var ret = markerProto._initInteraction.call(this);
393
+ var ret = markerProto._initInteraction.apply(this, arguments);
361
394
  if (this.dragging && this.dragging.enabled() && this._map && this._map._rotate) {
362
395
  // L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable
363
396
  markerDragProto = markerDragProto || Object.getPrototypeOf(this.dragging);
364
- this.dragging._onDragStart = MarkerDrag._onDragStart.bind(this.dragging);
365
- this.dragging._onDrag = MarkerDrag._onDrag.bind(this.dragging);
366
- this.dragging._onDragEnd = MarkerDrag._onDragEnd.bind(this.dragging);
397
+ Object.assign(this.dragging, {
398
+ // _onDragStart: MarkerDrag._onDragStart.bind(this.dragging),
399
+ _onDrag: MarkerDrag._onDrag.bind(this.dragging),
400
+ _onDragEnd: MarkerDrag._onDragEnd.bind(this.dragging),
401
+ });
367
402
  this.dragging.disable();
368
403
  this.dragging.enable();
369
404
  }
@@ -372,23 +407,25 @@
372
407
 
373
408
  _setPos: function(pos) {
374
409
 
375
- // TODO: use markerProto._setPos
410
+ /** @TODO use markerProto._setPos */
376
411
  if (this._map._rotate) {
377
412
  pos = this._map.rotatedPointToMapPanePoint(pos);
378
413
  }
379
414
 
380
- // TODO: use markerProto._setPos
415
+ /** @TODO use markerProto._setPos */
381
416
  var bearing = this.options.rotation || 0;
382
417
  if (this.options.rotateWithView) {
383
418
  bearing += this._map._bearing;
384
419
  }
385
420
 
386
- // TODO: use markerProto._setPos
387
- L.DomUtil.setPosition(this._icon, pos, bearing, pos);
421
+ /** @TODO use markerProto._setPos */
422
+ if (this._icon) {
423
+ L.DomUtil.setPosition(this._icon, pos, bearing, pos, this.options.scale);
424
+ }
388
425
 
389
- // TODO: use markerProto._setPos
426
+ /** @TODO use markerProto._setPos */
390
427
  if (this._shadow) {
391
- L.DomUtil.setPosition(this._shadow, pos, bearing, pos);
428
+ L.DomUtil.setPosition(this._shadow, pos, bearing, pos, this.options.scale);
392
429
  }
393
430
 
394
431
  this._zIndex = pos.y + this.options.zIndexOffset;
@@ -398,7 +435,7 @@
398
435
 
399
436
  _updateZIndex: function(offset) {
400
437
  if (!this._map._rotate) {
401
- return markerProto._updateZIndex.call(this, offset)
438
+ return markerProto._updateZIndex.apply(this, arguments);
402
439
  }
403
440
  this._icon.style.zIndex = Math.round(this._zIndex + offset);
404
441
  },
@@ -411,14 +448,17 @@
411
448
  });
412
449
 
413
450
  /**
414
- * L.GridLayer
451
+ * @external L.GridLayer
452
+ *
453
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/tile/GridLayer.js
415
454
  */
455
+
416
456
  const gridLayerProto = L.extend({}, L.GridLayer.prototype);
417
457
 
418
458
  L.GridLayer.include({
419
459
 
420
460
  getEvents: function() {
421
- var events = gridLayerProto.getEvents.call(this);
461
+ var events = gridLayerProto.getEvents.apply(this, arguments);
422
462
  if (this._map._rotate && !this.options.updateWhenIdle) {
423
463
  if (!this._onRotate) {
424
464
  this._onRotate = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
@@ -430,72 +470,71 @@
430
470
 
431
471
  _getTiledPixelBounds: function(center) {
432
472
  if (!this._map._rotate) {
433
- return gridLayerProto._getTiledPixelBounds.call(this, center);
473
+ return gridLayerProto._getTiledPixelBounds.apply(this, arguments);
434
474
  }
435
475
 
436
- var map = this._map,
437
- mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
438
- scale = map.getZoomScale(mapZoom, this._tileZoom),
439
- pixelCenter = map.project(center, this._tileZoom).floor(),
440
- size = map.getSize(),
441
- halfSize = new L.Bounds([
442
- map.containerPointToLayerPoint([0, 0]).floor(),
443
- map.containerPointToLayerPoint([size.x, 0]).floor(),
444
- map.containerPointToLayerPoint([0, size.y]).floor(),
445
- map.containerPointToLayerPoint([size.x, size.y]).floor()
446
- ]).getSize().divideBy(scale * 2);
447
-
448
- return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
476
+ return this._map._getNewPixelBounds(center, this._tileZoom);
449
477
  },
450
478
 
451
479
  });
452
480
 
453
481
  /**
454
- * L.Canvas
482
+ * @external L.Canvas
483
+ *
484
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/vector/Canvas.js
455
485
  */
456
- const canvasProto = L.extend({}, L.Canvas.prototype);
486
+
487
+ L.extend({}, L.Canvas.prototype);
457
488
 
458
489
  L.Canvas.include({
459
490
 
460
- onAdd: function() {
461
- canvasProto.onAdd.call(this);
462
- // When rotating the canvas itself, it is cleared by some weird reason, so redraw.
463
- this._map.on('rotate', this._redraw, this);
464
- },
491
+ // onAdd: function() {
492
+ // canvasProto.onAdd.apply(this, arguments);
493
+ // // When rotating the canvas itself, it is cleared by some weird reason, so redraw.
494
+ // this._map.on('rotate', this._redraw, this);
495
+ // },
465
496
 
466
- onRemove: function() {
467
- canvasProto.onRemove.call(this);
468
- this._map.off('rotate', this._redraw, this);
469
- },
497
+ // onRemove: function() {
498
+ // canvasProto.onRemove.apply(this, arguments);
499
+ // this._map.off('rotate', this._redraw, this);
500
+ // },
470
501
 
471
- _update: function() {
472
- canvasProto._update.call(this);
473
- // Tell paths to redraw themselves
474
- this.fire('update');
475
- },
502
+ // _update: function() {
503
+ // canvasProto._update.apply(this, arguments);
504
+ // // Tell paths to redraw themselves
505
+ // this.fire('update')
506
+ // },
476
507
 
477
508
  });
478
509
 
479
510
  /**
480
- * L.Renderer
511
+ * @external L.Renderer
512
+ *
513
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/vector/Renderer.js
481
514
  */
515
+
482
516
  const rendererProto = L.extend({}, L.Renderer.prototype);
483
517
 
484
518
  L.Renderer.include({
485
519
 
486
- onAdd: function() {
487
- rendererProto.onAdd.call(this);
488
- // this._map.on('rotate', this._update, this);
489
- },
520
+ // onAdd: function() {
521
+ // rendererProto.onAdd.apply(this, arguments);
522
+ // // this._map.on('rotate', this._update, this);
523
+ // },
490
524
 
491
- onRemove: function() {
492
- rendererProto.onRemove.call(this);
493
- // this._map.off('rotate', this._update, this);
494
- },
525
+ // onRemove: function() {
526
+ // rendererProto.onRemove.apply(this, arguments);
527
+ // // this._map.off('rotate', this._update, this);
528
+ // },
495
529
 
530
+ /**
531
+ * @TODO rechek this changes from leaflet@v1.9.3
532
+ *
533
+ * @see https://github.com/Leaflet/Leaflet/compare/v1.7.0...v1.9.3
534
+ */
496
535
  _updateTransform: function(center, zoom) {
497
536
  if (!this._map._rotate) {
498
- return rendererProto._updateTransform.call(this, center, zoom);
537
+ return rendererProto._updateTransform.apply(this, arguments);
499
538
  }
500
539
  var scale = this._map.getZoomScale(zoom, this._zoom),
501
540
  offset = this._map._latLngToNewLayerPoint(this._topLeft, zoom, center);
@@ -508,29 +547,12 @@
508
547
 
509
548
  _update: function() {
510
549
  if (!this._map._rotate) {
511
- return rendererProto._update.call(this);
550
+ return rendererProto._update.apply(this, arguments);
512
551
  }
513
552
  // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
514
553
  // Subclasses are responsible of firing the 'update' event.
515
- var p = this.options.padding,
516
- map = this._map,
517
- size = this._map.getSize(),
518
- padMin = size.multiplyBy(-p),
519
- padMax = size.multiplyBy(1 + p),
520
- //// TODO: Somehow refactor this out into map.something() - the code is
521
- //// pretty much the same as in GridLayer.
522
- clip = new L.Bounds([
523
- map.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),
524
- map.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),
525
- map.containerPointToLayerPoint([padMax.x, padMin.y]).floor(),
526
- map.containerPointToLayerPoint([padMax.x, padMax.y]).floor()
527
- ]);
528
- //min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
529
-
530
- this._bounds = clip;
531
- // this._topLeft = clip.min;
532
- this._topLeft = this._map.layerPointToLatLng(clip.min);
533
-
554
+ this._bounds = this._map._getPaddedPixelBounds(this.options.padding);
555
+ this._topLeft = this._map.layerPointToLatLng(this._bounds.min);
534
556
  this._center = this._map.getCenter();
535
557
  this._zoom = this._map.getZoom();
536
558
  },
@@ -538,57 +560,62 @@
538
560
  });
539
561
 
540
562
  /**
541
- * L.SVG
563
+ * @external L.SVG
564
+ *
565
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/vector/SVG.js
542
566
  */
543
- const svgProto = L.extend({}, L.SVG.prototype);
567
+
568
+ L.extend({}, L.SVG.prototype);
544
569
 
545
570
  L.SVG.include({
546
571
 
547
- _update: function() {
548
- svgProto._update.call(this);
549
- if (this._map._rotate) {
550
- this.fire('update');
551
- }
552
- },
572
+ // _update: function() {
573
+ // svgProto._update.apply(this, arguments);
574
+ // if (this._map._rotate) {
575
+ // this.fire('update');
576
+ // }
577
+ // },
553
578
 
554
579
  });
555
580
 
556
581
  /**
557
- * L.Map
582
+ * @external L.Map
583
+ *
584
+ * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/Map.js
558
585
  */
586
+
559
587
  const mapProto = L.extend({}, L.Map.prototype);
560
588
 
561
589
  L.Map.mergeOptions({ rotate: false, bearing: 0, });
562
590
 
563
591
  L.Map.include({
564
592
 
565
- initialize: function(id, options) { // (HTMLElement or String, Object)
593
+ /**
594
+ * @param {(HTMLElement|String)} id html selector
595
+ * @param {Object} [options={}] leaflet map options
596
+ */
597
+ initialize: function(id, options) {
566
598
  if (options.rotate) {
567
599
  this._rotate = true;
568
600
  this._bearing = 0;
569
601
  }
570
- mapProto.initialize.call(this, id, options);
602
+ mapProto.initialize.apply(this, arguments);
571
603
  if(this.options.rotate){
572
604
  this.setBearing(this.options.bearing);
573
605
  }
574
606
  },
575
607
 
576
- // createPane: function(name, container) {
577
- // if (!this._rotate || name == 'mapPane') {
578
- // return mapProto.createPane.call(this, name, container);
579
- // }
580
- // // init "rotatePane"
581
- // if (!this._rotatePane) {
582
- // // this._pivot = this.getSize().divideBy(2);
583
- // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane);
584
- // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot);
585
- // }
586
- // return mapProto.createPane.call(this, name, container || this._rotatePane);
587
- // },
588
-
589
- containerPointToLayerPoint: function(point) { // (Point)
608
+ /**
609
+ * Given a pixel coordinate relative to the map container,
610
+ * returns the corresponding pixel coordinate relative to
611
+ * the [origin pixel](#map-getpixelorigin).
612
+ *
613
+ * @param {L.Point} point pixel screen coordinates
614
+ * @returns {L.Point} transformed pixel point
615
+ */
616
+ containerPointToLayerPoint: function(point) {
590
617
  if (!this._rotate) {
591
- return mapProto.containerPointToLayerPoint.call(this, point);
618
+ return mapProto.containerPointToLayerPoint.apply(this, arguments);
592
619
  }
593
620
  return L.point(point)
594
621
  .subtract(this._getMapPanePos())
@@ -596,23 +623,16 @@
596
623
  .subtract(this._getRotatePanePos());
597
624
  },
598
625
 
599
- getBounds: function() {
626
+ /**
627
+ * Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
628
+ * returns the corresponding pixel coordinate relative to the map container.
629
+ *
630
+ * @param {L.Point} point pixel screen coordinates
631
+ * @returns {L.Point} transformed pixel point
632
+ */
633
+ layerPointToContainerPoint: function(point) {
600
634
  if (!this._rotate) {
601
- return mapProto.getBounds.call(this);
602
- }
603
- var size = this.getSize();
604
- var topleft = this.layerPointToLatLng(this.containerPointToLayerPoint([0, 0])),
605
- topright = this.layerPointToLatLng(this.containerPointToLayerPoint([size.x, 0])),
606
- bottomright = this.layerPointToLatLng(this.containerPointToLayerPoint([size.x, size.y])),
607
- bottomleft = this.layerPointToLatLng(this.containerPointToLayerPoint([0, size.y]));
608
-
609
- // Use LatLngBounds' build-in constructor that automatically extends the bounds to fit the passed points
610
- return new L.LatLngBounds([topleft, topright, bottomright, bottomleft]);
611
- },
612
-
613
- layerPointToContainerPoint: function(point) { // (Point)
614
- if (!this._rotate) {
615
- return mapProto.layerPointToContainerPoint.call(this, point);
635
+ return mapProto.layerPointToContainerPoint.apply(this, arguments);
616
636
  }
617
637
  return L.point(point)
618
638
  .add(this._getRotatePanePos())
@@ -620,89 +640,215 @@
620
640
  .add(this._getMapPanePos());
621
641
  },
622
642
 
623
- // Rotation methods
624
- // setBearing will work with just the 'theta' parameter.
643
+ /**
644
+ * Converts a coordinate from the rotated pane reference system
645
+ * to the reference system of the not rotated map pane.
646
+ *
647
+ * (rotatePane) --> (mapPane)
648
+ * (rotatePane) --> (norotatePane)
649
+ *
650
+ * @param {L.Point} point pixel screen coordinates
651
+ * @returns {L.Point}
652
+ *
653
+ * @since leaflet-rotate (v0.1)
654
+ */
655
+ rotatedPointToMapPanePoint: function(point) {
656
+ return L.point(point)
657
+ .rotate(this._bearing)
658
+ ._add(this._getRotatePanePos());
659
+ },
660
+
661
+ /**
662
+ * Converts a coordinate from the not rotated map pane reference system
663
+ * to the reference system of the rotated pane.
664
+ *
665
+ * (mapPane) --> (rotatePane)
666
+ * (norotatePane) --> (rotatePane)
667
+ *
668
+ * @param {L.Point} point pixel screen coordinates
669
+ *
670
+ * @since leaflet-rotate (v0.1)
671
+ */
672
+ mapPanePointToRotatedPoint: function(point) {
673
+ return L.point(point)
674
+ ._subtract(this._getRotatePanePos())
675
+ .rotate(-this._bearing);
676
+ },
677
+
678
+ /**
679
+ * Given latlng bounds, returns the bounds in projected pixel
680
+ * relative to the map container.
681
+ *
682
+ * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/map/Map.js#L1114-L1135
683
+ *
684
+ * @param {L.LatLngBounds} bounds
685
+ * @returns {L.Bounds}
686
+ *
687
+ * @since leaflet-rotate (v0.2)
688
+ */
689
+ mapBoundsToContainerBounds: function (bounds) {
690
+ if (!this._rotate && mapProto.mapBoundsToContainerBounds) {
691
+ return mapProto.mapBoundsToContainerBounds.apply(this, arguments);
692
+ }
693
+ const nw = this.latLngToContainerPoint(bounds.getNorthWest()),
694
+ ne = this.latLngToContainerPoint(bounds.getNorthEast()),
695
+ sw = this.latLngToContainerPoint(bounds.getSouthWest()),
696
+ se = this.latLngToContainerPoint(bounds.getSouthEast());
697
+ return L.bounds([
698
+ L.point(Math.min(nw.x, ne.x, se.x, sw.x), Math.min(nw.y, ne.y, se.y, sw.y)), // [ minX, minY ]
699
+ L.point(Math.max(nw.x, ne.x, se.x, sw.x), Math.max(nw.y, ne.y, se.y, sw.y)) // [ maxX, maxY ]
700
+ ]);
701
+ },
702
+
703
+ /**
704
+ * Returns geographical bounds visible in the current map view
705
+ *
706
+ * @TODO find out if map bounds calculated by `L.Map::getBounds()`
707
+ * function should match the `rotatePane` or `norotatePane` bounds
708
+ *
709
+ * @see https://github.com/fnicollet/Leaflet/issues/7
710
+ *
711
+ * @returns {L.LatLngBounds}
712
+ */
713
+ getBounds: function() {
714
+ if (!this._rotate) {
715
+ return mapProto.getBounds.apply(this, arguments);
716
+ }
717
+
718
+ // SEE: https://github.com/fnicollet/Leaflet/pull/22
719
+ //
720
+ // var bounds = this.getPixelBounds(),
721
+ // sw = this.unproject(bounds.getBottomLeft()),
722
+ // ne = this.unproject(bounds.getTopRight());
723
+ // return new LatLngBounds(sw, ne);
724
+ //
725
+
726
+ // LatLngBounds' constructor automatically
727
+ // extends the bounds to fit the passed points
728
+ var size = this.getSize();
729
+ return new L.LatLngBounds([
730
+ this.containerPointToLatLng([0, 0]), // topleft
731
+ this.containerPointToLatLng([size.x, 0]), // topright
732
+ this.containerPointToLatLng([size.x, size.y]), // bottomright
733
+ this.containerPointToLatLng([0, size.y]), // bottomleft
734
+ ]);
735
+ },
736
+
737
+ /**
738
+ * Returns the bounds of the current map view in projected pixel
739
+ * coordinates (sometimes useful in layer and overlay implementations).
740
+ *
741
+ * @TODO find out if map bounds calculated by `L.Map::getPixelBounds()`
742
+ * function should match the `rotatePane` or `norotatePane` bounds
743
+ *
744
+ * @see https://github.com/fnicollet/Leaflet/issues/7
745
+ *
746
+ * @returns {L.Bounds}
747
+ */
748
+ // getPixelBounds(center, zoom) {
749
+ // // const topLeftPoint = map.containerPointToLayerPoint(this._getTopLeftPoint());
750
+ // const topLeftPoint = this._getTopLeftPoint(center, zoom);
751
+ // return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
752
+ // },
753
+
754
+ /**
755
+ * Change map rotation
756
+ *
757
+ * @param {number} theta map degrees
758
+ *
759
+ * @since leaflet-rotate (v0.1)
760
+ */
625
761
  setBearing: function(theta) {
626
762
  if (!L.Browser.any3d || !this._rotate) { return; }
627
763
 
628
- var rotatePanePos = this._getRotatePanePos();
629
- var halfSize = this.getSize().divideBy(2);
630
- this._pivot = this._getMapPanePos().clone().multiplyBy(-1).add(halfSize);
631
-
632
- rotatePanePos = rotatePanePos.rotateFrom(-this._bearing, this._pivot);
764
+ var bearing = L.Util.wrapNum(theta, [0, 360]) * L.DomUtil.DEG_TO_RAD,
765
+ center = this._getPixelCenter(),
766
+ oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center),
767
+ newPos = oldPos.rotateFrom(bearing, center);
633
768
 
634
- this._bearing = theta * L.DomUtil.DEG_TO_RAD; // TODO: mod 360
635
- this._rotatePanePos = rotatePanePos.rotateFrom(this._bearing, this._pivot);
769
+ // CSS transform
770
+ L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center);
636
771
 
637
- L.DomUtil.setPosition(this._rotatePane, rotatePanePos, this._bearing, this._pivot);
772
+ this._pivot = center;
773
+ this._bearing = bearing;
774
+ this._rotatePanePos = newPos;
638
775
 
639
776
  this.fire('rotate');
640
777
  },
641
778
 
779
+ /**
780
+ * Get current map rotation
781
+ *
782
+ * @returns {number} theta map degrees
783
+ *
784
+ * @since leaflet-rotate (v0.1)
785
+ */
642
786
  getBearing: function() {
643
787
  return this._bearing * L.DomUtil.RAD_TO_DEG;
644
788
  },
645
789
 
790
+ /**
791
+ * Creates a new [map pane](#map-pane) with the given name if it doesn't
792
+ * exist already, then returns it. The pane is created as a child of
793
+ * `container`, or as a child of the main map pane if not set.
794
+ *
795
+ * @param {String} name leaflet pane
796
+ * @param {HTMLElement} [container] parent element
797
+ * @returns {HTMLElement} pane container
798
+ */
799
+ // createPane: function(name, container) {
800
+ // if (!this._rotate || name == 'mapPane') {
801
+ // return mapProto.createPane.apply(this, arguments);
802
+ // }
803
+ // // init "rotatePane"
804
+ // if (!this._rotatePane) {
805
+ // // this._pivot = this.getSize().divideBy(2);
806
+ // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane);
807
+ // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot);
808
+ // }
809
+ // return mapProto.createPane.call(this, name, container || this._rotatePane);
810
+ // },
811
+
812
+ /**
813
+ * Panes are DOM elements used to control the ordering of layers on
814
+ * the map. You can access panes with [`map.getPane`](#map-getpane)
815
+ * or [`map.getPanes`](#map-getpanes) methods. New panes can be created
816
+ * with the [`map.createPane`](#map-createpane) method.
817
+ *
818
+ * Every map has the following default panes that differ only in zIndex:
819
+ *
820
+ * - mapPane [HTMLElement = 'auto'] - Pane that contains all other map panes
821
+ * - tilePane [HTMLElement = 2] - Pane for tile layers
822
+ * - overlayPane [HTMLElement = 4] - Pane for overlays like polylines and polygons
823
+ * - shadowPane [HTMLElement = 5] - Pane for overlay shadows (e.g. marker shadows)
824
+ * - markerPane [HTMLElement = 6] - Pane for marker icons
825
+ * - tooltipPane [HTMLElement = 650] - Pane for tooltips.
826
+ * - popupPane [HTMLElement = 700] - Pane for popups.
827
+ */
646
828
  _initPanes: function() {
647
829
  var panes = this._panes = {};
648
830
  this._paneRenderers = {};
649
831
 
650
- // @section
651
- //
652
- // Panes are DOM elements used to control the ordering of layers on the map. You
653
- // can access panes with [`map.getPane`](#map-getpane) or
654
- // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
655
- // [`map.createPane`](#map-createpane) method.
656
- //
657
- // Every map has the following default panes that differ only in zIndex.
658
- //
659
- // @pane mapPane: HTMLElement = 'auto'
660
- // Pane that contains all other map panes
661
-
662
832
  this._mapPane = this.createPane('mapPane', this._container);
663
833
  L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
664
834
 
665
835
  if (this._rotate) {
666
836
  this._rotatePane = this.createPane('rotatePane', this._mapPane);
667
837
  this._norotatePane = this.createPane('norotatePane', this._mapPane);
668
-
669
- // @pane tilePane: HTMLElement = 2
670
- // Pane for tile layers
838
+ // rotatePane
671
839
  this.createPane('tilePane', this._rotatePane);
672
- // @pane overlayPane: HTMLElement = 4
673
- // Pane for overlays like polylines and polygons
674
840
  this.createPane('overlayPane', this._rotatePane);
675
-
676
- // @pane shadowPane: HTMLElement = 5
677
- // Pane for overlay shadows (e.g. marker shadows)
841
+ // norotatePane
678
842
  this.createPane('shadowPane', this._norotatePane);
679
- // @pane markerPane: HTMLElement = 6
680
- // Pane for marker icons
681
843
  this.createPane('markerPane', this._norotatePane);
682
- // @pane tooltipPane: HTMLElement = 650
683
- // Pane for tooltips.
684
844
  this.createPane('tooltipPane', this._norotatePane);
685
- // @pane popupPane: HTMLElement = 700
686
- // Pane for popups.
687
845
  this.createPane('popupPane', this._norotatePane);
688
846
  } else {
689
- // @pane tilePane: HTMLElement = 2
690
- // Pane for tile layers
691
847
  this.createPane('tilePane');
692
- // @pane overlayPane: HTMLElement = 4
693
- // Pane for overlays like polylines and polygons
694
848
  this.createPane('overlayPane');
695
- // @pane shadowPane: HTMLElement = 5
696
- // Pane for overlay shadows (e.g. marker shadows)
697
849
  this.createPane('shadowPane');
698
- // @pane markerPane: HTMLElement = 6
699
- // Pane for marker icons
700
850
  this.createPane('markerPane');
701
- // @pane tooltipPane: HTMLElement = 650
702
- // Pane for tooltips.
703
851
  this.createPane('tooltipPane');
704
- // @pane popupPane: HTMLElement = 700
705
- // Pane for popups.
706
852
  this.createPane('popupPane');
707
853
  }
708
854
 
@@ -712,37 +858,205 @@
712
858
  }
713
859
  },
714
860
 
715
- // @method rotatedPointToMapPanePoint(point: Point): Point
716
- // Converts a coordinate from the rotated pane reference system
717
- // to the reference system of the not rotated map pane.
718
- rotatedPointToMapPanePoint: function(point) {
719
- return L.point(point).rotate(this._bearing)._add(this._getRotatePanePos());
720
- },
861
+ /**
862
+ * Pans the map the minimum amount to make the `latlng` visible. Use
863
+ * padding options to fit the display to more restricted bounds.
864
+ * If `latlng` is already within the (optionally padded) display bounds,
865
+ * the map will not be panned.
866
+ *
867
+ * @see https://github.com/Raruto/leaflet-rotate/issues/18
868
+ *
869
+ * @param {L.LatLng} latlng coordinates
870
+ * @param {Object} [options={}] padding options
871
+ *
872
+ * @returns {L.Map} current map instance
873
+ */
874
+ panInside(latlng, options) {
875
+ options = options || {};
876
+
877
+ const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
878
+ paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
879
+ /** @TODO use mapProto.panInside */
880
+ // pixelPoint = this.project(latlng),
881
+ // pixelBounds = this.getPixelBounds(),
882
+ // pixelCenter = this.project(this.getCenter()),
883
+ rect = this._container.getBoundingClientRect(),
884
+ pixelPoint = this.latLngToContainerPoint(latlng),
885
+ pixelBounds = L.bounds([ L.point(rect), L.point(rect).add(this.getSize()) ]),
886
+ pixelCenter = pixelBounds.getCenter(),
887
+ //
888
+ paddedBounds = L.bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
889
+ paddedSize = paddedBounds.getSize();
890
+
891
+ if (!paddedBounds.contains(pixelPoint)) {
892
+ this._enforcingBounds = true;
893
+ const centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
894
+ const offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
895
+ pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
896
+ pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
897
+ /** @TODO use mapProto.panInside */
898
+ // this.panTo(this.unproject(pixelCenter), options);
899
+ this.panTo(this.containerPointToLatLng(pixelCenter), options);
900
+ //
901
+ this._enforcingBounds = false;
902
+ }
903
+ return this;
904
+ },
905
+
906
+ /**
907
+ * Pans the map to the closest view that would lie inside the given bounds
908
+ * (if it's not already), controlling the animation using the options specific,
909
+ * if any.
910
+ *
911
+ * @TODO check if map bounds calculated by `L.Map::panInsideBounds()`
912
+ * function should match the `rotatePane` or `norotatePane` bounds
913
+ *
914
+ * @see https://github.com/fnicollet/Leaflet/issues/7
915
+ *
916
+ * @param {L.LatLngBounds} bounds coordinates
917
+ * @param {Object} [options] pan options
918
+ * @returns {L.Map} current map instance
919
+ */
920
+ // panInsideBounds: function (bounds, options) {
921
+ // this._enforcingBounds = true;
922
+ // var center = this.getCenter(),
923
+ // newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
924
+ //
925
+ // if (!center.equals(newCenter)) {
926
+ // this.panTo(newCenter, options);
927
+ // }
928
+ //
929
+ // this._enforcingBounds = false;
930
+ // return this;
931
+ // },
721
932
 
722
- // @method mapPanePointToRotatedPoint(point: Point): Point
723
- // Converts a coordinate from the not rotated map pane reference system
724
- // to the reference system of the rotated pane.
725
- mapPanePointToRotatedPoint: function(point) {
726
- return L.point(point)._subtract(this._getRotatePanePos()).rotate(-this._bearing);
933
+ // adjust center for view to get inside bounds
934
+ // _limitCenter(center, zoom, bounds) {
935
+ //
936
+ // if (!bounds) { return center; }
937
+ //
938
+ // const centerPoint = this.project(center, zoom),
939
+ // viewHalf = this.getSize().divideBy(2),
940
+ // viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
941
+ // offset = this._getBoundsOffset(viewBounds, bounds, zoom);
942
+ //
943
+ // // If offset is less than a pixel, ignore.
944
+ // // This prevents unstable projections from getting into
945
+ // // an infinite loop of tiny offsets.
946
+ // if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
947
+ // return center;
948
+ // }
949
+ //
950
+ // return this.unproject(centerPoint.add(offset), zoom);
951
+ // },
952
+
953
+ // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
954
+ // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
955
+ // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
956
+ // flyToBounds(bounds, options) {
957
+ // const target = this._getBoundsCenterZoom(bounds, options);
958
+ // return this.flyTo(target.center, target.zoom, options);
959
+ // },
960
+
961
+ // _getBoundsCenterZoom(bounds, options) {
962
+ //
963
+ // options = options || {};
964
+ // bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
965
+ //
966
+ // const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
967
+ // paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]);
968
+ //
969
+ // let zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
970
+ //
971
+ // zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
972
+ //
973
+ // if (zoom === Infinity) {
974
+ // return { center: bounds.getCenter(), zoom };
975
+ // }
976
+ //
977
+ // return { center, zoom };
978
+ //
979
+ // },
980
+
981
+ /**
982
+ * Returns the maximum zoom level on which the given bounds fit to the map
983
+ * view in its entirety. If `inside` (optional) is set to `true`, the method
984
+ * instead returns the minimum zoom level on which the map view fits into
985
+ * the given bounds in its entirety.
986
+ *
987
+ * @param {L.LatLngBounds} bounds
988
+ * @param {Boolean} [inside=false]
989
+ * @param {L.Point} [padding=[0,0]]
990
+ *
991
+ * @returns {Number} zoom level
992
+ */
993
+ getBoundsZoom(bounds, inside, padding) {
994
+ if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
995
+ return mapProto.getBoundsZoom.apply(this, arguments);
996
+ }
997
+
998
+ bounds = L.latLngBounds(bounds);
999
+ padding = L.point(padding || [0, 0]);
1000
+
1001
+ let zoom = this.getZoom() || 0;
1002
+ const min = this.getMinZoom(),
1003
+ max = this.getMaxZoom(),
1004
+ /** @TODO use mapProto.getBoundsZoom */
1005
+ // nw = bounds.getNorthWest(),
1006
+ // se = bounds.getSouthEast(),
1007
+ // size = this.getSize().subtract(padding),
1008
+ // boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
1009
+ size = this.getSize().subtract(padding),
1010
+ boundsSize = this.mapBoundsToContainerBounds(bounds).getSize(),
1011
+ snap = this.options.zoomSnap,
1012
+ scalex = size.x / boundsSize.x,
1013
+ scaley = size.y / boundsSize.y,
1014
+ scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
1015
+
1016
+ zoom = this.getScaleZoom(scale, zoom);
1017
+
1018
+ if (snap) {
1019
+ zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
1020
+ zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
1021
+ }
1022
+
1023
+ return Math.max(min, Math.min(max, zoom));
727
1024
  },
728
1025
 
729
- // offset of the specified place to the current center in pixels
1026
+ /**
1027
+ * Layer point of the current center
1028
+ *
1029
+ * @returns {L.Point} layer center
1030
+ */
1031
+ // _getCenterLayerPoint: function () {
1032
+ // return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
1033
+ // },
1034
+
1035
+ /**
1036
+ * Offset of the specified place to the current center in pixels
1037
+ *
1038
+ * @param {L.LatLng} latlng map coordinates
1039
+ */
730
1040
  _getCenterOffset: function(latlng) {
731
- var centerOffset = mapProto._getCenterOffset.call(this, latlng);
1041
+ var centerOffset = mapProto._getCenterOffset.apply(this, arguments);
732
1042
  if (this._rotate) {
733
1043
  centerOffset = centerOffset.rotate(this._bearing);
734
1044
  }
735
1045
  return centerOffset;
736
1046
  },
737
1047
 
1048
+ /**
1049
+ * @since leaflet-rotate (v0.1)
1050
+ */
738
1051
  _getRotatePanePos: function() {
739
1052
  return this._rotatePanePos || new L.Point(0, 0);
1053
+ // return L.DomUtil.getPosition(this._rotatePane) || new L.Point(0, 0);
740
1054
  },
741
1055
 
742
1056
  _getNewPixelOrigin: function(center, zoom) {
743
1057
  var viewHalf = this.getSize()._divideBy(2);
744
1058
  if (!this._rotate) {
745
- mapProto._getNewPixelOrigin.call(this, center, zoom);
1059
+ return mapProto._getNewPixelOrigin.apply(this, arguments);
746
1060
  }
747
1061
  return this.project(center, zoom)
748
1062
  .rotate(this._bearing)
@@ -753,10 +1067,72 @@
753
1067
  ._round();
754
1068
  },
755
1069
 
1070
+ /**
1071
+ * @since leaflet-rotate (v0.2)
1072
+ *
1073
+ * @see src\layer\tile\GridLayer::_getTiledPixelBounds()
1074
+ */
1075
+ _getNewPixelBounds: function(center, zoom) {
1076
+ center = center || this.getCenter();
1077
+ zoom = zoom || this.getZoom();
1078
+ if (!this._rotate && mapProto._getNewPixelBounds) {
1079
+ return mapProto._getNewPixelBounds.apply(this, arguments);
1080
+ }
1081
+ var mapZoom = this._animatingZoom ? Math.max(this._animateToZoom, this.getZoom()) : this.getZoom(),
1082
+ scale = this.getZoomScale(mapZoom, zoom),
1083
+ pixelCenter = this.project(center, zoom).floor(),
1084
+ size = this.getSize(),
1085
+ halfSize = new L.Bounds([
1086
+ this.containerPointToLayerPoint([0, 0]).floor(),
1087
+ this.containerPointToLayerPoint([size.x, 0]).floor(),
1088
+ this.containerPointToLayerPoint([0, size.y]).floor(),
1089
+ this.containerPointToLayerPoint([size.x, size.y]).floor()
1090
+ ]).getSize().divideBy(scale * 2);
1091
+
1092
+ return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
1093
+ },
1094
+
1095
+ /**
1096
+ * @since leaflet-rotate (v0.2)
1097
+ *
1098
+ * @return {L.Point} map pivot point (center)
1099
+ */
1100
+ _getPixelCenter: function() {
1101
+ if (!this._rotate && mapProto._getPixelCenter) {
1102
+ return mapProto._getPixelCenter.apply(this, arguments);
1103
+ }
1104
+ return this.getSize()._divideBy(2)._subtract(this._getMapPanePos());
1105
+ },
1106
+
1107
+ /**
1108
+ * @since leaflet-rotate (v0.2)
1109
+ *
1110
+ * @see src\layer\vector\Renderer::_update()
1111
+ */
1112
+ _getPaddedPixelBounds: function(padding) {
1113
+ if (!this._rotate && mapProto._getPaddedPixelBounds) {
1114
+ return mapProto._getPaddedPixelBounds.apply(this, arguments);
1115
+ }
1116
+ var p = padding,
1117
+ size = this.getSize(),
1118
+ padMin = size.multiplyBy(-p),
1119
+ padMax = size.multiplyBy(1 + p);
1120
+ //min = this.containerPointToLayerPoint(size.multiplyBy(-p)).round();
1121
+
1122
+ return new L.Bounds([
1123
+ this.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),
1124
+ this.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),
1125
+ this.containerPointToLayerPoint([padMax.x, padMin.y]).floor(),
1126
+ this.containerPointToLayerPoint([padMax.x, padMax.y]).floor()
1127
+ ]);
1128
+ },
1129
+
756
1130
  _handleGeolocationResponse: function(pos) {
1131
+ if (!this._container._leaflet_id) { return; }
1132
+
757
1133
  var lat = pos.coords.latitude,
758
1134
  lng = pos.coords.longitude,
759
- // TODO: use mapProto._handleGeolocationResponse
1135
+ /** @TODO use mapProto._handleGeolocationResponse */
760
1136
  hdg = pos.coords.heading,
761
1137
  latlng = new L.LatLng(lat, lng),
762
1138
  bounds = latlng.toBounds(pos.coords.accuracy),
@@ -771,7 +1147,7 @@
771
1147
  latlng: latlng,
772
1148
  bounds: bounds,
773
1149
  timestamp: pos.timestamp,
774
- // TODO: use mapProto._handleGeolocationResponse
1150
+ /** @TODO use mapProto._handleGeolocationResponse */
775
1151
  heading: hdg
776
1152
  };
777
1153
 
@@ -787,39 +1163,50 @@
787
1163
  this.fire('locationfound', data);
788
1164
  },
789
1165
 
1166
+ /**
1167
+ * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/geo/LatLngBounds.js#L253-L264
1168
+ *
1169
+ * @param {L.Bounds} points
1170
+ * @returns {L.Bounds}
1171
+ */
1172
+ // toCircumscribedBounds(points) {
1173
+ // var minX = points.reduce(function (pv, v) { return Math.min(pv, v.x); }, points[0].x),
1174
+ // maxX = points.reduce(function (pv, v) { return Math.max(pv, v.x); }, points[0].x),
1175
+ // minY = points.reduce(function (pv, v) { return Math.min(pv, v.y); }, points[0].y),
1176
+ // maxY = points.reduce(function (pv, v) { return Math.max(pv, v.y); }, points[0].y);
1177
+ //
1178
+ // return L.bounds(L.point(minX, minY), L.point(maxX, maxY));
1179
+ // },
1180
+
790
1181
  });
791
1182
 
792
- /*
793
- * L.Map.CompassBearing will rotate the map according to a smartphone's compass.
1183
+ /**
1184
+ * Rotates the map according to a smartphone's compass.
1185
+ *
1186
+ * @typedef L.Map.CompassBearing
794
1187
  */
795
1188
 
796
1189
  L.Map.CompassBearing = L.Handler.extend({
797
1190
 
798
1191
  initialize: function(map) {
799
- if (!window.DeviceOrientationEvent) {
800
- this._capable = false;
801
- return;
802
- }
803
- this._capable = true;
804
1192
  this._map = map;
805
-
806
1193
  this._throttled = L.Util.throttle(this._onDeviceOrientation, 1000, this);
807
1194
  },
808
1195
 
809
1196
  addHooks: function() {
810
- if (this._capable && this._map._rotate) {
1197
+ if (this._map._rotate && window.DeviceOrientationEvent) {
811
1198
  L.DomEvent.on(window, 'deviceorientation', this._throttled, this);
812
1199
  }
813
1200
  },
814
1201
 
815
1202
  removeHooks: function() {
816
- if (this._capable && this._map._rotate) {
1203
+ if (this._map._rotate && window.DeviceOrientationEvent) {
817
1204
  L.DomEvent.off(window, 'deviceorientation', this._throttled, this);
818
1205
  }
819
1206
  },
820
1207
 
821
1208
  _onDeviceOrientation: function(event) {
822
- if (event.alpha !== null) {
1209
+ if (event.alpha !== null && window.orientation !== undefined) {
823
1210
  this._map.setBearing(event.alpha - window.orientation);
824
1211
  }
825
1212
  },
@@ -831,8 +1218,10 @@
831
1218
  // Compass bearing handler.
832
1219
  L.Map.addInitHook('addHandler', 'compassBearing', L.Map.CompassBearing);
833
1220
 
834
- /*
835
- * L.Handler.ContainerMutation triggers `invalidateResize` when the map's DOM container mutates.
1221
+ /**
1222
+ * Triggers `invalidateResize` when the map's DOM container mutates.
1223
+ *
1224
+ * @typedef L.Map.ContainerMutation
836
1225
  */
837
1226
 
838
1227
  // @namespace Map
@@ -850,9 +1239,10 @@
850
1239
  L.Map.ContainerMutation = L.Handler.extend({
851
1240
 
852
1241
  addHooks: function() {
853
- if (!L.Browser.mutation) {
854
- return;
855
- }
1242
+ /** @TODO check again this property. Will likely be added in future releases (leaflet > v1.9.3) */
1243
+ // if (!L.Browser.mutation) {
1244
+ // return;
1245
+ // }
856
1246
 
857
1247
  if (!this._observer) {
858
1248
  this._observer = new MutationObserver(L.Util.bind(this._onMutation, this));
@@ -868,9 +1258,10 @@
868
1258
  },
869
1259
 
870
1260
  removeHooks: function() {
871
- if (!L.Browser.mutation) {
872
- return;
873
- }
1261
+ /** @TODO check again this property. Will likely be added in future releases (leaflet > v1.9.3) */
1262
+ // if (!L.Browser.mutation) {
1263
+ // return;
1264
+ // }
874
1265
  this._observer.disconnect();
875
1266
  },
876
1267
 
@@ -885,8 +1276,13 @@
885
1276
  // Container mutation handler (disabled unless [`trackContainerMutation`](#map-trackcontainermutation) is set).
886
1277
  L.Map.addInitHook('addHandler', 'trackContainerMutation', L.Map.ContainerMutation);
887
1278
 
888
- /*
889
- * L.Handler.TouchGestures is both TouchZoom plus TouchRotate.
1279
+ /**
1280
+ * TouchGestures is both TouchZoom plus TouchRotate
1281
+ *
1282
+ * @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834
1283
+ * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
1284
+ *
1285
+ * @typedef L.Map.TouchGestures
890
1286
  */
891
1287
 
892
1288
  // @namespace Map
@@ -950,11 +1346,11 @@
950
1346
 
951
1347
  this._moved = false;
952
1348
 
953
- map.stop();
1349
+ map._stop();
954
1350
 
955
1351
  L.DomEvent
956
1352
  .on(document, 'touchmove', this._onTouchMove, this)
957
- .on(document, 'touchend', this._onTouchEnd, this);
1353
+ .on(document, 'touchend touchcancel', this._onTouchEnd, this);
958
1354
 
959
1355
  L.DomEvent.preventDefault(e);
960
1356
  },
@@ -1005,20 +1401,20 @@
1005
1401
  }
1006
1402
 
1007
1403
  if (!this._moved) {
1008
- map._moveStart(true);
1404
+ map._moveStart(true, false);
1009
1405
  this._moved = true;
1010
1406
  }
1011
1407
 
1012
1408
  L.Util.cancelAnimFrame(this._animRequest);
1013
1409
 
1014
- var moveFn = L.bind(map._move, map, this._center, this._zoom, { pinch: true, round: false });
1410
+ var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined);
1015
1411
  this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
1016
1412
 
1017
1413
  L.DomEvent.preventDefault(e);
1018
1414
  },
1019
1415
 
1020
1416
  _onTouchEnd: function() {
1021
- if (!this._moved || !this._zooming) {
1417
+ if (!this._moved || !(this._zooming || this._rotating)) {
1022
1418
  this._zooming = false;
1023
1419
  return;
1024
1420
  }
@@ -1028,13 +1424,13 @@
1028
1424
  L.Util.cancelAnimFrame(this._animRequest);
1029
1425
 
1030
1426
  L.DomEvent
1031
- .off(document, 'touchmove', this._onTouchMove)
1032
- .off(document, 'touchend', this._onTouchEnd);
1427
+ .off(document, 'touchmove', this._onTouchMove, this)
1428
+ .off(document, 'touchend touchcancel', this._onTouchEnd, this);
1033
1429
 
1034
1430
  if (this.zoom) {
1035
- // Pinch updates GridLayers' levels only when snapZoom is off, so snapZoom becomes noUpdate.
1431
+ // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
1036
1432
  if (this._map.options.zoomAnimation) {
1037
- this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.snapZoom);
1433
+ this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
1038
1434
  } else {
1039
1435
  this._map._resetView(this._center, this._map._limitZoom(this._zoom));
1040
1436
  }
@@ -1048,8 +1444,10 @@
1048
1444
  // Touch gestures handler.
1049
1445
  L.Map.addInitHook('addHandler', 'touchGestures', L.Map.TouchGestures);
1050
1446
 
1051
- /*
1052
- * L.Handler.TouchRotate is used by L.Map to add two-finger rotation gestures.
1447
+ /**
1448
+ * Rotates the map on two-finger (touch devices).
1449
+ *
1450
+ * @typedef L.Map.TouchRotate
1053
1451
  */
1054
1452
 
1055
1453
  // @namespace Map
@@ -1081,8 +1479,10 @@
1081
1479
  // Touch rotate handler.
1082
1480
  L.Map.addInitHook('addHandler', 'touchRotate', L.Map.TouchRotate);
1083
1481
 
1084
- /*
1085
- * L.Handler.ShiftKeyRotate is used by L.Map to add shift-wheel rotation.
1482
+ /**
1483
+ * Rotates the map on shift key + mouseheel scrolling (desktop).
1484
+ *
1485
+ * @typedef L.Map.ShiftKeyRotate
1086
1486
  */
1087
1487
 
1088
1488
  // @namespace Map
@@ -1134,8 +1534,12 @@
1134
1534
  }
1135
1535
  });
1136
1536
 
1137
- /*
1138
- * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
1537
+ /**
1538
+ * Adds pinch zoom rotation on mobile browsers
1539
+ *
1540
+ * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
1541
+ *
1542
+ * @external L.Map.TouchZoom
1139
1543
  */
1140
1544
 
1141
1545
  // @namespace Map
@@ -1147,10 +1551,18 @@
1147
1551
  // Whether the map can be zoomed by touch-dragging with two fingers. If
1148
1552
  // passed `'center'`, it will zoom to the center of the view regardless of
1149
1553
  // where the touch events (fingers) were. Enabled for touch-capable web
1150
- // browsers except for old Androids.
1151
- touchZoom: L.Browser.touch && !L.Browser.android23,
1554
+ // browsers.
1555
+ touchZoom: L.Browser.touch,
1556
+
1557
+ /**
1558
+ * @TODO check if this is a duplicate of `L.Map.TouchGestures::bounceAtZoomLimits`
1559
+ */
1152
1560
 
1561
+ // @option bounceAtZoomLimits: Boolean = true
1562
+ // Set it to false if you don't want the map to zoom beyond min/max zoom
1563
+ // and then bounce back when pinch-zooming.
1153
1564
  bounceAtZoomLimits: false,
1565
+
1154
1566
  });
1155
1567
 
1156
1568
  L.Map.TouchZoom = L.Handler.extend({
@@ -1174,13 +1586,15 @@
1174
1586
  L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
1175
1587
 
1176
1588
  /**
1177
- * L.Control.Rotate
1589
+ * A tri-state control for map rotation, states are:
1590
+ *
1591
+ * - Locked (default)
1592
+ * - Unlocked (user can pinch-rotate)
1593
+ * - Follow (rotation follows device orientation, if available)
1594
+ *
1595
+ * @typedef L.Control.Rotate
1178
1596
  */
1179
1597
 
1180
- // A tri-state control for map rotation. States are:
1181
- // Locked (default)
1182
- // Unlocked (user can pinch-rotate)
1183
- // Follow (rotation follows device orientation, if available)
1184
1598
  L.Control.Rotate = L.Control.extend({
1185
1599
 
1186
1600
  options: {
@@ -1210,6 +1624,7 @@
1210
1624
  link.appendChild(arrow);
1211
1625
  link.href = '#';
1212
1626
  link.title = 'Rotate map';
1627
+ // link.draggable = false;
1213
1628
 
1214
1629
  L.DomEvent
1215
1630
  .on(link, 'dblclick', L.DomEvent.stopPropagation)
@@ -1238,7 +1653,7 @@
1238
1653
  },
1239
1654
 
1240
1655
  _handleMouseDown: function(e) {
1241
- L.DomEvent.stopPropagation(e);
1656
+ L.DomEvent.stop(e); // L.DomEvent.stopPropagation(e);
1242
1657
  this.dragging = true;
1243
1658
  this.dragstartX = e.pageX;
1244
1659
  this.dragstartY = e.pageY;
@@ -1248,7 +1663,7 @@
1248
1663
  },
1249
1664
 
1250
1665
  _handleMouseUp: function(e) {
1251
- L.DomEvent.stopPropagation(e);
1666
+ L.DomEvent.stop(e); // L.DomEvent.stopPropagation(e);
1252
1667
  this.dragging = false;
1253
1668
 
1254
1669
  L.DomEvent
@@ -1342,5 +1757,5 @@
1342
1757
  }
1343
1758
  });
1344
1759
 
1345
- })));
1760
+ }));
1346
1761
  //# sourceMappingURL=leaflet-rotate-src.js.map