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.
package/src/map/Map.js CHANGED
@@ -1,39 +1,41 @@
1
1
  /**
2
- * L.Map
2
+ * @external L.Map
3
+ *
4
+ * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/Map.js
3
5
  */
6
+
4
7
  const mapProto = L.extend({}, L.Map.prototype);
5
8
 
6
9
  L.Map.mergeOptions({ rotate: false, bearing: 0, });
7
10
 
8
11
  L.Map.include({
9
12
 
10
- initialize: function(id, options) { // (HTMLElement or String, Object)
13
+ /**
14
+ * @param {(HTMLElement|String)} id html selector
15
+ * @param {Object} [options={}] leaflet map options
16
+ */
17
+ initialize: function(id, options) {
11
18
  if (options.rotate) {
12
19
  this._rotate = true;
13
20
  this._bearing = 0;
14
21
  }
15
- mapProto.initialize.call(this, id, options);
22
+ mapProto.initialize.apply(this, arguments);
16
23
  if(this.options.rotate){
17
24
  this.setBearing(this.options.bearing);
18
25
  }
19
26
  },
20
27
 
21
- // createPane: function(name, container) {
22
- // if (!this._rotate || name == 'mapPane') {
23
- // return mapProto.createPane.call(this, name, container);
24
- // }
25
- // // init "rotatePane"
26
- // if (!this._rotatePane) {
27
- // // this._pivot = this.getSize().divideBy(2);
28
- // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane);
29
- // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot);
30
- // }
31
- // return mapProto.createPane.call(this, name, container || this._rotatePane);
32
- // },
33
-
34
- containerPointToLayerPoint: function(point) { // (Point)
28
+ /**
29
+ * Given a pixel coordinate relative to the map container,
30
+ * returns the corresponding pixel coordinate relative to
31
+ * the [origin pixel](#map-getpixelorigin).
32
+ *
33
+ * @param {L.Point} point pixel screen coordinates
34
+ * @returns {L.Point} transformed pixel point
35
+ */
36
+ containerPointToLayerPoint: function(point) {
35
37
  if (!this._rotate) {
36
- return mapProto.containerPointToLayerPoint.call(this, point);
38
+ return mapProto.containerPointToLayerPoint.apply(this, arguments);
37
39
  }
38
40
  return L.point(point)
39
41
  .subtract(this._getMapPanePos())
@@ -41,113 +43,232 @@ L.Map.include({
41
43
  .subtract(this._getRotatePanePos());
42
44
  },
43
45
 
44
- getBounds: function() {
46
+ /**
47
+ * Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
48
+ * returns the corresponding pixel coordinate relative to the map container.
49
+ *
50
+ * @param {L.Point} point pixel screen coordinates
51
+ * @returns {L.Point} transformed pixel point
52
+ */
53
+ layerPointToContainerPoint: function(point) {
45
54
  if (!this._rotate) {
46
- return mapProto.getBounds.call(this);
55
+ return mapProto.layerPointToContainerPoint.apply(this, arguments);
47
56
  }
48
- var size = this.getSize();
49
- var topleft = this.layerPointToLatLng(this.containerPointToLayerPoint([0, 0])),
50
- topright = this.layerPointToLatLng(this.containerPointToLayerPoint([size.x, 0])),
51
- bottomright = this.layerPointToLatLng(this.containerPointToLayerPoint([size.x, size.y])),
52
- bottomleft = this.layerPointToLatLng(this.containerPointToLayerPoint([0, size.y]));
57
+ return L.point(point)
58
+ .add(this._getRotatePanePos())
59
+ .rotateFrom(this._bearing, this._getRotatePanePos())
60
+ .add(this._getMapPanePos());
61
+ },
53
62
 
54
- // Use LatLngBounds' build-in constructor that automatically extends the bounds to fit the passed points
55
- return new L.LatLngBounds([topleft, topright, bottomright, bottomleft]);
63
+ /**
64
+ * Converts a coordinate from the rotated pane reference system
65
+ * to the reference system of the not rotated map pane.
66
+ *
67
+ * (rotatePane) --> (mapPane)
68
+ * (rotatePane) --> (norotatePane)
69
+ *
70
+ * @param {L.Point} point pixel screen coordinates
71
+ * @returns {L.Point}
72
+ *
73
+ * @since leaflet-rotate (v0.1)
74
+ */
75
+ rotatedPointToMapPanePoint: function(point) {
76
+ return L.point(point)
77
+ .rotate(this._bearing)
78
+ ._add(this._getRotatePanePos());
56
79
  },
57
80
 
58
- layerPointToContainerPoint: function(point) { // (Point)
81
+ /**
82
+ * Converts a coordinate from the not rotated map pane reference system
83
+ * to the reference system of the rotated pane.
84
+ *
85
+ * (mapPane) --> (rotatePane)
86
+ * (norotatePane) --> (rotatePane)
87
+ *
88
+ * @param {L.Point} point pixel screen coordinates
89
+ *
90
+ * @since leaflet-rotate (v0.1)
91
+ */
92
+ mapPanePointToRotatedPoint: function(point) {
93
+ return L.point(point)
94
+ ._subtract(this._getRotatePanePos())
95
+ .rotate(-this._bearing);
96
+ },
97
+
98
+ /**
99
+ * Given latlng bounds, returns the bounds in projected pixel
100
+ * relative to the map container.
101
+ *
102
+ * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/map/Map.js#L1114-L1135
103
+ *
104
+ * @param {L.LatLngBounds} bounds
105
+ * @returns {L.Bounds}
106
+ *
107
+ * @since leaflet-rotate (v0.2)
108
+ */
109
+ mapBoundsToContainerBounds: function (bounds) {
110
+ if (!this._rotate && mapProto.mapBoundsToContainerBounds) {
111
+ return mapProto.mapBoundsToContainerBounds.apply(this, arguments);
112
+ }
113
+ const nw = this.latLngToContainerPoint(bounds.getNorthWest()),
114
+ ne = this.latLngToContainerPoint(bounds.getNorthEast()),
115
+ sw = this.latLngToContainerPoint(bounds.getSouthWest()),
116
+ se = this.latLngToContainerPoint(bounds.getSouthEast());
117
+ return L.bounds([
118
+ L.point(Math.min(nw.x, ne.x, se.x, sw.x), Math.min(nw.y, ne.y, se.y, sw.y)), // [ minX, minY ]
119
+ L.point(Math.max(nw.x, ne.x, se.x, sw.x), Math.max(nw.y, ne.y, se.y, sw.y)) // [ maxX, maxY ]
120
+ ]);
121
+ },
122
+
123
+ /**
124
+ * Returns geographical bounds visible in the current map view
125
+ *
126
+ * @TODO find out if map bounds calculated by `L.Map::getBounds()`
127
+ * function should match the `rotatePane` or `norotatePane` bounds
128
+ *
129
+ * @see https://github.com/fnicollet/Leaflet/issues/7
130
+ *
131
+ * @returns {L.LatLngBounds}
132
+ */
133
+ getBounds: function() {
59
134
  if (!this._rotate) {
60
- return mapProto.layerPointToContainerPoint.call(this, point);
135
+ return mapProto.getBounds.apply(this, arguments);
61
136
  }
62
- return L.point(point)
63
- .add(this._getRotatePanePos())
64
- .rotateFrom(this._bearing, this._getRotatePanePos())
65
- .add(this._getMapPanePos());
137
+
138
+ // SEE: https://github.com/fnicollet/Leaflet/pull/22
139
+ //
140
+ // var bounds = this.getPixelBounds(),
141
+ // sw = this.unproject(bounds.getBottomLeft()),
142
+ // ne = this.unproject(bounds.getTopRight());
143
+ // return new LatLngBounds(sw, ne);
144
+ //
145
+
146
+ // LatLngBounds' constructor automatically
147
+ // extends the bounds to fit the passed points
148
+ var size = this.getSize();
149
+ return new L.LatLngBounds([
150
+ this.containerPointToLatLng([0, 0]), // topleft
151
+ this.containerPointToLatLng([size.x, 0]), // topright
152
+ this.containerPointToLatLng([size.x, size.y]), // bottomright
153
+ this.containerPointToLatLng([0, size.y]), // bottomleft
154
+ ]);
66
155
  },
67
156
 
68
- // Rotation methods
69
- // setBearing will work with just the 'theta' parameter.
157
+ /**
158
+ * Returns the bounds of the current map view in projected pixel
159
+ * coordinates (sometimes useful in layer and overlay implementations).
160
+ *
161
+ * @TODO find out if map bounds calculated by `L.Map::getPixelBounds()`
162
+ * function should match the `rotatePane` or `norotatePane` bounds
163
+ *
164
+ * @see https://github.com/fnicollet/Leaflet/issues/7
165
+ *
166
+ * @returns {L.Bounds}
167
+ */
168
+ // getPixelBounds(center, zoom) {
169
+ // // const topLeftPoint = map.containerPointToLayerPoint(this._getTopLeftPoint());
170
+ // const topLeftPoint = this._getTopLeftPoint(center, zoom);
171
+ // return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
172
+ // },
173
+
174
+ /**
175
+ * Change map rotation
176
+ *
177
+ * @param {number} theta map degrees
178
+ *
179
+ * @since leaflet-rotate (v0.1)
180
+ */
70
181
  setBearing: function(theta) {
71
182
  if (!L.Browser.any3d || !this._rotate) { return; }
72
183
 
73
- var rotatePanePos = this._getRotatePanePos();
74
- var halfSize = this.getSize().divideBy(2);
75
- this._pivot = this._getMapPanePos().clone().multiplyBy(-1).add(halfSize);
184
+ var bearing = L.Util.wrapNum(theta, [0, 360]) * L.DomUtil.DEG_TO_RAD,
185
+ center = this._getPixelCenter(),
186
+ oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center),
187
+ newPos = oldPos.rotateFrom(bearing, center);
76
188
 
77
- rotatePanePos = rotatePanePos.rotateFrom(-this._bearing, this._pivot);
189
+ // CSS transform
190
+ L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center);
78
191
 
79
- this._bearing = theta * L.DomUtil.DEG_TO_RAD; // TODO: mod 360
80
- this._rotatePanePos = rotatePanePos.rotateFrom(this._bearing, this._pivot);
81
-
82
- L.DomUtil.setPosition(this._rotatePane, rotatePanePos, this._bearing, this._pivot);
192
+ this._pivot = center;
193
+ this._bearing = bearing;
194
+ this._rotatePanePos = newPos;
83
195
 
84
196
  this.fire('rotate');
85
197
  },
86
198
 
199
+ /**
200
+ * Get current map rotation
201
+ *
202
+ * @returns {number} theta map degrees
203
+ *
204
+ * @since leaflet-rotate (v0.1)
205
+ */
87
206
  getBearing: function() {
88
207
  return this._bearing * L.DomUtil.RAD_TO_DEG;
89
208
  },
90
209
 
210
+ /**
211
+ * Creates a new [map pane](#map-pane) with the given name if it doesn't
212
+ * exist already, then returns it. The pane is created as a child of
213
+ * `container`, or as a child of the main map pane if not set.
214
+ *
215
+ * @param {String} name leaflet pane
216
+ * @param {HTMLElement} [container] parent element
217
+ * @returns {HTMLElement} pane container
218
+ */
219
+ // createPane: function(name, container) {
220
+ // if (!this._rotate || name == 'mapPane') {
221
+ // return mapProto.createPane.apply(this, arguments);
222
+ // }
223
+ // // init "rotatePane"
224
+ // if (!this._rotatePane) {
225
+ // // this._pivot = this.getSize().divideBy(2);
226
+ // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane);
227
+ // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot);
228
+ // }
229
+ // return mapProto.createPane.call(this, name, container || this._rotatePane);
230
+ // },
231
+
232
+ /**
233
+ * Panes are DOM elements used to control the ordering of layers on
234
+ * the map. You can access panes with [`map.getPane`](#map-getpane)
235
+ * or [`map.getPanes`](#map-getpanes) methods. New panes can be created
236
+ * with the [`map.createPane`](#map-createpane) method.
237
+ *
238
+ * Every map has the following default panes that differ only in zIndex:
239
+ *
240
+ * - mapPane [HTMLElement = 'auto'] - Pane that contains all other map panes
241
+ * - tilePane [HTMLElement = 2] - Pane for tile layers
242
+ * - overlayPane [HTMLElement = 4] - Pane for overlays like polylines and polygons
243
+ * - shadowPane [HTMLElement = 5] - Pane for overlay shadows (e.g. marker shadows)
244
+ * - markerPane [HTMLElement = 6] - Pane for marker icons
245
+ * - tooltipPane [HTMLElement = 650] - Pane for tooltips.
246
+ * - popupPane [HTMLElement = 700] - Pane for popups.
247
+ */
91
248
  _initPanes: function() {
92
249
  var panes = this._panes = {};
93
250
  this._paneRenderers = {};
94
251
 
95
- // @section
96
- //
97
- // Panes are DOM elements used to control the ordering of layers on the map. You
98
- // can access panes with [`map.getPane`](#map-getpane) or
99
- // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
100
- // [`map.createPane`](#map-createpane) method.
101
- //
102
- // Every map has the following default panes that differ only in zIndex.
103
- //
104
- // @pane mapPane: HTMLElement = 'auto'
105
- // Pane that contains all other map panes
106
-
107
252
  this._mapPane = this.createPane('mapPane', this._container);
108
253
  L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
109
254
 
110
255
  if (this._rotate) {
111
256
  this._rotatePane = this.createPane('rotatePane', this._mapPane);
112
257
  this._norotatePane = this.createPane('norotatePane', this._mapPane);
113
-
114
- // @pane tilePane: HTMLElement = 2
115
- // Pane for tile layers
258
+ // rotatePane
116
259
  this.createPane('tilePane', this._rotatePane);
117
- // @pane overlayPane: HTMLElement = 4
118
- // Pane for overlays like polylines and polygons
119
260
  this.createPane('overlayPane', this._rotatePane);
120
-
121
- // @pane shadowPane: HTMLElement = 5
122
- // Pane for overlay shadows (e.g. marker shadows)
261
+ // norotatePane
123
262
  this.createPane('shadowPane', this._norotatePane);
124
- // @pane markerPane: HTMLElement = 6
125
- // Pane for marker icons
126
263
  this.createPane('markerPane', this._norotatePane);
127
- // @pane tooltipPane: HTMLElement = 650
128
- // Pane for tooltips.
129
264
  this.createPane('tooltipPane', this._norotatePane);
130
- // @pane popupPane: HTMLElement = 700
131
- // Pane for popups.
132
265
  this.createPane('popupPane', this._norotatePane);
133
266
  } else {
134
- // @pane tilePane: HTMLElement = 2
135
- // Pane for tile layers
136
267
  this.createPane('tilePane');
137
- // @pane overlayPane: HTMLElement = 4
138
- // Pane for overlays like polylines and polygons
139
268
  this.createPane('overlayPane');
140
- // @pane shadowPane: HTMLElement = 5
141
- // Pane for overlay shadows (e.g. marker shadows)
142
269
  this.createPane('shadowPane');
143
- // @pane markerPane: HTMLElement = 6
144
- // Pane for marker icons
145
270
  this.createPane('markerPane');
146
- // @pane tooltipPane: HTMLElement = 650
147
- // Pane for tooltips.
148
271
  this.createPane('tooltipPane');
149
- // @pane popupPane: HTMLElement = 700
150
- // Pane for popups.
151
272
  this.createPane('popupPane');
152
273
  }
153
274
 
@@ -157,37 +278,205 @@ L.Map.include({
157
278
  }
158
279
  },
159
280
 
160
- // @method rotatedPointToMapPanePoint(point: Point): Point
161
- // Converts a coordinate from the rotated pane reference system
162
- // to the reference system of the not rotated map pane.
163
- rotatedPointToMapPanePoint: function(point) {
164
- return L.point(point).rotate(this._bearing)._add(this._getRotatePanePos());
281
+ /**
282
+ * Pans the map the minimum amount to make the `latlng` visible. Use
283
+ * padding options to fit the display to more restricted bounds.
284
+ * If `latlng` is already within the (optionally padded) display bounds,
285
+ * the map will not be panned.
286
+ *
287
+ * @see https://github.com/Raruto/leaflet-rotate/issues/18
288
+ *
289
+ * @param {L.LatLng} latlng coordinates
290
+ * @param {Object} [options={}] padding options
291
+ *
292
+ * @returns {L.Map} current map instance
293
+ */
294
+ panInside(latlng, options) {
295
+ options = options || {};
296
+
297
+ const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
298
+ paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
299
+ /** @TODO use mapProto.panInside */
300
+ // pixelPoint = this.project(latlng),
301
+ // pixelBounds = this.getPixelBounds(),
302
+ // pixelCenter = this.project(this.getCenter()),
303
+ rect = this._container.getBoundingClientRect(),
304
+ pixelPoint = this.latLngToContainerPoint(latlng),
305
+ pixelBounds = L.bounds([ L.point(rect), L.point(rect).add(this.getSize()) ]),
306
+ pixelCenter = pixelBounds.getCenter(),
307
+ //
308
+ paddedBounds = L.bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
309
+ paddedSize = paddedBounds.getSize();
310
+
311
+ if (!paddedBounds.contains(pixelPoint)) {
312
+ this._enforcingBounds = true;
313
+ const centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
314
+ const offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
315
+ pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
316
+ pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
317
+ /** @TODO use mapProto.panInside */
318
+ // this.panTo(this.unproject(pixelCenter), options);
319
+ this.panTo(this.containerPointToLatLng(pixelCenter), options);
320
+ //
321
+ this._enforcingBounds = false;
322
+ }
323
+ return this;
165
324
  },
166
325
 
167
- // @method mapPanePointToRotatedPoint(point: Point): Point
168
- // Converts a coordinate from the not rotated map pane reference system
169
- // to the reference system of the rotated pane.
170
- mapPanePointToRotatedPoint: function(point) {
171
- return L.point(point)._subtract(this._getRotatePanePos()).rotate(-this._bearing);
326
+ /**
327
+ * Pans the map to the closest view that would lie inside the given bounds
328
+ * (if it's not already), controlling the animation using the options specific,
329
+ * if any.
330
+ *
331
+ * @TODO check if map bounds calculated by `L.Map::panInsideBounds()`
332
+ * function should match the `rotatePane` or `norotatePane` bounds
333
+ *
334
+ * @see https://github.com/fnicollet/Leaflet/issues/7
335
+ *
336
+ * @param {L.LatLngBounds} bounds coordinates
337
+ * @param {Object} [options] pan options
338
+ * @returns {L.Map} current map instance
339
+ */
340
+ // panInsideBounds: function (bounds, options) {
341
+ // this._enforcingBounds = true;
342
+ // var center = this.getCenter(),
343
+ // newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
344
+ //
345
+ // if (!center.equals(newCenter)) {
346
+ // this.panTo(newCenter, options);
347
+ // }
348
+ //
349
+ // this._enforcingBounds = false;
350
+ // return this;
351
+ // },
352
+
353
+ // adjust center for view to get inside bounds
354
+ // _limitCenter(center, zoom, bounds) {
355
+ //
356
+ // if (!bounds) { return center; }
357
+ //
358
+ // const centerPoint = this.project(center, zoom),
359
+ // viewHalf = this.getSize().divideBy(2),
360
+ // viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
361
+ // offset = this._getBoundsOffset(viewBounds, bounds, zoom);
362
+ //
363
+ // // If offset is less than a pixel, ignore.
364
+ // // This prevents unstable projections from getting into
365
+ // // an infinite loop of tiny offsets.
366
+ // if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
367
+ // return center;
368
+ // }
369
+ //
370
+ // return this.unproject(centerPoint.add(offset), zoom);
371
+ // },
372
+
373
+ // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
374
+ // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
375
+ // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
376
+ // flyToBounds(bounds, options) {
377
+ // const target = this._getBoundsCenterZoom(bounds, options);
378
+ // return this.flyTo(target.center, target.zoom, options);
379
+ // },
380
+
381
+ // _getBoundsCenterZoom(bounds, options) {
382
+ //
383
+ // options = options || {};
384
+ // bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
385
+ //
386
+ // const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
387
+ // paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]);
388
+ //
389
+ // let zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
390
+ //
391
+ // zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
392
+ //
393
+ // if (zoom === Infinity) {
394
+ // return { center: bounds.getCenter(), zoom };
395
+ // }
396
+ //
397
+ // return { center, zoom };
398
+ //
399
+ // },
400
+
401
+ /**
402
+ * Returns the maximum zoom level on which the given bounds fit to the map
403
+ * view in its entirety. If `inside` (optional) is set to `true`, the method
404
+ * instead returns the minimum zoom level on which the map view fits into
405
+ * the given bounds in its entirety.
406
+ *
407
+ * @param {L.LatLngBounds} bounds
408
+ * @param {Boolean} [inside=false]
409
+ * @param {L.Point} [padding=[0,0]]
410
+ *
411
+ * @returns {Number} zoom level
412
+ */
413
+ getBoundsZoom(bounds, inside, padding) {
414
+ if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
415
+ return mapProto.getBoundsZoom.apply(this, arguments);
416
+ }
417
+
418
+ bounds = L.latLngBounds(bounds);
419
+ padding = L.point(padding || [0, 0]);
420
+
421
+ let zoom = this.getZoom() || 0;
422
+ const min = this.getMinZoom(),
423
+ max = this.getMaxZoom(),
424
+ /** @TODO use mapProto.getBoundsZoom */
425
+ // nw = bounds.getNorthWest(),
426
+ // se = bounds.getSouthEast(),
427
+ // size = this.getSize().subtract(padding),
428
+ // boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
429
+ size = this.getSize().subtract(padding),
430
+ boundsSize = this.mapBoundsToContainerBounds(bounds).getSize(),
431
+ snap = this.options.zoomSnap,
432
+ scalex = size.x / boundsSize.x,
433
+ scaley = size.y / boundsSize.y,
434
+ scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
435
+
436
+ zoom = this.getScaleZoom(scale, zoom);
437
+
438
+ if (snap) {
439
+ zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
440
+ zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
441
+ }
442
+
443
+ return Math.max(min, Math.min(max, zoom));
172
444
  },
173
445
 
174
- // offset of the specified place to the current center in pixels
446
+ /**
447
+ * Layer point of the current center
448
+ *
449
+ * @returns {L.Point} layer center
450
+ */
451
+ // _getCenterLayerPoint: function () {
452
+ // return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
453
+ // },
454
+
455
+ /**
456
+ * Offset of the specified place to the current center in pixels
457
+ *
458
+ * @param {L.LatLng} latlng map coordinates
459
+ */
175
460
  _getCenterOffset: function(latlng) {
176
- var centerOffset = mapProto._getCenterOffset.call(this, latlng);
461
+ var centerOffset = mapProto._getCenterOffset.apply(this, arguments);
177
462
  if (this._rotate) {
178
463
  centerOffset = centerOffset.rotate(this._bearing);
179
464
  }
180
465
  return centerOffset;
181
466
  },
182
467
 
468
+ /**
469
+ * @since leaflet-rotate (v0.1)
470
+ */
183
471
  _getRotatePanePos: function() {
184
472
  return this._rotatePanePos || new L.Point(0, 0);
473
+ // return L.DomUtil.getPosition(this._rotatePane) || new L.Point(0, 0);
185
474
  },
186
475
 
187
476
  _getNewPixelOrigin: function(center, zoom) {
188
477
  var viewHalf = this.getSize()._divideBy(2);
189
478
  if (!this._rotate) {
190
- mapProto._getNewPixelOrigin.call(this, center, zoom);
479
+ return mapProto._getNewPixelOrigin.apply(this, arguments);
191
480
  }
192
481
  return this.project(center, zoom)
193
482
  .rotate(this._bearing)
@@ -198,10 +487,72 @@ L.Map.include({
198
487
  ._round();
199
488
  },
200
489
 
490
+ /**
491
+ * @since leaflet-rotate (v0.2)
492
+ *
493
+ * @see src\layer\tile\GridLayer::_getTiledPixelBounds()
494
+ */
495
+ _getNewPixelBounds: function(center, zoom) {
496
+ center = center || this.getCenter();
497
+ zoom = zoom || this.getZoom();
498
+ if (!this._rotate && mapProto._getNewPixelBounds) {
499
+ return mapProto._getNewPixelBounds.apply(this, arguments);
500
+ }
501
+ var mapZoom = this._animatingZoom ? Math.max(this._animateToZoom, this.getZoom()) : this.getZoom(),
502
+ scale = this.getZoomScale(mapZoom, zoom),
503
+ pixelCenter = this.project(center, zoom).floor(),
504
+ size = this.getSize(),
505
+ halfSize = new L.Bounds([
506
+ this.containerPointToLayerPoint([0, 0]).floor(),
507
+ this.containerPointToLayerPoint([size.x, 0]).floor(),
508
+ this.containerPointToLayerPoint([0, size.y]).floor(),
509
+ this.containerPointToLayerPoint([size.x, size.y]).floor()
510
+ ]).getSize().divideBy(scale * 2);
511
+
512
+ return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
513
+ },
514
+
515
+ /**
516
+ * @since leaflet-rotate (v0.2)
517
+ *
518
+ * @return {L.Point} map pivot point (center)
519
+ */
520
+ _getPixelCenter: function() {
521
+ if (!this._rotate && mapProto._getPixelCenter) {
522
+ return mapProto._getPixelCenter.apply(this, arguments);
523
+ }
524
+ return this.getSize()._divideBy(2)._subtract(this._getMapPanePos());
525
+ },
526
+
527
+ /**
528
+ * @since leaflet-rotate (v0.2)
529
+ *
530
+ * @see src\layer\vector\Renderer::_update()
531
+ */
532
+ _getPaddedPixelBounds: function(padding) {
533
+ if (!this._rotate && mapProto._getPaddedPixelBounds) {
534
+ return mapProto._getPaddedPixelBounds.apply(this, arguments);
535
+ }
536
+ var p = padding,
537
+ size = this.getSize(),
538
+ padMin = size.multiplyBy(-p),
539
+ padMax = size.multiplyBy(1 + p);
540
+ //min = this.containerPointToLayerPoint(size.multiplyBy(-p)).round();
541
+
542
+ return new L.Bounds([
543
+ this.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),
544
+ this.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),
545
+ this.containerPointToLayerPoint([padMax.x, padMin.y]).floor(),
546
+ this.containerPointToLayerPoint([padMax.x, padMax.y]).floor()
547
+ ]);
548
+ },
549
+
201
550
  _handleGeolocationResponse: function(pos) {
551
+ if (!this._container._leaflet_id) { return; }
552
+
202
553
  var lat = pos.coords.latitude,
203
554
  lng = pos.coords.longitude,
204
- // TODO: use mapProto._handleGeolocationResponse
555
+ /** @TODO use mapProto._handleGeolocationResponse */
205
556
  hdg = pos.coords.heading,
206
557
  latlng = new L.LatLng(lat, lng),
207
558
  bounds = latlng.toBounds(pos.coords.accuracy),
@@ -216,7 +567,7 @@ L.Map.include({
216
567
  latlng: latlng,
217
568
  bounds: bounds,
218
569
  timestamp: pos.timestamp,
219
- // TODO: use mapProto._handleGeolocationResponse
570
+ /** @TODO use mapProto._handleGeolocationResponse */
220
571
  heading: hdg
221
572
  };
222
573
 
@@ -232,4 +583,19 @@ L.Map.include({
232
583
  this.fire('locationfound', data);
233
584
  },
234
585
 
586
+ /**
587
+ * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/geo/LatLngBounds.js#L253-L264
588
+ *
589
+ * @param {L.Bounds} points
590
+ * @returns {L.Bounds}
591
+ */
592
+ // toCircumscribedBounds(points) {
593
+ // var minX = points.reduce(function (pv, v) { return Math.min(pv, v.x); }, points[0].x),
594
+ // maxX = points.reduce(function (pv, v) { return Math.max(pv, v.x); }, points[0].x),
595
+ // minY = points.reduce(function (pv, v) { return Math.min(pv, v.y); }, points[0].y),
596
+ // maxY = points.reduce(function (pv, v) { return Math.max(pv, v.y); }, points[0].y);
597
+ //
598
+ // return L.bounds(L.point(minX, minY), L.point(maxX, maxY));
599
+ // },
600
+
235
601
  });